25/10/10

Grandes novedades en la próxima versión de PHP

Hoy me ha dado por mirar el changelog del trunk de PHP. Y he visto cosas la mar de interesantes.
Por lo pronto he visto bastantes mejoras de rendimiento y de uso de memoria. Algo que siempre se agradece. Y cosas que pensaba que ya debería estar haciendo que no hacía. ¡Bien!
Correcciones típicas y las correspondientes actualizaciones de sqlite y pcre y otras librerías.
Pero lo que me ha llamado la atención, son cosas que PHP estaba pidiendo a gritos desde hace tiempo:

- Added support for Traits. (Stefan)
- Added array dereferencing support. (Felipe)
- Added scalar typehints to the parser and the reflection API. (Ilia, Derick)


Y además las tres de golpe. Voy a explicar lo que hace cada una.


TRAITS en PHP:


Los traits ha sido un RFC que lleva bastante tiempo en el wiki de PHP (http://wiki.php.net/rfc/horizontalreuse).
Es algo que otros lenguajes como D soportan desde hace muchísimo tiempo (mediante templates y mixings) y que otros lenguajes como C++ soportan de forma parcial (mediante herencia múltiple). No sé de qué forma se han acabado implementado, pero ahí están.

La idea es que permitirá definir bloques de código reutilizables en distintas clases sin las limitaciones de la herencia simple y sin los problemas de la herencia múltiple.

El uso que le daré yo nada más empezar será el que hacía ahora con herencia simple a lo cutre y que es extender la funcionalidad del __get y el __set en algunos casos:


class BaseObject {
public $__cache = array();


public function __get($name) {
if (isset($this->__cache[$name])) return $this->__cache[$name];


if (method_exists($this, $method = "__get_cached_{$name}")) {
@$this->__cache[$name] = $v = $this->$method();
return $v;
} else if (method_exists($this, $method = "__get_{$name}")) {
return $this->$method();
} else if (method_exists($this, $method = "$name")) {
return $this->$method();
}
}


public function __sleep() {
$keys = array();
foreach ($this as $k => $v) if ($k != '__cache') $keys[] = $k;
return $keys;
}


public function __isset($name) {
return isset($this->__cache[$name]) || method_exists($this, "__get_{$name}") || method_exists($this, "__get_cached_{$name}");
}


public function __toString() {
return sprintf('%s object', get_called_class());
}
}




Array dereferencing en PHP:

PHP ha tenido de siempre la limitación de únicamente poder acceder a elementos de un array haciendo uso de una variable.
Es decir, se podía hacer:
$a[0]
pero no se podía hacer
a()[0]
Había que hacer:
$b = a(); $b[0]

Esta limitación venía impuesta por la dificultad de implementar la característica en una máquina virtual que no funcionaba con pila (donde es muy muy sencillo hacer esto) sino con variables locales (entiendo que es por eso).

Parece que ahora se ha eliminado dicha limitación. No sé hasta qué punto. Todavía tengo que ver si se podrán hacer este tipo de cosas:

echo (array('a' => 1) + array('a' => 2))['a'];
<=>
echo (($z = (array('a' => 1) + array('a' => 2))) || 1) ? $z['a'] : null;

Por cierto, ¿sabéis qué devolvería eso? :P

Scalar typehints en PHP:

A partir de PHP 5, PHP soporta typehinting en los parámetros de las funciones y métodos. Y solo ahí. No soporta typehinting en los campos de los objetos por ejemplo (Lo que sería de gran ayuda). Tampoco soporta typehinting en el valor de retorno de las funciones.
El typehinting no suele ser muy útil en lenguajes de tipado dinámico, aunque puede ayudar a mejorar el optimizador y a evitar errores. El problema del uso de typehinting en lenguajes de tipado dinámico es que eliminan la flexibilidad de usar una función para diferentes propósitos. En lenguajes compilados se suele soportar el uso de un mismo nombre para una función con diferentes prototipos. En lenguajes no compilados esto no es posible haciéndolo de una forma óptima. Así que o se opta por comprobar el tipo de los parámetros, o por usar diferentes nombres para funciones con diferentes tipos de parámetros.

La limitación actual del typehinting de PHP, además de ser el ámbito de los parámetros de las funciones, es el de los tipos que podemos utilizar. Únicamente se soportaban nombres de clases. Ahora se van a soportar tipos escalares y entiendo que también array.

function fname(int $a, string $b, float $c, array $d)