Я реализую дизайн ООП с использованием PHP. Интересно, как PHP обрабатывает наследование для своих магических методов, таких как __get и __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;
}
}
Теперь для расширения 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;
}
}
Как возвращается PHP null
в результате функции, которой нечего возвращать … это может привести к неоднозначности parent::__get()
или же parent::__set()
вернул null верно или вернулся без значения; и приводит к накладным расходам.
Теперь, если PHP считает static::_get()
а также static::__set()
сначала и отступить к parent
версии на сбой, это может быть упрощено как:
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;
}
}
Я не могу проверить это на текущей реализации, потому что классы в контексте манипулируют производственной базой данных. Какая правильная реализация?
Спасибо!
Используйте магические методы только в качестве прокси-методов. Ваш код будет намного чище, и вам не придется иметь дело с проблемами, вызванными наследованием.
Простой пример:
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;
}
}
Пример вызова:
$bar = new Bar;
$bar->property1 = 'foo';
$bar->property2 = 'bar';
var_dump($bar);
Мне удалось создать изолированную среду и протестировать оба метода.
Способ 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}'";
Приведенные выше результаты кода:
been here @line(59) Bar::__set('place', 'Wonderland')
been here @line(43) Bar::__get('place')
done that: ' in Wonderland'
Это явно не ответ.
Способ 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}'";
Что приводит к:
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'
Это правильный ответ для перегрузки магических функций __get и __set при работе с наследованием, но это накладные расходы. Это решение, но есть ли лучшее решение?