À moins que je ne me trompe complètement, les méthodes __get
et __set
sont censées permettre de surcharger les → get
et set
.
Par exemple, les instructions suivantes doivent appeler la __get
méthode:
echo $foo->bar;
$var = $foo->bar;
Et ce qui suit devrait utiliser la __set
méthode:
$foo->bar = 'test';
Cela ne fonctionnait pas 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]";
Cela se traduit uniquement par:
[test]
Mettre quelques die()
appels là-dedans montre que cela ne touche pas du tout.
Pour l'instant, je viens de dire visser, et j'utilise manuellement __get
là où c'est nécessaire pour l'instant, mais ce n'est pas très dynamique et nécessite de savoir que le code `` surchargé '' n'est en fait pas appelé à moins d'être spécifiquement appelé. J'aimerais savoir si cela n'est pas censé fonctionner comme je l'ai compris ou pourquoi cela ne fonctionne pas.
Cela continue php 5.3.3
.
la source
node
n'est pas une propriété de $ foo mais une propriété dedoesNotExist
. Donc, à moins que 'doesNotExist' soit un objet (qui implémente __set ou possède une propriété publique appelée node), cela ne fonctionnera pas.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
c'est protégé), pour éviter les collisions.la source
Depuis le manuel PHP :
Ceci n'est appelé que lors de la lecture / écriture de propriétés inaccessibles . Votre propriété est cependant publique, ce qui signifie qu'elle est accessible. La modification du modificateur d'accès en protégé résout le problème.
la source
Pour développer la réponse de Berry, le fait de définir le niveau d'accès sur protected permet d'utiliser __get et __set avec des propriétés explicitement déclarées (au moins en cas d'accès en dehors de la classe) et la vitesse étant considérablement plus lente, je vais citer un commentaire d'une autre question sur ce sujet et faites un cas pour l'utiliser quand même:
Je suis d'accord que __get est plus lent à une fonction get personnalisée (faisant les mêmes choses), c'est 0,0124455 le temps pour __get () et ce 0,0024445 est pour custom get () après 10000 boucles. - Melsi 23 novembre 12 à 22:32 Meilleures pratiques: méthodes PHP Magic __set et __get
Selon les tests de Melsi, considérablement plus lent est environ 5 fois plus lent. C'est certainement beaucoup plus lent, mais notez également que les tests montrent que vous pouvez toujours accéder à une propriété avec cette méthode 10000 fois, en comptant le temps pour l'itération de la boucle, en environ 1/100 de seconde. Il est considérablement plus lent en comparaison avec les méthodes get et set définies, 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 ne vaut pas la peine d'être pris en compte dans 99% des applications du monde réel. Le seul moment où cela devrait vraiment être évité, c'est lorsque vous allez réellement accéder aux propriétés plus de 10000 fois en une seule demande. Les sites à fort trafic font quelque chose de vraiment mal s'ils ne peuvent pas se permettre de lancer quelques serveurs supplémentaires pour maintenir leurs applications en marche. Une annonce textuelle sur une seule ligne au pied d'un site à fort trafic où le taux d'accès devient un problème pourrait probablement payer pour une batterie de 1 000 serveurs avec cette ligne de texte. L'utilisateur final ne se demandera jamais ce qui prend si longtemps à charger la page, car l'accès aux propriétés de votre application prend un millionième de seconde.
Je dis cela en tant que développeur issu d'une expérience dans .NET, mais les méthodes get and set invisibles pour le consommateur ne sont pas l'invention de .NET. Ce ne sont tout simplement pas des propriétés sans elles, et ces méthodes magiques sont la grâce salvatrice du développeur PHP pour même appeler leur version de propriétés "propriétés". De plus, l'extension Visual Studio pour PHP prend en charge intellisense avec des propriétés protégées, avec cette astuce à l'esprit, je pense. Je pense qu'avec suffisamment de développeurs utilisant les méthodes magiques __get et __set de cette façon, les développeurs PHP ajusteraient le temps d'exécution pour répondre à la communauté des développeurs.
Edit: En théorie, les propriétés protégées semblaient fonctionner dans la plupart des situations. En pratique, il s'avère que vous voudrez souvent utiliser vos getters et setters lors de l'accès aux propriétés dans la définition de classe et les classes étendues. Une meilleure solution est une classe de base et une interface pour l'extension d'autres classes, vous pouvez donc simplement copier les quelques lignes de code de la classe de base dans la classe d'implémentation. Je fais un peu plus avec la classe de base de mon projet, donc je n'ai pas d'interface à fournir pour le moment, mais voici la définition de classe dépouillée non testée avec la propriété magique obtenant et définissant en utilisant la réflexion pour supprimer et déplacer les propriétés vers un tableau protégé:
/** 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; } }
Mes excuses s'il y a des bogues dans le code.
la source
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.
La suppression
public $bar;
de votre classe devrait corriger cela.la source
Utilisez au mieux les méthodes magic set / get avec des méthodes set / get personnalisées prédéfinies comme dans l'exemple ci-dessous. De cette façon, vous pouvez combiner le meilleur de deux mondes. En termes de vitesse, je conviens qu'ils sont un peu plus lents mais pouvez-vous même sentir la différence. L'exemple ci-dessous valide également le tableau de données par rapport à des paramètres prédéfinis.
C'est pourquoi nous devrions utiliser les deux.
EXEMPLE D'ARTICLE DE CLASSE
/* * 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
PRODUCTION
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
la source
Supprimez la
public $bar;
déclaration et cela devrait fonctionner comme prévu.la source
Intenta con:
__GET($k){ return $this->$k; } _SET($k,$v){ return $this->$k = $v; }
la source