PHP 5.4.1 liberado

Hace no mucho que está disponible la primera revisión de php 5.4 y es agradable ver como el lenguaje va madurando y mejorando con características interesantes.

En el siguiente post en genbetadev que explica bastante bien los principales cambios de la versión 5.4, de los cuales me quedo con la eliminación de SafeMode y la introducción de traits.

SafeMode es una característica del pasado oscuro de php, que pretendía capar ciertas características potencialmente peligrosas de php para ejecutar código potencialmente inseguro o mal escrito, lo cual me recuerda a una frase que dijo una vez un ponente de una conferencia de seguridad a la que asistí en la que bromeaba con que las siglas PHP podrían significar “Podríais Hacerlo Peor”, por lo habitual que han sido ciertos tipos de fallos de seguridad en sitios desarrollados en PHP.

Pero el tiempo ha pasado y hoy día disponemos de librerías, frameworks y aplicaciones bien desarrolladas que permiten construir sitios web seguros de manera rápida y eficaz, por lo que no echaremos al SafeMode de menos.

El segundo tema interesante son los traits. Desde el punto de vista de la POO esto permite tener un mecanismo para compartir código entre clases que no heredan de la misma clase base, por tanto es un mecanismo parecido a la herencia múltiple pero mucho más simple ya que no obliga a la clase que lo usa a entrar en una determinada jerarquía de herencia. Actualmente ni Java, ni C# implementan herencia múltiple aunque C++ y Python si.

Es por esto que al no haber una jerarquía se habla de una composición horizontal de comportamiento, ya que un mismo trait podríamos aplicarlo a cualquier clase independientemente de que clase sea la clase padre o que interfaces implemente, mientras que la herencia obligaría a que el código a reusar esté en la clase padre (o la clase padre de la clase padre …).

El primer ejemplo de uso de esto es la implementación del patrón Singleton, el cual se implementa en PHP con un atributo estático y un método público estático:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class FeatureProvider {
private static $_instance = null;
public static function getInstance() {
if (self::$_instance === null) {
self::$_instance = new FeatureProvider();
}
return self::$_instance;
}
private function __construct() {}
public function __clone() {
throw new Exception("Can't clone a Singleton!");
}
public function __wakeup() {
throw new Exception("Can't unserialize a Singleton!");
}
}

Es una implementación corta, pero muy repetitiva. Hasta ahora no había manera de refactorizar este código y aprovecharlo, pero gracias a los traits es posible encapsular toda la implementación del patrón y compartirla entre varias clases sin mayores complicaciones:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
trait Singleton {
private static $_instance = null;
public static function getInstance() {
if (self::$_instance === null) {
self::$_instance = new self();
}
return self::$_instance;
}
public function __clone() {
throw new Exception("Can't clone a Singleton!");
}
public function __wakeup() {
throw new Exception("Can't unserialize a Singleton!");
}
}

Con lo que la clase inicial queda muchísimo más simple y elegante:

1
2
3
class FeatureProvider {
use Singleton;
}

Los traits abren todo un mundo de posibilidades para el desarrollo web en PHP ya que podemos comenzar a pensar en diseñar nuestras clases de manera modular para reaprovechar todos los traits que vayamos desarrollando y ahorrarnos así tiempo en desarrollo. Los traits que desarrollemos al no depender de la herencia pueden aplicarse no sólo a clases dentro de un mismo proyecto, sino también a clases entre proyectos realizados con diferentes frameworks que pueden heredar de sus propias clases base.

Por ejemplo, supongamos que queremos implementar una clase del modelo del modelo de datos para almacenar datos de usuario que llamaremos Person.

Nuestra capa de persistencia requiere que las clases del modelo implementen un campo para almacenar el id así que extraeremos el código común en un trait en lugar de hacer una clase base.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
trait IdentityField {
private $_id;
public function getId() {
return $this->_id;
}
public function setId($id) {
$this->_id = $id;
}
public function isSame($other) {
return !empty($this->_id) && $this->_id === $other->_id;
}
}

Por otro lado necesitamos almacenar el nombre y apellidos, que suelen ser comunes a todas las implementaciones entre proyectos y pondremos también en un trait:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
trait PersonalData {
private $_firstName;
public function setFirstName($firstName) {
$this->_firstName = $firstName;
}
public function getFirstName() {
return $this->_firstName;
}
private $_lastName;
public function setLastName($lastName) {
$this->_lastName = $lastName;
}
public function getLastName() {
return $this->_lastName;
}
public function getFullName() {
return $this->_firstName . ' ' . $this->_lastName;
}
}

Durante la depuración del código puede ser útil crear una cadena con los contenidos de los campos de una calse de modo que se puedan enviar a un log para ser examinados. Esto también podemos meterlo en un trait y aprovecharlo :

1
2
3
4
5
6
7
8
9
10
11
12
trait ObjectToString {
public function __toString() {
$fields = get_object_vars($this);
$result = '' . __CLASS__ . ':{';
foreach($fields as $name => $value) {
$result.= $name .'=' . $value . ',';
}
$result .= "}\n";
return $result;
}
}

Y para mayor comodidad en las pruebas definiremos una pequeña factoría para instanciar objetos de tipo Person usando el trait singleton definido anteriormente y de paso ir asignando un id consecutivo a las diferentes instancias:

1
2
3
4
5
6
7
8
9
class PersonFactory {
use Singleton;
private static $_lastId = 0;
public static function getPerson($firstName, $lastName) {
return new Person(++self::$_lastId, $firstName, $lastName);
}
}

Así finalmente, teniendo todos estos traits definidos, podemos definir la clase Person con unas pocas líneas de código:

1
2
3
4
5
6
7
8
9
10
11
12
class Person {
use IdentityField, PersonalData, Singleton, ObjectToString {
IdentityField::getId as getIdPerson;
IdentityField::setId as setIdPerson;
}
public function __construct($id = null, $firstName = null, $lastName = null) {
$this->setIdPerson($id);
$this->_firstName = $firstName;
$this->_lastName = $lastName;
}
}

En la clase anterior hemos redefinido los nombres de los métodos del trait IdentityField para reflejar el nombre de la clase y hacerlos más entendibles.
Ahora tan sólo hay que ejercitar un poco el código para probarlo:

1
/****** EJEMPLO *******/
1
2
3
4
5
6
7
8
9
$factory = PersonFactory::getInstance();
$person = $factory->getPerson("Mister1", 'X1');
$person2 = $factory->getPerson("Mister2", 'X2');
echo "Are the same {$person} and {$person2}? "
. ($person->isSame($person2) ? 'Yeah' : 'Nope') . "\n";
echo "Are the same {$person} and {$person}? "
. ($person->isSame($person) ? 'Yeah' : 'Nope') . "\n";

Referencias:

[1] https://www.genbetadev.com/php/php-5-4-a-fondo
[2] https://es.php.net/traits

Suscríbete a nuestro Boletín