I am implementing an OOP design using PHP. I wonder how PHP handles inheritance for its magic methods like __get and __set.
class Foo
{
protected $property1;
public function __get($name)
{
if ($name == "property1")
{
// do some logic
return $result; // may be null
}
return;
}
public function __set($name, $value)
{
if ($name == "property1")
{
// do some logic
return $result; // may be null
}
result;
}
}
Now for extending Foo:
class Bar extends Foo
{
protected $property2;
public function __get($name)
{
if (($result = parent::__get($name)) !== null)
return $result; // may be null
if ($name == "property2")
{
// do some logic
return $result; // may be null
}
return;
}
public function __set($name, $value)
{
if (($result = parent::__set($name, $value)) !== null)
return $result; // may be null
if ($name == "property2")
{
// do some logic
return $result; // may be null
}
return;
}
}
As PHP returns null as the result of a function with nothing to return... this may lead in ambiguity of whether the parent::__get() or parent::__set() returned null truely or returned with no value; and leads to overhead.
Now if PHP considers the static::_get() and static::__set() first and fall backs to the parent versions on failure, this could be simplified as:
class Bar extends Foo
{
protected $property2;
public function __get($name)
{
if ($name = "property2")
{
// do some logic
return $result; // may be null
}
return;
}
public function __set($name, $value)
{
if ($name = "property2")
{
// do some logic
return $result; // may be null
}
return;
}
}
I can't test it on current implementation because the classes in context manipulates production, live database. Which is the correct implementation?
Thanks!
Use magic methods only as proxy methods. Your code will be much cleaner and you don't have to deal with problems caused by inheritance.
Simple example:
class Foo
{
protected $property1;
public function setProperty1($property1)
{
// do some logic
$this->property1 = $property1;
return $this;
}
public function getProperty1()
{
// do some logic
return $this->property1;
}
public function __get($name)
{
$method = 'get' . ucfirst($name);
if (method_exists($this, $method)) {
return $this->$method();
}
}
public function __set($name, $value)
{
$method = 'set' . ucfirst($name);
if (method_exists($this, $method)) {
$this->$method($value);
}
}
}
class Bar extends Foo
{
protected $property2;
public function setProperty2($property2)
{
// do some logic
$this->property2 = $property2;
return $this;
}
public function getProperty2()
{
// do some logic
return $this->property2;
}
}
Example call:
$bar = new Bar;
$bar->property1 = 'foo';
$bar->property2 = 'bar';
var_dump($bar);
I have managed to create an Isolated environment and test both methods.
Method 1
class Foo
{
protected $property1;
public function __get($name)
{
if ($name == "name")
{
echo "been here @line(" . __LINE__ . ") Foo::__get('$name') <br/>";
$result = $this->property1;
return $result; // may be null
}
return;
}
public function __set($name, $value)
{
if ($name == "name")
{
echo "been here @line(" . __LINE__ . ") Foo::__set('$name', '$value') <br/>";
$result = $this->property1 = $value;
return $result; // may be null
}
return;
}
}
class Bar extends Foo
{
protected $property2;
public function __get($name)
{
if ($name == "place")
{
echo "been here @line(" . __LINE__ . ") Bar::__get('$name') <br/>";
$result = $this->property2;
return $result; // may be null
}
return;
}
public function __set($name, $value)
{
if ($name == "place")
{
echo "been here @line(" . __LINE__ . ") Bar::__set('$name', '$value') <br/>";
$result = $this->property2 = $value;
return $result; // may be null
}
return;
}
}
$bar = new Bar;
$bar->name = 'Alice';
$bar->place = 'Wonderland';
echo "done that: '{$bar->name} in {$bar->place}'";
The above code results are:
been here @line(59) Bar::__set('place', 'Wonderland')
been here @line(43) Bar::__get('place')
done that: ' in Wonderland'
That is clearly not the answer.
Method 2
class Foo
{
protected $property1;
public function __get($name)
{
if ($name == "name")
{
echo "been here @line(" . __LINE__ . ") Foo::__get('$name') <br/>";
$result = $this->property1;
return $result; // may be null
}
return;
}
public function __set($name, $value)
{
if ($name == "name")
{
echo "been here @line(" . __LINE__ . ") Foo::__set('$name', '$value') <br/>";
$result = $this->property1 = $value;
return $result; // may be null
}
return;
}
}
class Bar extends Foo
{
protected $property2;
public function __get($name)
{
if (($result = parent::__get($name)) !== null)
{
echo "been here @line(" . __LINE__ . ") parent::__get('$name') <br/>";
return $result; // may be null
}
if ($name == "place")
{
echo "been here @line(" . __LINE__ . ") Bar::__get('$name') <br/>";
$result = $this->property2;
return $result; // may be null
}
return;
}
public function __set($name, $value)
{
if (($result = parent::__set($name, $value)) !== null)
{
echo "been here @line(" . __LINE__ . ") parent::__set('$name', '$value') <br/>";
return $result; // may be null
}
if ($name == "place")
{
echo "been here @line(" . __LINE__ . ") Bar::__set('$name', '$value') <br/>";
$result = $this->property2 = $value;
return $result; // may be null
}
return;
}
}
$bar = new Bar;
$bar->name = 'Alice';
$bar->place = 'Wonderland';
echo "done that: '{$bar->name} in {$bar->place}'";
Which results in:
been here @line(22) Foo::__set('name', 'Alice')
been here @line(54) parent::__set('name', 'Alice')
been here @line(59) Bar::__set('place', 'Wonderland')
been here @line(11) Foo::__get('name')
been here @line(38) parent::__get('name')
been here @line(43) Bar::__get('place')
done that: 'Alice in Wonderland'
This is the correct answer for overloading __get and __set magic functions while dealing with inheritance, but this has overheads. It is a solution but is there any better solution?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With