Page 1 sur 1

Petit casse-tête PHP : foreach et référence

Posté : 21 juil. 2010, 09:47
par Hywan
Hey :),

Voici un petit casse-tête que j'ai eu récemment. Amusez-vous bien :
<?php
     
class C {
     
    protected $_foo = array('a', 'b', 'c', 'd');
     
    public function &getData ( ) {
     
        return $this->_foo;
    }
}
     
function f ( C $c ) {
     
    var_dump(current($c->getData()));
}
     
$c = new C();
$a = &$c->getData();
     
foreach($a as $i => $v) {
     
    var_dump($v);
    f($c);
     
    echo "\n";
}
     
/**
 * Will output:
 *
 * string(1) "a"
 * string(1) "b"
 *
 * string(1) "b"
 * string(1) "c"
 *
 * string(1) "c"
 * string(1) "d"
 *
 * string(1) "d"
 * bool(false)
 */

Re: Petit casse-tête PHP : foreach et référence

Posté : 21 juil. 2010, 10:59
par stopher
on peu poster les commentaires sur le code ? savoir si on a bon ? :D

Re: Petit casse-tête PHP : foreach et référence

Posté : 21 juil. 2010, 11:18
par Hywan
Amuses-toi vas-y ;-).

Re: Petit casse-tête PHP : foreach et référence

Posté : 21 juil. 2010, 11:35
par stopher
Et voilà , mes explications sont en commentaires juste au dessus du foreach

Bon pas bon ? vous en pensez quoi ?

:D
<?php

class C {

    protected $_foo = array('a', 'b', 'c', 'd');

    //retourne la reference du tableau $_foo
    public function &getData ( ) {

        return $this->_foo;
    }
}


//fonction f qui prend en paramétre l'instance de C
function f ( C $c ) {

    //envoi au buffer de sortie la valeur de la valeur position courante
    var_dump(current($c->getData()));
}


//instancie "C"
$c = new C();

//recupére une reference de $c->_foo
$a = &$c->getData();


// à chaque boucle , $v récupère la "valeure" ENSUITE , la position courante avance car elle est modifiable car nous avons la référence , on exécute le code dans les accolades
//   foreach défini $v avec la valeure courante , puis avance la reference interne au tableau 
//   d'ou le décalage entre la valeur récupérée et la valeur renvoyé par l'appel de current()  dand la boucle foreach


foreach($a as $i => $v) {

    //boucle 1 : affichage de la valeur de $v = 'a'
    //boucle 2 : affichage de la valeur de $v = 'b'
    //boucle 3 : affichage de la valeur de $v = 'c'
    //boucle 4 : affichage de la valeur de $v = 'd'
    var_dump($v);

    //boucle 1 : affichage position courante dans le tableau : 'b'
    //boucle 2 : affichage position courante dans le tableau : 'c'
    //boucle 3 : affichage position courante dans le tableau : 'd'
    //boucle 4 : affichage position courante dans le tableau : 'false' car fin de tableau
    f($c);

    echo "\n";
}

/**
 * Will output:
 *
 * string(1) "a"
 * string(1) "b"
 *
 * string(1) "b"
 * string(1) "c"
 *
 * string(1) "c"
 * string(1) "d"
 *
 * string(1) "d"
 * bool(false)
 */



//le code peut se réduire ainsi :

$test = array('a','b','c','d');

foreach( $test as &$val ){

    var_dump($val);
    echo "\n";
    var_dump(current($test));
    echo "\n";
}

Re: Petit casse-tête PHP : foreach et référence

Posté : 21 juil. 2010, 11:39
par stealth35
la question que le me pose moi c'est :

pourquoi quand on rentre dans le foreach la position est a 1 et non pas a 0
$a = array('a', 'b', 'c', 'd');

foreach($a as $v)
{
   var_dump(key($a));
}
/*
int 1
int 1
int 1
int 1
*/
du-coup en référence il part de 1
$a = array('a', 'b', 'c', 'd');
$b = &$a;
foreach($b as $v)
{
   var_dump(key($b));
}

/*
int 1
int 2
int 3
null
*/

par contre en copie il par bien de 0
$a = array('a', 'b', 'c', 'd');
$b = $a;
foreach($b as $v)
{
   var_dump(key($b));
}
/*
int 0
int 0
int 0
int 0
*/
EDIT : encore plus drôle, rien que le fait de le copié met le pointeur a 0;
$a = array('a', 'b', 'c', 'd');
$b = $a;

foreach($a as $v)
{
   var_dump(key($a));
}
/*
int 0
int 0
int 0
int 0
*/
EDIT 2 : le simple fait de faire un foreach redéfinie la dernière variable du tableau
$a = array('a', 'b', 'c', 'd');

foreach($a as $v)
{

}

debug_zval_dump($a);
/*
array(4) refcount(2){
  [0]=>
  string(1) "a" refcount(1)
  [1]=>
  string(1) "b" refcount(1)
  [2]=>
  string(1) "c" refcount(1)
  [3]=>
  string(1) "d" refcount(2)
}
*/