Variables, referencias y ámbitos en PHP

En el número 8 de  The Original Hacker, incluyen un interesante artículo sobre la referencia de objetos en PHP. Cómo hace tiempo ya escribí una entrada sobre las variables en PHP, he pensado en repescarla y ampliarla un poco.

En PHP las variables son de hecho una  estructura llamada zval_struct”.

struct _zval_struct {
  zvalue_value value;
  zend_uint refcount;
  zend_uchar type; /* active type */
  zend_uchar is_ref;
};
  • tipo (el tipo de la variable)
  • valor (el valor que toma  la variable)
  • is_ref ( indica si la variable es una referencia a otro valor)
  • refcount (contador del número de símbolos que apuntan a este zval)

Se crea un contenedor zval cuando se crea una variable con un valor constante, como por ejemplo: $a = 5.
Instalando el módulo xdebug, podemos examinar los valores de zval_struct:

<?php 
   $a = 5;
   xdebug_debug_zval('a');

Obtenemos:

   a: (refcount=1, is_ref=0),int 5

refcount: 1 variable apunta al valor 5
is_ref: no hay ninguna referencia al valor 5.

<?php 
  $a = 5;
  $b = $a;
  xdebug_debug_zval('a');
a: (refcount=2, is_ref=0),int 5

No se duplica el valor, sino que aumentamos la referencia. Y si añadimos una referencia.

<?php 
   $a = 5;
   $b = $a;
   $c = &$a;
   xdebug_debug_zval('a');

Obtenemos :

a: (refcount=2, is_ref=1),int 5

De la misma forma, unset no barra resultados sino disminuye los contadores:

<?php
   $a = 5;
   $b = $a;
   $c = &$a;
   xdebug_debug_zval('a');
   unset($b);
   xdebug_debug_zval('a');
a: (refcount=1, is_ref=1),int 5

PHP no copia el valor del contenedor a menos que sea necesario:

<?php
   $a = 5;
   $b = $a;
   xdebug_debug_zval('a');
   xdebug_debug_zval('b');

a y b comparten el mismo contendor

a: (refcount=2, is_ref=0),int 5
b: (refcount=2, is_ref=0),int 5

En cambio :

<?php
   $a = 5;
   $b = $a;
   $b = 4;
   xdebug_debug_zval('a');
   xdebug_debug_zval('b');

a y b no ya comparten el mismo contenedor

a: (refcount=1, is_ref=0),int 5
b: (refcount=1, is_ref=0),int 4

Tablas de símbolos y ámbitos

¿Cómo comparten $a y $b un mismo zval?: Mediante tablas de símbolos. Una tabla de símbolos es un lista que asocia cada nombre de variable al zvals correspondiente. Al comienzo de los scripts se crea una tabla de síbolos muy conocida: $GLOBALS.

<?php
   $a = 5;
   var_dump($GLOBALS['a']);
   int 5

En PHP tenemos una tabla de símbolos por ámbito y en PHP el ámbito está unido a las funciones o métodos.

Cuando el refcount de un zval llega a 0, se elimina de la tabla de símbolos.