Ver Mensaje Individual
  #10 (permalink)  
Antiguo 13/08/2014, 17:04
Avatar de hhs
hhs
Colaborador
 
Fecha de Ingreso: junio-2013
Ubicación: México
Mensajes: 2.995
Antigüedad: 11 años, 5 meses
Puntos: 379
Respuesta: Extraño comportamiento de foreach

Cita:
Eso no es tan asi (o no me doy cuenta al menos, espero me tengas paciencia), considera el siguiente ejemplo similar al primero:
Código PHP:
Ver original
  1. $test = new A();
  2.     foreach($test as $test)
  3.     {    
  4.         var_dump($test);
  5.     }
  6.     var_dump($test);
No se ve a simple vista, pero el $test que imprimes dentro del foreach ya no contiene el identificador del objeto por una simple y sencilla razón que estas pasando por alto. La variable $test que pasas al foreach y el que regresa están en el mismo ámbito debido a que el foreach no es una función $test no esta en un ámbito local, asi que la sobre escribe.
Entonces que es lo que pasa cuando iteras el objeto ?

Hasta aqui ya sabemos que $test se sobre escribe pero aun asi se itera el objeto y la razón ya te la respondiste.
Cita:
El objeto solo se destruye cuando no hay mas identificadores definidos de ese objeto.

¿Es posible que el foreach realice una copia de ese identificador y por lo tanto pueda seguir iterando aunque la variable original ya haya cambiado de valor?
Php trata de forma diferente a los objetos por que su "referencia" se hace por el identificador que pasa a otras variables, por lo cual el foreach a nivel interno tiene que mantenerlo para no crear efectos adversos ya que el objeto puede estar "referenciado" en otras variables.
Esto lo comprobamos de forma sencilla:
Código PHP:
Ver original
  1. class A implements Iterator
  2. {
  3.     private $datos = [1, 2, 3, 4, 5];
  4.  
  5.     public function rewind() { reset($this->datos); }
  6.     public function current() { return current($this->datos); }
  7.     public function key() { return key($this->datos); }
  8.     public function next() { return next($this->datos); }
  9.     public function valid() { return (key($this->datos) !== NULL && key($this->datos) !== FALSE); }
  10.     public function __destruct()
  11.     {
  12.         echo("destruido");
  13.     }
  14. }
  15.  
  16. $test = new A();
  17. $test1 = $test; //pasamos identificador
  18. foreach($test as $test)
  19. {
  20.     var_dump($test);
  21. }
  22. var_dump($test); //se sobre escribe; ahora vale 5
  23. var_dump($test1); //se mantiene el objeto
  24. echo 'Depues de esto se llama a __destruct <br>';
Despues de foreach el objeto original sigue con vida aun después de finalizar el ciclo y como se puede comprobar nuevamente $test se sobre escribe.
Si aun existe duda de si $test mantiene o no la referencia podemos probar algo: tu puedes llamar a __destruct si usas unset
Código PHP:
Ver original
  1. class A implements Iterator
  2. {
  3.     private $datos = [1, 2, 3, 4, 5];
  4.  
  5.     public function rewind() { reset($this->datos); }
  6.     public function current() { return current($this->datos); }
  7.     public function key() { return key($this->datos); }
  8.     public function next() { return next($this->datos); }
  9.     public function valid() { return (key($this->datos) !== NULL && key($this->datos) !== FALSE); }
  10.     public function __destruct()
  11.     {
  12.         echo("destruido");
  13.     }
  14. }
  15.  
  16. $test = new A();
  17. $test1 = $test; //pasamos identificador
  18. foreach($test as $test)
  19. {
  20.     var_dump($test);
  21.     unset($test);
  22. }
  23. var_dump($test); //se sobre escribe; ahora vale 5
  24. var_dump($test1); //se mantiene el objeto
  25. echo 'Depues de esto se llama a __destruct <br>';
Si el $test sigue con el identificador debiera de detener de forma prematura el foreach al destruir el objeto y por ende tendríamos un resultado inesperado en $test1. Pero no es asi si ejecutas esto obtienes lo siguiente:
Cita:
int 1
int 2
int 3
int 4
int 5


( ! ) Notice: Undefined variable: test in /home/hheredia/public_html/Curso/iteracion.php on line 56
Call Stack
# Time Memory Function Location
1 0.0022 236176 {main}( ) ../iteracion.php:0

null

object(A)[1]
private 'datos' =>
array (size=5)
0 => int 1
1 => int 2
2 => int 3
3 => int 4
4 => int 5

Depues de esto se llama a __destruct
destruido
$test1 sigue integro con su referencia al objeto como es de esperarse junto con el notice de variable indefinida de $test.
__________________
Saludos
About me
Laraveles
A class should have only one reason to change.