Novedades PHP 5.3 (I) – Late static bindings

Me aventuro a iniciar esta ronda de posts sobre las novedades de PHP 5.3 ya que por un lado es bastante complicado encontrar información en castellano al respecto y por otro sigue existiendo la idea generalizada de que PHP 5.3 no tiene nada nuevo. Además, pueden aparecer problemas por el E_DEPRECATED con webs antiguas o programadas a la vieja usanza y por ello en muchos sitios todavía no se usa pese a ser plenamente funcional y lleno de mejoras.

Espero pues aportar mi granito de arena en la progresiva migración a esta nueva versión y a que de una vez por todas PHP se quite esa injusta etiqueta de “lenguaje scripting para malos programadores” y empecemos a hacer las cosas un poquito mejor.

Comezaremos con un ejemplo sencillito de los late static bindings y para qué nos pueden servir a la hora de desarrollar aplicaciones donde tenemos mezcladas herencia de clases y métodos static en las mismas.

La verdad es que últimamente se abusa de static ya que la sintaxis es más corta, es bastante habitual tener muchas clases con una sola instancia en la aplicación (como pueden ser los modelos) y da pereza hacer Singletons y escribir Clase::getInstance para todo. En la última PHP Conference hubo muchas ponencias acerca de este tema, del abuso de la palabra static en PHP, de la dificultad o directamente imposibilidad de realizar testeos unitarios y, por qué no recordarlo, algo peor rendimiento que en métodos de un objeto instanciado.

Para ilustrar nuestro ejemplo vamos a suponer una aplicación donde tenemos un currencyHelper para formatear un valor decimal (que podría ser el precio de un artículo) y que realiza las acciones de añadir los impuestos del país y la divisa para presentarlo directamente en HTML. Es un ejemplo muy básico pero en el cual podemos tener problemas si no lo hacemos bien.

Tendremos pues una clase currencyHelper que podría ser el default de nuestra aplicación, añadiendo el IVA (18%) que estará como propiedad de la clase, la moneda €, definida de igual manera y una clase currencyHelperBrazil que extiende a la anterior y solamente redefine las propiedades (IVA al 20%, que me lo invento y como moneda los Reales brasileños) pero no la lógica, ya que es exactamente igual.

Y veremos 3 ejemplos:

– Con métodos y variables no estáticos
– Con todo estático y sus problemas hasta PHP 5.2
– El uso de Late Static Bindings en PHP 5.3

Sin static

<?php
class currencyHelper {
    protected $tax = 18;
    protected $currency = '€';
 
    public function formatPrice($fPrice) {
        $fFinalPrice = $fPrice * (1 + $this->tax / 100);
        return number_format($fFinalPrice, 2) . $this->currency;
    }
}
 
class brazilCurrencyHelper extends currencyHelper {
    protected $tax = 20;
    protected $currency = 'R$';
}
$fPrice = 17.98;
$stdHelper = new currencyHelper();
echo 'Std Helper: ' .$stdHelper->formatPrice($fPrice).'<br />';
$brHelper = new brazilCurrencyHelper();
echo 'Br Helper: ' .$brHelper->formatPrice($fPrice).'<br />';
?>

Con esto, el primero echo nos da 21.22€ y el segundo echo 21.58R$, todo parece funcionar correctamente y si tuviéramos que hacer otros países sería sencillo al sólo tener que reescribir los parámetros tax y currency de la clase.

Todo static: Posibles problemas en PHP 5.2

<?php
class currencyHelper {
    protected static $tax = 18;
    protected static $currency = '€';
 
    public static function formatPrice($fPrice) {
        $fFinalPrice = $fPrice * (1 + self::$tax / 100);
        return number_format($fFinalPrice, 2) . self::$currency;
    }
}
 
class brazilCurrencyHelper extends currencyHelper {
    protected static $tax = 20;
    protected static $currency = 'R$';
}
$fPrice = 17.98;
echo 'Std Helper: ' .  currencyHelper::formatPrice($fPrice).'<br />';
echo 'Br Helper: ' .   brazilCurrencyHelper::formatPrice($fPrice).'<br />';
?>

¡Qué bien! Terminamos el código pensando que hemos entendido muy bien los static, nos hemos ahorrado unas líneas, parece que sabemos mucho y caramba, qué bonito queda el código con los ::.
Lamentablemente si probáis este código, veréis que los dos echo nos dan 21.22€.

¿Cómo es eso posible? ¿No nos hemos enterado bien del tema de herencia de clases? ¿PHP tiene bugs?
Pues… PHP tiene bugs pero el caso es que esto no es uno de ellos. En static las herencias no van igual que en los objetos instanciados. El self:: se resuelve en el momento del parseo (y no de la ejecución) y al parsear self:: está referido al primer currencyHelper. Por tanto, self es como poner directamente currencyHelper, aunque la función formatPrice, en realidad, se llama en el contexto de un brazilCurrencyHelper en el segundo echo.

Esta situación hasta PHP5.3 no tiene solución y deberíamos replantear el código y el diagrama de clases para solucionarlo.

La solución: PHP 5.3 y static:: en lugar de self::

<?php
class currencyHelper {
    protected static $tax = 18;
    protected static $currency = '€';
 
    public static function formatPrice($fPrice) {
        $fFinalPrice = $fPrice * (1 + static::$tax / 100);
        return number_format($fFinalPrice, 2) . static::$currency;
    }
}
 
class brazilCurrencyHelper extends currencyHelper {
    protected static $tax = 20;
    protected static $currency = 'R$';
}
$fPrice = 17.98;
echo 'Std Helper: ' .  currencyHelper::formatPrice($fPrice).'<br />';
echo 'Br Helper: ' .   brazilCurrencyHelper::formatPrice($fPrice).'<br />';
?>

Oleeeee! Ahora el código ya hace lo que esperábamos. static:: se resuelve en tiempo de ejecución (de ahí ese nombre tan molón de Late static bindings ya que la asignación de clases se hace más tarde 😉 ). Lo malo es que esto solamente funciona a partir de PHP 5.3. Aunque no sea exactamente lo mismo, podemos pensar que static:: se comporta de forma parecida al $this en los objetos instanciados.

¿Y el rendimiento de todo esto? ¿Es mejor todo static o todo instanciado? Lo veremos en próximos artículos.
¿Y qué pasa si solo tenemos 5.2 y nuestro jefe o nuestro hosting no se quieren actualizar? Insistid, sed pesados, pensad que mucho código está mal escrito y en PHP 5.3 dará errores de E_DEPRECATED así que eso os dará trabajo en estos tiempos de crisis.

Próximamente: Rendimiento de static vs objetos instanciados y más sobre PHP 5.3 y la certificación Zend!

You may also like...