Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Probleme de retour par reference

6 réponses
Avatar
phep
Bonjour,

J'ai beau lire la doc (language.references.return.php), ses notes
(notamment la deuxième, relative au E_NOTICE de php>=4.4.0 - c'est le
problème que j'essaie de résoudre), ses commentaires (celui du 9 octobre
dernier), et les rapports de bugs (notamment les bugs 26439, 24687 et
33884), je reste perplexe ...

Je veux bien comprendre qu'on ne puisse pas écrire un truc du genre :

return new MyClass();

et qu'on soit obligé de déclarer une variable intermédiaire :

$v = new MyClass();
return $v;

Un peu tordu mais bon...

Mais quelqu'un pourrait-il m'expliquer pourquoi le bout de code suivant
(qui implémente le pattern Singleton de manière basique) marche bien
(depuis des années, contrairement à ce qu'on peut lire ici ou là) mais
génère depuis 4.4.0 un E_NOTICE "Only variable references should be
returned by reference" ?

<?php
error_reporting(E_ALL);

class Aclass {
var $val = 0;

function & Aclass () {
$this->val = 0;
}

function & getInstance() {
static $instance;
if ($instance === NULL) {
$instance = new Aclass();
}
return $instance;
}

}

$a =& Aclass::getInstance();
echo "<p>" . $a->val . "</p>";
$a->val += 1;
echo "<p>" . $a->val . "</p>";

$b =& Aclass::getInstance();
echo "<p>" . $b->val . "</p>";
$b->val += 1;
echo "<p>" . $b->val . "</p>";
?>

Chez moi (TM), avec php4.4.0 ce code crache :
0
1
1
2

Ce qui est bien ce que j'attends, mais j'ai malheureusement, depuis le
passage 4.3 -> 4.4, le fameux E_NOTICE.

Je pourrais fermer les yeux (puisque ça marche) mais j'aime pas ne pas
comprendre :-( Je retourne pourtant bien une _variable_ ('return
$instance') et pas une expression. D'ailleurs le message d'erreur me
renvoie à la parenthèse fermante du constructeur, pas de 'getInstance()'.

Ayant lu des trucs bizarres au sujet des variables statiques, j'ai
remplacé '$instance' par une variable dans $GLOBALS sans que celà ne
change rien du tout : même punition, même motif.

Si une bonne âme y comprend quelque chose, je lui en serais grandement
reconnaissant !

phep

6 réponses

Avatar
ftc
[SNIP]

Mais quelqu'un pourrait-il m'expliquer pourquoi le bout de code suivant
(qui implémente le pattern Singleton de manière basique) marche bien
(depuis des années, contrairement à ce qu'on peut lire ici ou là)


Il marche parce que tu utilise le "work around" du bug sur les variables
statiques dans les objets.

Ton code aurait du être:

function & getInstance() {
static $instance;
if ($instance === NULL) {
$instance = & new Aclass();
}
return $instance;
}

Et ce code là ne fonctionne pas ( du moins avec PHP < 4.4 ).

mais
génère depuis 4.4.0 un E_NOTICE "Only variable references should be
returned by reference" ?


[SNIP]



function & Aclass () {
$this->val = 0;
}


Si tu enleve le & pour le retour du constructeur, ça ne marche pas mieux ?

Le constructeur ne retourne rien ( return void ), il ne sert qu'à
initialiser tes variables, on n'est pas en Python, c'est l'opérateur new
qui se charge de la réservation en mémoire et qui retourne le pointeur
de celle-ci, pas le constructeur.

Avatar
Reno
Il y a des chances que ce bug soit dû au Zend Optimizer (voir le bug
34009).

A part ça, êtes vous sûr que c'est correcte pour php 4.4.* de mettre
un retour par référence dans le constructeur ? (function & Aclass ())

Je n'utilise plus php4 donc je ne peux pas trop tester pour vous
aider...
Avatar
phep
ftc wrote:
[SNIP]
function & Aclass () {
$this->val = 0;
}



Si tu enleve le & pour le retour du constructeur, ça ne marche pas mieux ?

Le constructeur ne retourne rien ( return void ), il ne sert qu'à
initialiser tes variables, on n'est pas en Python, c'est l'opérateur new
qui se charge de la réservation en mémoire et qui retourne le pointeur
de celle-ci, pas le constructeur.


Vi, vi, c'est bien ça. Les choses commencent à s'éclaircir mais une zone
d'ombre reste (bien qu'elle ne soit pas bloquante point de vue du code).
Je me suis rendu compte que j'utilisais cette écriture sur les
constructeurs parce que en général je tapais ainsi mes affectations
(presque inconsciemment, réminiscence de C++) :
$a =& new Aclass();

Je pense que ce réflexe était aussi motivé, d'une part par
l'observations de code tiers, d'autre part après la lecture de
language.references.whatdo.html, plus précisément de la note suivante :

8<--
<?php
$bar =& new fooclass();
$foo =& find_var($bar);
?>

Note: Not using the & operator causes a copy of the object to be
made. If you use $this in the class it will operate on the current
instance of the class. The assignment without & will copy the instance
(i.e. the object) and $this will operate on the copy, which is not
always what is desired. Usually you want to have a single instance to
work with, due to performance and memory consumption issues.
8<--

Je me rends compte aujourd'hui que je n'ai jamais _vraiment_ compris
(pas plus aujourd'hui d'ailleurs) ce paragraphe, dans son contexte en
tout cas. Je comprends bien l'application de cette note si l'on compare
'$b =& $a' et '$b = $a', '$a' étant une instance d'une classe
quelconque. Mais quelle sont les différences entre ces deux assignations :
$bar =& new fooclass();
et
$bar = new fooclass();

Dans plusieurs endroits la doc semble sous-entendre (bien qu'elle ne le
dise jamais clairement) que ce que retourne 'new' est une référence (que
retourner d'autre ?), l'utilisation de '=&' fait donc sens, mais
qu'est-ce qui se passe si l'on n'utilise que '=' ? PHP5 semble lever
toute ambiguité ("Since PHP 5, new return reference automatically so
using =& in this context is deprecated and produces E_STRICT level
message") et instaurer une logique d'écriture "à la java".

Quelques tests rapides avec php4.4 me donnent strictement les mêmes
résultats et les mêmes comportements avec '=&' et '='. Est-ce simplement
une redondance ou une imprécision du langage ou est-ce qu'il y a
là-dedans une fonctionnalité qui m'échappe ?

Merci en tous les cas à ftc et Reno !

phep


Avatar
Reno
$bar = new fooclass();

Pour php4, $bar est une copie de l'instance créé. Ca peut paraitre
abérent, mais c'est pourtant le cas. En interne, le moteur utilise la
technique du reference counting et il n'y a donc pas vraiment de perte
de performance.
Il faut donc préciser explicitement "=&" si on désire obtenir une
référence.

A partir de php5, l'opérateur new retourne toujours une référence.
Plus besoins de trimbaler les "&" que ce soit pour assigner ou pour
passer des objets par référence.
Avatar
phep
Reno wrote:
$bar = new fooclass();

Pour php4, $bar est une copie de l'instance créé. Ca peut paraitre
abérent, mais c'est pourtant le cas. En interne, le moteur utilise la
technique du reference counting et il n'y a donc pas vraiment de perte
de performance.
Il faut donc préciser explicitement "=&" si on désire obtenir une
référence.


Aargh, j'avais donc bien compris, malgré tout...

A partir de php5, l'opérateur new retourne toujours une référence.
Plus besoins de trimbaler les "&" que ce soit pour assigner ou pour
passer des objets par référence.


Oui, vu ça dans la doc.

Merci encore !

phep

Avatar
ftc
$bar = new fooclass();

Pour php4, $bar est une copie de l'instance créé. Ca peut paraitre
abérent, mais c'est pourtant le cas. En interne, le moteur utilise la
technique du reference counting et il n'y a donc pas vraiment de perte
de performance.
Il faut donc préciser explicitement "=&" si on désire obtenir une
référence.


D'ailleurs, il ne faut utiliser les références que quand on en a
réllement besoin. Il vaut mieux laisser PHP faire ses copies,la plupart
du temps c'est plus efficace que le passage de références.