Если я не совсем ошибаюсь, __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
.
источник
node
это не свойство $ foo, а свойствоdoesNotExist
. Итак, если 'doesNotExist' не является объектом (который реализует __set или имеет общедоступное свойство, называемое node), он не будет работать.Я бы рекомендовал использовать массив для хранения всех значений через
__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 :
Это вызывается только при чтении / записи недоступных свойств. Однако ваша собственность является общедоступной, что означает, что она доступна. Изменение модификатора доступа на защищенный решает проблему.
источник
Чтобы расширить ответ Берри, установка уровня доступа на protected позволяет использовать __get и __set с явно объявленными свойствами (по крайней мере, при доступе за пределами класса), а скорость значительно ниже, я процитирую комментарий из другого вопроса по этой теме и в любом случае приведите аргументы в пользу его использования:
Я согласен с тем, что __get медленнее настраивается на пользовательскую функцию get (делает то же самое), это 0,0124455 время для __get (), а 0,0024445 - для настраиваемого get () после 10000 циклов. - Мелси, 23 ноя., 2012, в 22:32 Лучшая практика: магические методы PHP __set и __get
Согласно тестам Мелси, значительно медленнее - примерно в 5 раз медленнее. Это определенно значительно медленнее, но также обратите внимание, что тесты показывают, что вы все еще можете получить доступ к свойству с помощью этого метода 10 000 раз, считая время для итерации цикла, примерно за 1/100 секунды. Это значительно медленнее по сравнению с определенными фактическими методами get и set, и это еще не все, но в целом даже в 5 раз медленнее никогда не бывает.
Время вычисления операции по-прежнему ничтожно и не стоит учитывать в 99% реальных приложений. Единственный раз, когда этого действительно следует избегать, - это когда вы фактически собираетесь обращаться к свойствам более 10 000 раз за один запрос. Сайты с высоким трафиком делают что-то действительно неправильно, если они не могут позволить себе добавить еще несколько серверов, чтобы их приложения работали. Однострочное текстовое объявление в нижнем колонтитуле сайта с высоким трафиком, где скорость доступа становится проблемой, вероятно, могло бы заплатить за ферму из 1000 серверов с этой строкой текста. Конечный пользователь никогда не будет постукивать пальцами, задаваясь вопросом, почему страница так долго загружается, потому что доступ к свойствам вашего приложения занимает миллионную долю секунды.
Я говорю это, говоря как разработчик, имеющий опыт работы с .NET, но невидимые для потребителя методы get и set не являются изобретением .NET. Они просто не являются свойствами без них, и эти волшебные методы - спасительная изящество разработчика PHP, которое даже может назвать свою версию свойств «свойствами». Кроме того, расширение Visual Studio для PHP поддерживает intellisense с защищенными свойствами, я думаю, имея в виду этот трюк. Я бы подумал, что с достаточным количеством разработчиков, использующих магические методы __get и __set таким образом, разработчики PHP настроят время выполнения, чтобы удовлетворить потребности сообщества разработчиков.
Изменить: Теоретически защищенные свойства казалось, что они будут работать в большинстве ситуаций. На практике оказывается, что во многих случаях вы захотите использовать свои геттеры и сеттеры при доступе к свойствам в определении класса и расширенных классах. Лучшее решение - это базовый класс и интерфейс для расширения других классов, поэтому вы можете просто скопировать несколько строк кода из базового класса в реализующий класс. Я делаю немного больше с базовым классом моего проекта, поэтому у меня нет интерфейса, который я мог бы предоставить прямо сейчас, но вот непроверенное урезанное определение класса с получением и настройкой магического свойства с использованием отражения для удаления и перемещения свойств в защищенный массив:
/** 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;
из вашего класса должно исправить это.источник
Лучше всего использовать магические методы set / get с предопределенными пользовательскими методами set / get, как в примере ниже. Таким образом вы сможете объединить лучшее из двух миров. Что касается скорости, я согласен, что они немного медленнее, но вы даже можете почувствовать разницу. В приведенном ниже примере также проверяется соответствие массива данных предопределенным сеттерам.
Вот почему мы должны использовать оба.
КЛАСС ПУНКТ ПРИМЕР
/* * 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); } }
INDEX.PHP
$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
источник
Отбросьте
public $bar;
объявление, и оно должно работать должным образом.источник
Intenta con:
__GET($k){ return $this->$k; } _SET($k,$v){ return $this->$k = $v; }
источник