88 votes

PHP __get et __set méthodes magiques

Sauf si je suis complètement trompé, l' __get et __set méthodes sont censés permettre la surcharge de l' → get et set.

Par exemple, les instructions suivantes doivent invoquer l' __get méthode:

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

Et le suivant doit utiliser l' __set méthode:

$foo->bar = 'test';

Ce n'était pas de travail dans mon code, et est reproductible avec cet exemple simple:

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]";

Ce seul résultat:

[test]

Mettre un peu de die() des appels dans il montre qu'il n'est pas de frapper à toutes.

Pour l'instant, j'ai juste dit à vis, et je suis manuellement à l'aide d' __get là où elle est nécessaire pour l'instant, mais ce n'est pas très dynamique et nécessite des connaissances que la "surcharge" de code est en fait de ne pas être appelé, sauf si expressément demandé. Je voudrais savoir si ce n'est pas censé fonctionner de la manière que j'ai compris qu'il devrait ou pourquoi cela ne fonctionne pas.

C'est en cours d'exécution sur php 5.3.3.

174voto

Gordon Points 156415

__get, __set, __call et __callStatic sont invoquées lorsque la méthode ou propriété n'est pas accessible. Votre $bar est public et donc pas inaccessible.

Voir la section sur la Propriété de Surcharge dans le manuel:

  • __set() est exécuté lorsque l'écriture des données inaccessibles propriétés.
  • __get() est utilisé pour la lecture des données inaccessibles à partir de propriétés.

Les méthodes magiques ne sont pas des substituts pour les getters et les setters. Ils ont juste vous permettent de gérer les appels de méthode ou de l'accès à la propriété qui serait autrement une erreur. En tant que tel, il y a beaucoup plus liée à l'erreur de manipulation. Notez également qu'elles sont considérablement plus lente que l'utilisation appropriée de lecture et de définition ou de diriger les appels de méthode.

39voto

faileN Points 2746

Je recommanderais d'utiliser un tableau pour stocker toutes les valeurs via __set() .

 class foo {

    protected $values = array();

    public function __get( $key )
    {
        return $this->values[ $key ];
    }

    public function __set( $key, $value )
    {
        $this->values[ $key ] = $value;
    }

}
 

De cette façon, vous vous assurez que vous ne pouvez pas accéder aux variables d'une autre manière (notez que $values est protégé), afin d'éviter les collisions.

19voto

Berry Langerak Points 11119

Du manuel PHP :

  • __set () est exécuté lors de l'écriture de données dans des propriétés inaccessibles.
  • __get () est utilisé pour lire des données à partir de propriétés inaccessibles.

Ceci est appelé uniquement lors de la lecture / écriture de propriétés inaccessibles . Cependant, votre propriété est publique, ce qui signifie qu’elle est accessible. Le fait de modifier le modificateur d'accès sur protégé résout le problème.

8voto

Jason Ensinger Points 11

Pour développer Berry, en réponse, que le réglage du niveau d'accès protégé permet d' __get et __set pour être utilisé avec déclarées explicitement les propriétés (lors de l'accès à l'extérieur de la classe, au moins) et la vitesse est beaucoup plus lente, je vais vous citer un commentaire à partir d'une autre question sur ce sujet et de faire un cas pour l'aide quand même:

*Je suis d'accord qu' __get est plus lente à une coutume fonction get (faire les mêmes choses), c'est 0.0124455 le temps de __get() et ce 0.0024445 est de coutume get() après 10000 boucles.* – Melsi Nov 23 '12 à 22:32 Meilleures pratiques: PHP les Méthodes Magiques __ensemble et __get

Selon Melsi tests, beaucoup plus lent est environ 5 fois plus lent. C'est certainement beaucoup plus lent, mais aussi de noter que les tests montrent que vous pouvez toujours accéder à la propriété avec cette méthode 10 000 fois, le décompte du temps de la boucle d'itération, dans environ 1/100 de seconde. Il est beaucoup plus lent en comparaison avec la réalité des méthodes get et set défini, et c'est un euphémisme, mais dans le grand schéma des choses, même 5 fois plus lent n'est jamais vraiment lent.

Le temps de calcul de l'opération est encore négligeable et pas la peine d'envisager, dans 99% des applications du monde réel. La seule fois où il devrait vraiment être évité, c'est quand vous êtes en fait accédant aux propriétés de plus de 10 000 fois en une seule demande. Des sites très fréquentés sont en train de faire quelque chose de vraiment mauvais si ils ne peuvent pas se permettre de jeter un peu plus de serveurs que pour maintenir leurs applications en cours d'exécution. Une seule ligne de texte d'annonce sur le pied de page d'un fort trafic de site où le taux d'accès devient une question pourrait probablement payer pour une ferme de 1 000 serveurs avec cette ligne de texte. L'utilisateur final ne va jamais à exploiter leurs doigts se demander quelle est la prise de la page si longtemps à charger en raison de votre application, l'accès à la propriété prend un millionième de seconde.

Je dis cela en tant que développeur de venir d'un milieu .NET, mais invisible méthodes get et set pour le consommateur ne l'est pas .NET de l'invention. Ils ne sont tout simplement pas les propriétés sans eux, et ces méthodes magiques sont de PHP, développeur de la grâce salvatrice de même pour l'appel de leur version de propriétés "propriétés". Aussi, l'extension de Visual Studio pour PHP prend en charge intellisense avec des biens protégés, avec ce truc dans l'esprit, je pense. Je pense qu'avec suffisamment de développeurs à l'aide de l'magiques __get et __set méthodes de cette manière, le PHP et les développeurs de régler le temps d'exécution pour répondre à la communauté des développeurs.

Edit: En théorie, les propriétés protégées semblait qu'il fallait dans la plupart des situations. Dans la pratique, il s'avère qu'il y a un grand nombre de fois, vous allez avoir à utiliser votre getters et setters lors de l'accès à des propriétés au sein de la définition de la classe et de l'étendue des classes. Une meilleure solution est une classe de base et de l'interface lors de l'extension d'autres classes, de sorte que vous pouvez simplement copier quelques lignes de code à partir de la classe de base dans la mise en œuvre de la classe. Je suis en train de faire un peu plus avec mon projet de classe de base, donc je n'ai pas une interface de fournir dès maintenant, mais ici, c'est la non testé dépouillé définition de la classe avec la magie de la propriété l'obtention et la configuration à l'aide de la réflexion afin de supprimer et de déplacer les propriétés protégé tableau:

/** 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;
    }
}

Toutes mes excuses si il y a des bogues dans le code.

5voto

Matt Lowden Points 1859

C'est parce que $ bar est une propriété publique.

 $foo->bar = 'test';
 

Il n’est pas nécessaire d’appeler la méthode magique lors de l’exécution de ce qui précède.

Supprimer public $bar; de votre classe devrait corriger cela.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X