O ile całkowicie się nie mylę, metody __get
i __set
mają pozwolić na przeciążenie → get
i set
.
Na przykład poniższe instrukcje powinny wywołać __get
metodę:
echo $foo->bar;
$var = $foo->bar;
Z __set
metody tej powinny skorzystać :
$foo->bar = 'test';
To nie działało w moim kodzie i można je odtworzyć za pomocą tego prostego przykładu:
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]";
Skutkuje to tylko:
[test]
Umieszczenie tam niektórych die()
połączeń pokazuje, że w ogóle nie trafia.
Na razie powiedziałem tylko „wkręć” i używam ręcznie __get
tam, gdzie jest to potrzebne, ale nie jest to zbyt dynamiczne i wymaga wiedzy, że „przeciążony” kod w rzeczywistości nie jest wywoływany, chyba że zostanie specjalnie wywołany. Chciałbym wiedzieć, czy to albo nie powinno działać tak, jak zrozumiałem, lub dlaczego nie działa.
To trwa php 5.3.3
.
źródło
node
nie jest to własność $ foo, ale własnośćdoesNotExist
. Tak więc, chyba że „doesNotExist” jest obiektem (który implementuje __set lub ma publiczną właściwość o nazwie node), to nie zadziała.Polecam użycie tablicy do przechowywania wszystkich wartości za pośrednictwem
__set()
.class foo { protected $values = array(); public function __get( $key ) { return $this->values[ $key ]; } public function __set( $key, $value ) { $this->values[ $key ] = $value; } }
W ten sposób upewniasz się, że nie możesz uzyskać dostępu do zmiennych w inny sposób (uwaga, że
$values
jest chroniony), aby uniknąć kolizji.źródło
Z podręcznika PHP :
Jest to wywoływane tylko przy odczytywaniu / zapisywaniu niedostępnych właściwości. Twoja własność jest jednak publiczna, co oznacza, że jest dostępna. Zmiana modyfikatora dostępu na chroniony rozwiązuje problem.
źródło
Aby rozwinąć odpowiedź Berry'ego, że ustawienie poziomu dostępu na protected pozwala __get i __set na użycie z jawnie zadeklarowanymi właściwościami (przynajmniej w przypadku dostępu poza klasą), a prędkość jest znacznie wolniejsza, zacytuję komentarz z innego pytania na ten temat i tak czy owak uzasadnij jego użycie:
Zgadzam się, że __get działa wolniej niż niestandardowa funkcja get (robi to samo), jest to 0,0124455 czas dla __get () i 0,0024445 dla niestandardowej funkcji get () po 10000 pętli. - Melsi Lis 23 '12 o 22:32 Najlepsza praktyka: Magiczne metody PHP __set i __get
Według testów Melsi, znacznie wolniejszy jest około 5 razy wolniejszy. Jest to zdecydowanie wolniejsze, ale należy również zauważyć, że testy pokazują, że nadal można uzyskać dostęp do właściwości za pomocą tej metody 10000 razy, licząc czas iteracji pętli, w przybliżeniu w 1/100 sekundy. Jest znacznie wolniejszy w porównaniu z rzeczywistymi zdefiniowanymi metodami pobierania i ustawiania, i to mało powiedziane, ale w ogólnym rozrachunku nawet 5 razy wolniej nigdy nie jest tak naprawdę powolne.
Czas obliczeniowy operacji jest nadal znikomy i nie warty rozważenia w 99% rzeczywistych aplikacji. Jedynym przypadkiem, którego naprawdę należy unikać, jest fakt, że zamierzasz uzyskać dostęp do właściwości ponad 10000 razy w jednym żądaniu. Witryny o dużym ruchu robią coś naprawdę złego, jeśli nie stać ich na wyrzucenie kilku serwerów więcej, aby ich aplikacje działały. Jednowierszowa reklama tekstowa w stopce witryny o dużym natężeniu ruchu, w której szybkość dostępu staje się problemem, mogłaby prawdopodobnie zapłacić za farmę 1000 serwerów z tym wierszem tekstu. Użytkownik końcowy nigdy nie będzie dotykał palcami, zastanawiając się, co tak długo trwa ładowanie strony, ponieważ dostęp do właściwości aplikacji zajmuje milionową część sekundy.
Mówię, że jako programista wywodzący się z doświadczenia w .NET, ale niewidoczne metody pobierania i ustawiania dla konsumenta nie są wymysłem .NET. Po prostu nie są one właściwościami bez nich, a te magiczne metody są dla programistów PHP zachowaniem, które pozwala nawet nazywanie ich wersji właściwości „właściwościami”. Ponadto rozszerzenie Visual Studio dla PHP obsługuje technologię Intellisense z chronionymi właściwościami, myślę, że mając na uwadze tę sztuczkę. Myślę, że przy wystarczającej liczbie programistów używających w ten sposób magicznych metod __get i __set programiści PHP dostroiliby czas wykonywania, aby zaspokoić potrzeby społeczności programistów.
Edycja: Teoretycznie chronione właściwości wydawały się działać w większości sytuacji. W praktyce okazuje się, że wiele razy będziesz chciał użyć swoich metod pobierających i ustawiających podczas uzyskiwania dostępu do właściwości w ramach definicji klasy i klas rozszerzonych. Lepszym rozwiązaniem jest klasa bazowa i interfejs do rozszerzania innych klas, więc możesz po prostu skopiować kilka wierszy kodu z klasy bazowej do klasy implementującej. Robię trochę więcej z klasą bazową mojego projektu, więc nie mam teraz interfejsu do udostępnienia, ale oto niesprawdzona, uproszczona definicja klasy z magiczną właściwością, która pobiera i ustawia za pomocą odbicia w celu usunięcia i przeniesienia właściwości do chroniona tablica:
/** 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; } }
Przepraszam, jeśli w kodzie są jakieś błędy.
źródło
To dlatego, że $ bar jest własnością publiczną.
$foo->bar = 'test';
Nie ma potrzeby wywoływania magicznej metody podczas uruchamiania powyższego.
Usunięcie
public $bar;
z klasy powinno to naprawić.źródło
Najlepiej używać magicznych metod set / get z predefiniowanymi niestandardowymi metodami set / get, jak w przykładzie poniżej. W ten sposób możesz połączyć to, co najlepsze z dwóch światów. Jeśli chodzi o prędkość, zgadzam się, że są nieco wolniejsze, ale można nawet poczuć różnicę. Przykład poniżej również sprawdza poprawność tablicy danych względem predefiniowanych metod ustawiających.
Dlatego powinniśmy używać obu.
PRZYKŁAD POZYCJI KLASY
/* * 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
WYNIK
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
źródło
Porzuć
public $bar;
deklarację i powinno działać zgodnie z oczekiwaniami.źródło
Intenta con:
__GET($k){ return $this->$k; } _SET($k,$v){ return $this->$k = $v; }
źródło