PHP Pobierz i ustaw magiczne metody

O ile się całkowicie nie mylę, metody __get i __set mają pozwolić na przeciążenie → get i set.

Na przykład następujące polecenia powinny wywoływać metodę __get:

echo $foo->bar;
$var = $foo->bar;

I należy zastosować metodę __set:

$foo->bar = 'test';

To nie działało w moim kodzie i jest odtwarzalne z 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]";

Daje to tylko:

[test]

Umieszczenie tam niektórych die() połączeń pokazuje, że nie jest / align = "left" /

Na razie powiedziałem, żeby to wkręcić i ręcznie używam __get tam, gdzie jest to na razie potrzebne, ale to nie jest zbyt dynamiczne i wymaga wiedzy, że "przeciążony" kod w rzeczywistości nie jest wywoływany, chyba że jest specjalnie wywoływany. Chciałbym wiedzieć, czy to nie powinno działać tak, jak zrozumiałem, i dlaczego to nie działa.

To działa na php 5.3.3.

Author: idmean, 2011-01-17

7 answers

__get, __set, __call i __callStatic są wywoływane, gdy metoda lub właściwość jest niedostępna. Twoje $bar jest publiczne i do nich nie jest niedostępne.

Zobacz sekcję dotyczącą przeciążenia nieruchomości w instrukcji:

  • __set() jest uruchamiany podczas zapisu danych do niedostępnych właściwości.
  • __get() jest wykorzystywany do odczytu danych z niedostępnych właściwości.
Magiczne metody nie są substytutami getterów i seterów. Oni po prostu umożliwia obsługę wywołań metod lub dostępu do właściwości, które w przeciwnym razie spowodowałyby błąd. Jako takie, istnieje znacznie więcej związanych z obsługą błędów. Zauważ również, że są one znacznie wolniejsze niż używanie WŁAŚCIWEGO gettera i settera lub bezpośrednich wywołań metod.
 139
Author: Gordon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-01-17 13:34:30

Zalecam użycie tablicy do przechowywania wszystkich wartości przez __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 (zauważ, że $values jest chroniony), aby uniknąć kolizji.

 28
Author: Alex Sawallich,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-05 11:49:25

Z instrukcji PHP :

  • __set() jest uruchamiany podczas zapisu danych do niedostępnych właściwości.
  • __get() jest używana do odczytu danych z niedostępnych właściwości.

Jest to wywoływane tylko przy odczycie/zapisie niedostępnych właściwości . Twoja nieruchomość jest jednak publiczna, co oznacza, że jest dostępna. Zmiana modyfikatora dostępu na chroniony rozwiązuje problem.

 16
Author: Berry Langerak,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-01-17 13:36:01

Aby rozwinąć odpowiedź Berry ' ego, że ustawienie poziomu dostępu na protected pozwala _ _ get I _ _ set używać z jawnie zadeklarowanymi właściwościami (jeśli są dostępne poza klasą, co najmniej), a prędkość jest znacznie wolniejsza, zacytuję komentarz z innego pytania na ten temat i zrobię argumenty za używaniem go tak czy Inaczej:

zgadzam się, że _ _ get jest bardziej powolne dla niestandardowej funkcji get (robiąc to samo), to jest 0.0124455 czas dla _ _ get() i to 0.0024445 jest dla niestandardowej get () po 10000 pętli. – Melsi Nov 23 ' 12 at 22: 32 najlepsza praktyka: PHP magiczne metody _ _ set I __get

Według testów Melsi, znacznie wolniej jest około 5 razy wolniej. Jest to zdecydowanie znacznie wolniejsze, ale należy również pamiętać, że testy pokazują, że nadal można uzyskać dostęp do właściwości za pomocą tej metody 10 000 razy, licząc czas iteracji pętli, w przybliżeniu 1/100 sekundy. Jest znacznie wolniejsza w porównaniu z rzeczywistymi zdefiniowanymi metodami get I set, i że to mało powiedziane, ale w wielkim układzie rzeczy, nawet 5 razy wolniej nigdy nie jest tak naprawdę powolny.

Czas obliczeniowy operacji jest nadal znikomy i nie warto brać pod uwagę w 99% rzeczywistych zastosowań. Jedynym czasem, kiedy naprawdę należy tego unikać, jest to, że masz zamiar uzyskać dostęp do właściwości ponad 10 000 razy w jednym żądaniu. Witryny o dużym natężeniu ruchu robią coś naprawdę złego, jeśli nie stać ich na wrzucenie kilku serwerów, aby utrzymać ich uruchomione aplikacje. Pojedyncza reklama tekstowa w stopce witryny o dużym natężeniu ruchu, w której wskaźnik dostępu staje się problemem, może prawdopodobnie zapłacić za farmę serwerów 1,000 z tą linią tekstu. Użytkownik końcowy nigdy nie będzie dotykał palcami zastanawiając się, co trwa tak długo, aby załadować stronę, ponieważ dostęp do właściwości aplikacji zajmuje milionową sekundę.

Mówię to jako programista pochodzący z tła w. NET, ale niewidoczne get i ustawić metody do konsument nie jest wynalazkiem. NET. Po prostu nie są one właściwościami bez nich, a te magiczne metody są ratunkiem programisty PHP dla nawet nazywania ich wersji właściwości "właściwościami" w ogóle. Ponadto rozszerzenie Visual Studio dla PHP obsługuje intellisense z chronionymi właściwościami, z myślą o tej sztuczce. Myślę, że z wystarczającą ilością programistów korzystających z magicznych metod _ _ get I _ _ set w ten sposób, programiści PHP dostroiliby czas wykonania, aby zaspokoić społeczność programistów.

Edit: 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żywać getterów i seterów podczas uzyskiwania dostępu do właściwości w ramach definicji klasy i klas rozszerzonych. Lepszym rozwiązaniem jest klasa bazowa i interfejs dla rozszerzania innych klas, więc możesz po prostu skopiować kilka linii kodu z klasy bazowej do klasy implementującej. Robię trochę więcej z moim klasy bazowej projektu, więc nie mam teraz interfejsu do dostarczenia, ale tutaj jest niesprawdzona, rozebrana definicja klasy z magicznymi właściwościami uzyskiwania i ustawiania za pomocą refleksji, aby usunąć i przenieść właściwości do chronionej tablicy:

/** 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.
 5
Author: Jason Ensinger,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:10:36

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 poprawić.

 4
Author: Matt Lowden,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-01-17 13:36:14

Porzuć deklarację public $bar; i powinna działać zgodnie z oczekiwaniami.

 0
Author: Alix Axel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-01-17 13:36:42

Najlepiej używać metod magic set / get z predefiniowanymi niestandardowymi metodami set/get, jak w przykładzie poniżej. W ten sposób można połączyć najlepsze z dwóch światów. Jeśli chodzi o prędkość to Zgadzam się, że są trochę wolniejsze, ale czy można w ogóle poczuć różnicę. Poniższy przykład sprawdza również tablicę danych względem predefiniowanych ustawiaczy.

"magiczne metody nie są substytutami getterów i seterów. Oni po prostu pozwalają obsługiwać wywołania metod lub dostęp do właściwości, które w przeciwnym razie skutkuje błąd."

Dlatego powinniśmy używać obu.

PRZYKŁAD ELEMENTU 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);
    }
}

Indeks.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

Wyjście

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
 0
Author: DevWL,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-25 15:45:23