Практические применения магических методов PHP - __get, __set и __call
Я вообще старался держаться подальше от магических методов PHP, потому что они, похоже, запутывают открытый интерфейс объекта. Тем не менее, они, кажется, используются все больше и больше, по крайней мере, в коде, который я прочитал, поэтому я должен спросить: существует ли какой-либо консенсус относительно того, когда их использовать? Существуют ли общие шаблоны для использования этих трех магических методов?
Ответы
Ответ 1
__call()
Я видел, что он использовался для реализации поведения, например, при добавлении дополнительных функций в класс через плагируемый интерфейс.
Псевдокод выглядит так:
$method = function($self) {};
$events->register('object.method', $method);
$entity->method(); // $method($this);
Это также облегчает запись в основном похожих функций, например, в ORM. например:.
$entity->setName('foo'); // set column name to 'foo'
__get()/__set()
В основном я видел, как он использовал для доступа к закрытым переменным.
ORM - лучший пример, который приходит на ум:
$entity->name = 'foo'; // set column name to 'foo'
Ответ 2
Основная причина в том, что вам не нужно вводить столько же. Вы можете использовать их, например, для записи ORM и действовать как неявные сеттеры/получатели:
с помощью __call()
:
$user = new User();
$user->setName("Foo Bar");
$user->setAge(42);
$user->save();
с помощью __set()
:
$user->name = "Foo Bar";
$user->age = 42;
который сопоставляется с простым массивом:
array(
"name" => "Foo Bar",
"age" => 42
)
Намного проще записать такой массив в базу данных, чем делать много ручных вызовов для сбора всей необходимой информации. __set()
и __get()
имеют еще одно преимущество перед публичными членами: вы можете проверить/форматировать свои данные.
Ответ 3
Это позволяет вам делать такие вещи:
class myclass {
private $propertybag;
public function __get($name) {
if(isset($this->propertybag[$name]) {return $this->propertybag[$name];}
throw new Exception("Unknown property " . (string) $name);
}
}
Затем вы можете заполнить $propertybag
из SQL-запроса в одной строке, а не задавать целую кучу свойств один за другим.
Кроме того, он позволяет вам иметь определенные свойства, которые доступны только для чтения (т.е. не позволяют изменять их с помощью __set()
). Возможно, полезно для поля идентификатора, например.
Кроме того, вы можете поместить код в __get()
и __set()
, чтобы вы могли сделать что-то более сложное, чем просто получать или устанавливать одну переменную. Например, если у вас есть поле storeID
, вы также можете указать свойство storeName
. Вы можете реализовать это в __get()
через перекрестный поиск ссылок, поэтому вам может не понадобиться имя, которое должно быть сохранено в классе. И, конечно, storeName
не хотел бы реализовываться в __get()
.
Здесь много возможностей.
Конечно, есть некоторые способы использования магических методов. Самый большой для меня - это тот факт, что вы потеряете автоматическую полную функциональность в своей среде IDE. Это может или не важно для вас.
Ответ 4
Так как магические методы могут сэкономить вам много кодов, когда речь заходит о повторяющихся задачах, таких как определение членов, их заполнение, а затем их извлечение - вместо того, чтобы выполнять эту скучную длинную работу, вы можете использовать упомянутые 3 метода, чтобы сократить время, чтобы закодировать все это. При необходимости я могу привести несколько примеров, которые они могут найти в различных учебниках по сети.
Я не знаю, является ли это общим консенсусом, но обычный должен применяться - используйте, где это необходимо. Если вы обнаружите, что выполняете повторяющуюся задачу (определите член, заполните его, получите член, вызовите функции X, которые немного отличаются) - магические методы могут вам помочь.
Ответ 5
Один общий шаблон состоит в том, чтобы иметь один дескриптор для ваших клиентов и прокси-сервер для вызовов инкапсулированных объектов или синглетов на основе соглашений или конфигураций именования.
class db
{
static private $instance = null;
static public function getInstance()
{
if( self::$instance == NULL )
self::$instance = new db;
return self::$instance;
}
function fetch()
{
echo "I'm fetching\n";
}
}
class dataHandler
{
function __call($name, $argv)
{
if( substr($name, 0, 4) == 'data' )
{
$fn = substr($name, 4);
db::getInstance()->$fn($argv);
}
}
}
$dh = new dataHandler;
$dh->datafetch('foo', 'bar');
Те же принципы могут использоваться для управления различными бэкэдами одинаковой функциональности без изменения драйвера.
Ответ 6
Всякий раз, когда вы хотите, до тех пор, пока магические свойства/методы задокументированы; недокументированной магии следует избегать, если не работать с очень абстрактным слоем кода, например при разработке ORM.
приемлемо на абстрактном уровне
/**
* DB backed model base class.
*/
class Model {
protected $attributes = [];
function __get($name) {
return @$this->attributes[$name];
}
}
приемлемо при документировании
/**
* User model backed by DB tables.
* @property-read string $first_name
* @property-read string $last_name
*/
class UserModel extends Model {
}
ленивый и неприемлемый (и распространенный при использовании ORM)
/**
* This class is magical and awesome and I am a lazy shithead!
*/
class UserModel extends WhoCaresWhenEverythingIsMagical {
}