Обычно я старался держаться подальше от магических методов PHP, потому что они, похоже, запутывают открытый интерфейс объекта. Тем не менее, они, кажется, используются все больше и больше, по крайней мере, в коде, который я читал, поэтому я должен спросить: есть ли какой-либо консенсус относительно того, когда их использовать? Есть ли общие шаблоны использования этих трех магических методов?
Практическое применение магических методов PHP — __get, __set и __call
Ответы (6)
__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'
__call()
вызов запрошенного метода с входными аргументами. $events возражает что-то вроде менеджера событий symfony, WP do_action() и т.д.
- person Denis de Bernardy; 12.05.2011
traits
, но я видел умную реализацию __call
, чтобы попытаться подражать traits
.
- person Clement Herreman; 13.05.2011
Основная причина в том, что вам не нужно печатать так много. Вы можете использовать их, скажем, для записи 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()
имеют еще одно преимущество перед общедоступными участниками: вы можете проверять/форматировать свои данные.
Это позволяет вам делать такие вещи:
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()
). Может быть полезно, например, для поля ID.
Кроме того, вы можете поместить код в __get()
и __set()
, чтобы вы могли делать что-то более сложное, чем просто получение или установка одной переменной. Например, если у вас есть поле storeID
, вы также можете указать свойство storeName
. Вы можете реализовать это в __get()
с помощью поиска по перекрестным ссылкам, поэтому вам может не понадобиться, чтобы имя действительно сохранялось в классе. И, конечно, storeName
не хотел бы быть реализованным в __get()
.
Там много возможностей.
Есть, конечно, и некоторые недостатки использования магических методов. Самым большим для меня является тот факт, что вы теряете функцию автозаполнения в своей среде IDE. Это может или не может иметь для вас значения.
Так как волшебные методы могут сэкономить вам МНОГО кода, когда дело доходит до повторяющихся задач, таких как определение членов, их заполнение и последующее их извлечение — вместо того, чтобы выполнять эту скучную, длинную работу, вы можете использовать упомянутые 3 метода, чтобы сократить время написания кода. все это. Если нужно, я могу привести несколько примеров, хотя их можно найти в различных учебниках в сети.
Я не знаю, является ли это общим мнением, но должно применяться обычное - используйте там, где это уместно. Если вы обнаружите, что выполняете повторяющуюся задачу (определение члена, заполнение члена, получение члена, вызов X-функций, которые немного отличаются) - магические методы могут вам помочь.
__set()
с массивным switch($member) { case 'name': /* some stuff */; break; case 'age': /* other special process */; break; /* Add 98 others case here */ }
? Не я, и я верю людям, которые в конечном итоге будут поддерживать такой код.
- person Clement Herreman; 13.05.2011
<precision> Code generating from the private attribute just the first time <I write the class.</precision>
Говоря только о читабельности и простоте понимания кода, да, явное объявление геттеров и сеттеров лучше. Попытайтесь понять бизнес-классы Magento, я думаю, вы поймете мою точку зрения. Почему это плохо с вашей точки зрения? Слишком много символов для ввода с клавиатуры? Слишком длинный файл класса? Я искренне не понимаю.
- person Clement Herreman; 13.05.2011
Одним из распространенных шаблонов является наличие единого дескриптора для ваших клиентов и проксирование вызовов инкапсулированных объектов или синглетонов на основе соглашений об именах или конфигураций.
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');
Одни и те же принципы могут быть использованы для управления различными серверными частями одной и той же функциональности без необходимости изменения драйвера.
Всякий раз, когда вы хотите, пока магические свойства/методы задокументированы; следует избегать недокументированной магии, если только вы не работаете с очень абстрактным слоем кода, например, при разработке 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 {
}