В PHP GET и set методы
если я не совсем ошибаюсь, то __get и __set методы должны допускать перегрузку → get и set.
например, следующие операторы должны вызывать __get способ:
echo $foo->bar;
$var = $foo->bar;
и следующие должны использовать __set способ:
$foo->bar = 'test';
это не работает в моем коде, и будет воспроизводиться с помощью этого простого примера:
class foo {
public $bar;
public function __get($name) {
echo "Get:$name";
return $this->$name;
}
public function __set($name, $value) {
echo "Set:$name to $value";
$this->$name = $value;
}
}
$foo = new foo();
echo $foo->bar;
$foo->bar = 'test';
echo "[$foo->bar]";
это только результаты в:
[test]
выкладывания die() звонки там показывают, что он не попадает в него вообще.
на данный момент, я просто сказал винт его, и я вручную с помощью __get где это необходимо сейчас, но это не очень динамично и требует знания того, что "перегруженный" код на самом деле не вызывается, если специально не вызывается. Я хотел бы знать, если это либо не должно функционировать так, как я понял, что это должно или почему это не так рабочий.
это работает на php 5.3.3.
7 ответов:
__get,__set,__callи__callStaticвызывается, когда метод или свойство недоступны. Ваш$barявляется общедоступным и поэтому не недоступным.посмотреть раздел о перегрузке свойств в руководстве:
__set()выполняется при записи данных в недоступные свойства.__get()используется для чтения данных из недоступных свойств.магия методы не являются заменой геттеров и сеттеров. Они просто позволяют обрабатывать вызовы методов или доступ к свойствам, которые в противном случае привели бы к ошибке. Таким образом, существует гораздо больше связанных с обработкой ошибок. Также обратите внимание, что они значительно медленнее, чем использование правильного геттера и сеттера или прямых вызовов методов.
Я бы рекомендовал использовать массив для хранения всех значений через
__set().class foo { protected $values = array(); public function __get( $key ) { return $this->values[ $key ]; } public function __set( $key, $value ) { $this->values[ $key ] = $value; } }таким образом, вы убедитесь, что вы не можете получить доступ к переменным в другую сторону (заметим, что
$valuesзащищен), чтобы избежать столкновений.
с PHP manual:
- _ _ set () запускается при записи данных в недоступные свойства.
- _ _ get () используется для чтения данных из недоступных свойств.
Это вызывается только при чтении / записи недоступен свойства. Ваша собственность, однако, является общедоступной, что означает, что она доступна. Изменение модификатора доступа на защищенный решает проблему.
чтобы расширить ответ Берри, что установка уровня доступа на protected позволяет использовать _ _ get и __set с явно объявленными свойствами (по крайней мере, при доступе за пределами класса), а скорость значительно медленнее, я процитирую комментарий из другого вопроса по этой теме и сделаю случай для его использования в любом случае:
Я согласен, что _ _ get медленнее пользовательской функции get (делает то же самое), это 0.0124455 время для _ _ get() и это 0.0024445 для пользовательских get () после 10000 циклов. – Мельси 23 ноября ' 12 в 22: 32 Лучшая практика: PHP Magic Methods _ _ set and __get
согласно тестам Мельси, значительно медленнее примерно в 5 раз медленнее. Это определенно значительно медленнее, но также обратите внимание, что тесты показывают, что вы все еще можете получить доступ к свойству с помощью этого метода 10 000 раз, считая время для итерации цикла примерно за 1/100 секунды. Это значительно медленнее по сравнению с фактическим get и set методы определены, и это преуменьшение, но в великой схеме вещей даже 5 раз медленнее никогда не бывает на самом деле медленным.
вычислительное время операции по-прежнему незначительно и не стоит рассматривать в 99% реальных приложений. Единственный раз, когда этого действительно следует избегать, - это когда вы на самом деле собираетесь получить доступ к свойствам более 10 000 раз в одном запросе. Сайты с высоким трафиком делают что-то действительно неправильное, если они не могут позволить себе бросить еще несколько серверы до держать их приложения запущены. Однострочное текстовое объявление в нижнем колонтитуле сайта с высоким трафиком, где скорость доступа становится проблемой, вероятно, может заплатить за ферму из 1000 серверов с этой строкой текста. Конечный пользователь никогда не будет постукивать пальцами, задаваясь вопросом, что так долго загружает страницу, потому что доступ к свойствам вашего приложения занимает миллионную долю секунды.
Я говорю это, говоря как разработчик, исходящий из фона в .NET, но невидимый методы get и set для потребителя-это не изобретение .NET. Они просто не являются свойствами без них, и эти магические методы являются спасительной благодатью разработчика PHP для даже вызова их версии свойств "свойства" вообще. Кроме того, расширение Visual Studio для PHP поддерживает intellisense с защищенными свойствами, имея в виду этот трюк, я бы подумал. Я бы подумал, что с достаточным количеством разработчиков, использующих методы magic _ _ get и __set таким образом, разработчики PHP настроят выполнение время, чтобы угодить сообществу разработчиков.
Edit: в теории, защищенные свойства, казалось, что это будет работать в большинстве ситуаций. На практике оказывается, что есть много раз, когда вы захотите использовать свои геттеры и сеттеры при доступе к свойствам в определении класса и расширенных классах. Лучшим решением является базовый класс и интерфейс для расширения других классов, поэтому вы можете просто скопировать несколько строк кода из базового класса в класс реализации. Я делая немного больше с базовым классом моего проекта, поэтому у меня нет интерфейса для предоставления прямо сейчас, но вот непроверенное урезанное определение класса с волшебным свойством, получающим и устанавливающим использование отражения для удаления и перемещения свойств в защищенный массив:
/** Base class with magic property __get() and __set() support for defined properties. */ class Component { /** Gets the properties of the class stored after removing the original * definitions to trigger magic __get() and __set() methods when accessed. */ protected $properties = array(); /** Provides property get support. Add a case for the property name to * expand (no break;) or replace (break;) the default get method. When * overriding, call parent::__get($name) first and return if not null, * then be sure to check that the property is in the overriding class * before doing anything, and to implement the default get routine. */ public function __get($name) { $caller = array_shift(debug_backtrace()); $max_access = ReflectionProperty::IS_PUBLIC; if (is_subclass_of($caller['class'], get_class($this))) $max_access = ReflectionProperty::IS_PROTECTED; if ($caller['class'] == get_class($this)) $max_access = ReflectionProperty::IS_PRIVATE; if (!empty($this->properties[$name]) && $this->properties[$name]->class == get_class() && $this->properties[$name]->access <= $max_access) switch ($name) { default: return $this->properties[$name]->value; } } /** Provides property set support. Add a case for the property name to * expand (no break;) or replace (break;) the default set method. When * overriding, call parent::__set($name, $value) first, then be sure to * check that the property is in the overriding class before doing anything, * and to implement the default set routine. */ public function __set($name, $value) { $caller = array_shift(debug_backtrace()); $max_access = ReflectionProperty::IS_PUBLIC; if (is_subclass_of($caller['class'], get_class($this))) $max_access = ReflectionProperty::IS_PROTECTED; if ($caller['class'] == get_class($this)) $max_access = ReflectionProperty::IS_PRIVATE; if (!empty($this->properties[$name]) && $this->properties[$name]->class == get_class() && $this->properties[$name]->access <= $max_access) switch ($name) { default: $this->properties[$name]->value = $value; } } /** Constructor for the Component. Call first when overriding. */ function __construct() { // Removing and moving properties to $properties property for magic // __get() and __set() support. $reflected_class = new ReflectionClass($this); $properties = array(); foreach ($reflected_class->getProperties() as $property) { if ($property->isStatic()) { continue; } $properties[$property->name] = (object)array( 'name' => $property->name, 'value' => $property->value , 'access' => $property->getModifier(), 'class' => get_class($this)); unset($this->{$property->name}); } $this->properties = $properties; } }мои извинения, если есть какие-либо ошибки в коде.
это потому, что $bar является публичной собственностью.
$foo->bar = 'test';нет необходимости вызывать магический метод при запуске выше.
удаление
public $bar;из вашего класса должен исправить это.
лучше всего использовать методы magic set/get с предопределенными пользовательскими методами set / get, как в примере ниже. Таким образом, вы можете объединить лучшее из двух миров. С точки зрения скорости я согласен, что они немного медленнее, но вы даже можете почувствовать разницу. Пример ниже также проверяет массив данных на соответствие предопределенным установщикам.
"магические методы не заменяют геттеров и сеттеров. Они просто позвольте вам обрабатывать вызовы методов или доступ к свойствам, которые будут в противном случае результат по ошибке."
вот почему мы должны использовать оба.
ПРИМЕР ЭЛЕМЕНТА КЛАССА
.PHP/* * Item class */ class Item{ private $data = array(); function __construct($options=""){ //set default to none $this->setNewDataClass($options); //calling function } private function setNewDataClass($options){ foreach ($options as $key => $value) { $method = 'set'.ucfirst($key); //capitalize first letter of the key to preserve camel case convention naming if(is_callable(array($this, $method))){ //use seters setMethod() to set value for this data[key]; $this->$method($value); //execute the setters function }else{ $this->data[$key] = $value; //create new set data[key] = value without seeters; } } } private function setNameOfTheItem($value){ // no filter $this->data['name'] = strtoupper($value); //assign the value return $this->data['name']; // return the value - optional } private function setWeight($value){ //use some kind of filter if($value >= "100"){ $value = "this item is too heavy - sorry - exceeded weight of maximum 99 kg [setters filter]"; } $this->data['weight'] = strtoupper($value); //asign the value return $this->data['weight']; // return the value - optional } function __set($key, $value){ $method = 'set'.ucfirst($key); //capitalize first letter of the key to preserv camell case convention naming if(is_callable(array($this, $method))){ //use seters setMethod() to set value for this data[key]; $this->$method($value); //execute the seeter function }else{ $this->data[$key] = $value; //create new set data[key] = value without seeters; } } function __get($key){ return $this->data[$key]; } function dump(){ var_dump($this); } }$data = array( 'nameOfTheItem' => 'tv', 'weight' => '1000', 'size' => '10x20x30' ); $item = new Item($data); $item->dump(); $item->somethingThatDoNotExists = 0; // this key (key, value) will trigger magic function __set() without any control or check of the input, $item->weight = 99; // this key will trigger predefined setter function of a class - setWeight($value) - value is valid, $item->dump(); $item->weight = 111; // this key will trigger predefined setter function of a class - setWeight($value) - value invalid - will generate warning. $item->dump(); // display object infoвыход
object(Item)[1] private 'data' => array (size=3) 'name' => string 'TV' (length=2) 'weight' => string 'THIS ITEM IS TOO HEAVY - SORRY - EXIDED WEIGHT OF MAXIMUM 99 KG [SETTERS FILTER]' (length=80) 'size' => string '10x20x30' (length=8) object(Item)[1] private 'data' => array (size=4) 'name' => string 'TV' (length=2) 'weight' => string '99' (length=2) 'size' => string '10x20x30' (length=8) 'somethingThatDoNotExists' => int 0 object(Item)[1] private 'data' => array (size=4) 'name' => string 'TV' (length=2) 'weight' => string 'THIS ITEM IS TOO HEAVY - SORRY - EXIDED WEIGHT OF MAXIMUM 99 KG [SETTERS FILTER]' (length=80) 'size' => string '10x20x30' (length=8) 'somethingThatDoNotExists' => int 0
Comments