Je sais que cette question a été posée à plusieurs reprises, mais aucune d’entre elles n’a de véritable solution de rechange. Peut-être qu'il y en a un pour mon cas particulier.
Je construis une classe de mappeur qui utilise la méthode magique __get()
pour charger paresseusement d'autres objets. Cela ressemble à quelque chose comme ça:
public function __get ( $index )
{
if ( isset ($this->vars[$index]) )
{
return $this->vars[$index];
}
// $index = 'role';
$obj = $this->createNewObject ( $index );
return $obj;
}
Dans mon code je fais:
$user = createObject('user');
$user->role->rolename;
Cela fonctionne jusqu'à présent. L'objet User
n'a pas de propriété appelée 'rôle', il utilise donc la méthode magique __get()
pour créer cet objet et renvoie sa propriété à partir de l'objet 'role'.
Mais quand j'essaie de modifier le 'nom de rôle':
$user = createUser();
$user->role->rolename = 'Test';
Ensuite, cela me donne l'erreur suivante:
Remarque: la modification indirecte de la propriété surchargée est sans effet
Je ne sais pas s'il s'agit toujours d'un bogue dans PHP ou s'il s'agit d'un "comportement attendu", mais dans tous les cas, cela ne fonctionne pas comme je le souhaite. C'est vraiment un spectacle pour moi. .. Comment puis-je changer les propriétés des objets chargés paresseux?
MODIFIER:
Le problème réel ne semble se produire que lorsque je retourne un tableau contenant plusieurs objets.
J'ai ajouté un exemple de code qui reproduit le problème:
Vous devriez vraiment exécuter ceci dans votre environnement PHP, voyez vraiment l'erreur). Mais il y a quelque chose de vraiment intéressant qui se passe ici.
J'essaie de changer la propriété d'un objet, ce qui me donne la mention 'ne peut pas changer la propriété surchargée'. Mais si je répète la propriété après cela, je constate qu'elle DID change la valeur ... vraiment bizarre ...
Sympa tu m'as donné quelque chose à jouer avec
Courir
class Sample extends Creator {
}
$a = new Sample ();
$a->role->rolename = 'test';
echo $a->role->rolename , PHP_EOL;
$a->role->rolename->am->love->php = 'w00';
echo $a->role->rolename , PHP_EOL;
echo $a->role->rolename->am->love->php , PHP_EOL;
Sortie
test
test
w00
Classe utilisée
abstract class Creator {
public function __get($name) {
if (! isset ( $this->{$name} )) {
$this->{$name} = new Value ( $name, null );
}
return $this->{$name};
}
public function __set($name, $value) {
$this->{$name} = new Value ( $name, $value );
}
}
class Value extends Creator {
private $name;
private $value;
function __construct($name, $value) {
$this->name = $name;
$this->value = $value;
}
function __toString()
{
return (string) $this->value ;
}
}
class Sample extends Creator {
}
$a = new Sample ();
$a->role = array (
"A",
"B",
"C"
);
$a->role[0]->Nice = "OK" ;
print ($a->role[0]->Nice . PHP_EOL);
$a->role[1]->Nice->ok = array("foo","bar","die");
print ($a->role[1]->Nice->ok[2] . PHP_EOL);
$a->role[2]->Nice->raw = new stdClass();
$a->role[2]->Nice->raw->name = "baba" ;
print ($a->role[2]->Nice->raw->name. PHP_EOL);
Sortie
Ok die baba
Classe modifiée
abstract class Creator {
public function __get($name) {
if (! isset ( $this->{$name} )) {
$this->{$name} = new Value ( $name, null );
}
return $this->{$name};
}
public function __set($name, $value) {
if (is_array ( $value )) {
array_walk ( $value, function (&$item, $key) {
$item = new Value ( $key, $item );
} );
}
$this->{$name} = $value;
}
}
class Value {
private $name ;
function __construct($name, $value) {
$this->{$name} = $value;
$this->name = $value ;
}
public function __get($name) {
if (! isset ( $this->{$name} )) {
$this->{$name} = new Value ( $name, null );
}
if ($name == $this->name) {
return $this->value;
}
return $this->{$name};
}
public function __set($name, $value) {
if (is_array ( $value )) {
array_walk ( $value, function (&$item, $key) {
$item = new Value ( $key, $item );
} );
}
$this->{$name} = $value;
}
public function __toString() {
return (string) $this->name ;
}
}
Tout ce que vous avez à faire est d’ajouter "&" devant votre fonction __get pour le transmettre comme référence:
public function &__get ( $index )
Lutté avec celui-ci pendant un moment.
J'ai eu la même erreur, sans tout votre code, il est difficile de déterminer exactement comment le réparer, mais cela est dû au fait que vous n'avez pas de fonction __set.
La façon dont je l’ai contourné par le passé est que j’ai fait des choses comme celle-ci:
$user = createUser();
$role = $user->role;
$role->rolename = 'Test';
maintenant si vous faites ceci:
echo $user->role->rolename;
vous devriez voir 'Test'
Je recevais cet avis pour cela:
$var = reset($myClass->my_magic_property);
Cela l'a corrigé:
$tmp = $myClass->my_magic_property;
$var = reset($tmp);
Bien que je sois très en retard dans cette discussion, j'ai pensé que cela pourrait être utile pour quelqu'un à l'avenir.
J'avais fait face à une situation similaire. La solution de contournement la plus simple pour ceux qui ne craignent pas de réinitialiser et de réinitialiser la variable consiste à le faire. Je suis à peu près sûr que la raison pour laquelle cela ne fonctionne pas est clairement indiquée dans les autres réponses et dans le manuel php.net. La solution la plus simple a fonctionné pour moi est
Supposition:
$object
est l’objet surchargé __get
et __set
de la classe de base, que je ne suis pas libre de modifier.shippingData
est le tableau dont je veux modifier un champ, par exemple. :- numéro de téléphone// First store the array in a local variable.
$tempShippingData = $object->shippingData;
unset($object->shippingData);
$tempShippingData['phone_number'] = '888-666-0000' // what ever the value you want to set
$object->shippingData = $tempShippingData; // this will again call the __set and set the array variable
unset($tempShippingData);
Remarque: cette solution est l’une des solutions rapides permettant de résoudre le problème et de copier la variable. Si le tableau est trop énorme, il peut être utile de forcer la réécriture du __get
méthode pour renvoyer une référence assez coûteuse à la copie de grands tableaux.
J'ai rencontré le même problème que w00, mais je n'avais pas la liberté de réécrire les fonctionnalités de base du composant dans lequel ce problème (E_NOTICE) se produisait. J'ai pu résoudre le problème en utilisant un ArrayObject au lieu du type de base array () . Cela retournera un objet, qui sera renvoyé par défaut.
Je suis d’accord avec VinnyD que ce que vous devez faire, c’est d’ajouter "&" devant votre fonction __get, pour que le résultat voulu soit renvoyé comme référence:
public function &__get ( $propertyname )
Mais soyez conscient de deux choses:
1) Vous devriez aussi faire
return &$something;
ou vous retournez peut-être encore une valeur et non une référence ...
2) N'oubliez pas que dans tous les cas où __get renvoie une référence, cela signifie également que le __set correspondant ne sera JAMAIS appelé; c'est parce que php résout ce problème en utilisant la référence renvoyée par __get, appelée à la place!
Alors:
$var = $object->NonExistentArrayProperty;
signifie que __get est appelé et, puisque __get a & __ get and return & $ quelque chose, $ var est maintenant, comme prévu, une référence à la propriété surchargée ...
$object->NonExistentArrayProperty = array();
fonctionne comme prévu et __set est appelé comme prévu ...
Mais:
$object->NonExistentArrayProperty[] = $value;
ou
$object->NonExistentArrayProperty["index"] = $value;
fonctionne comme prévu dans le sens où l'élément sera correctement ajouté ou modifié dans la propriété de tableau surchargé, MAIS __set NE SERA PAS APPELÉ: __get sera appelé à la place!
Ces deux appels NE fonctionneraient PAS s'ils n'utilisaient pas & __ obtenir et retourner quelque chose, mais ils fonctionnent de cette façon, mais ils n'appellent JAMAIS __set, mais ils appellent toujours __get.
C'est pourquoi j'ai décidé de renvoyer une référence
return &$something;
lorsque $ quelque chose est un tableau () ou lorsque la propriété surchargée n'a pas de méthode de définition spéciale et renvoie à la place une valeur
return $something;
quand $ quelque chose n'est PAS un tableau ou a une fonction spéciale de définition.
En tout cas, c'était assez difficile à comprendre pour moi! :)
Ceci est dû à la façon dont PHP traite les propriétés surchargées en ce sens qu'elles ne sont pas modifiables ni transmises par référence.
Voir le manuel pour plus d'informations sur la surcharge.
Pour contourner ce problème, vous pouvez soit utiliser un __set
fonction ou créer une méthode createObject
.
Ci-dessous, un __get
et __set
qui fournit une solution de contournement à une situation similaire à la vôtre, vous pouvez simplement modifier le __set
pour répondre à vos besoins.
Noter la __get
ne renvoie jamais réellement une variable. et plutôt une fois que vous avez défini une variable dans votre objet, elle n'est plus surchargée.
/**
* Get a variable in the event.
*
* @param mixed $key Variable name.
*
* @return mixed|null
*/
public function __get($key)
{
throw new \LogicException(sprintf(
"Call to undefined event property %s",
$key
));
}
/**
* Set a variable in the event.
*
* @param string $key Name of variable
*
* @param mixed $value Value to variable
*
* @return boolean True
*/
public function __set($key, $value)
{
if (stripos($key, '_') === 0 && isset($this->$key)) {
throw new \LogicException(sprintf(
"%s is a read-only event property",
$key
));
}
$this->$key = $value;
return true;
}
Ce qui permettra:
$object = new obj();
$object->a = array();
$object->a[] = "b";
$object->v = new obj();
$object->v->a = "b";