PHP: Grandes novedades en la versión de PHP 5.4

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.

Leer más...

PHP: Desempaquetar una cadena con un listado de subcadenas

Esto puede ser útil por ejemplo para extraer los elementos de un enum en PHP usando un SHOW COLUMNS. En ese caso el decode_list_json podría ser el más apropiado ya que, generalmente, los elementos de un enum o un set no contendrán comillas.

// Lenta pero segura y sin efectos colaterales (en principio).  
function decode_list_preg($list_string) {  
    preg_match_all("@(?:,|^)('|\")((?:\\\\'|\\\\\\\\|[^'])*)\\1@Umsi", $list_string, $matches);  
    return array_map('stripslashes', $matches[2]);  
}  
// Rápida pero insegura con posibilidad de inyección de código con una entrada inválida.  
function decode_list_eval($list_string) {  
    eval('$list = array(' . $list_string . ');');  
    return $list;  
}  
// Rápida pero con efectos colaterales: cambia comillas simples por dobles para que sea un json válido.  
function decode_list_json($list_string) {  
    return json_decode('[' . str_replace("'", '"', $list_string) . ']');  
}  
// Lenta y segura usando el tokenizer de php.  
function decode_list_php($list_string) {  
    $tokens = array_slice(token_get_all('<?php ' . $list_string), 1);  
    $expect_comma = false;  
    $list = array();  
    //print_r($tokens);  
    foreach ($tokens as $token) {  
        if (!is_array($token)) $token = array(0, $token, -1);  
        if ($expect_comma) {  
            if ($token[1] !== ',') throw(new Exception("Invalid input (" . token_name($token[0]) . ")"));  
        } else {  
            if ($token[0] !== T_CONSTANT_ENCAPSED_STRING) throw(new Exception("Invalid input (" . token_name($token[0]) . ")"));  
            $list[] = stripslashes(substr($token[1], 1, -1));  
        }  
        $expect_comma = !$expect_comma;  
    }  
    return $list;  
}  
function util_rand_string($len) {  
    $str = '';  
    while ($len-- > 0) $str .= chr(mt_rand(0x20, 0x7e));  
    return $str;  
}  

$str_list = array();  
for ($n = 0, $len = mt_rand(1000, 2000); $n < $len; $n++) $str_list[] = var_export(util_rand_string(mt_rand(40, 80)), true);  
$list_string = implode(',', $str_list);  

//echo $list_string; exit;  

//$list_string = "'ho\'la',\"esto\",'es','una','prueba'";  

//print_r(decode_list_preg($list_string));  
//print_r(decode_list_eval($list_string));  
//print_r(decode_list_json($list_string));  
print_r(decode_list_php($list_string));  

Leer más...

PHP: Referencias en PHP y funciones útiles auxiliares

Uno de los aspectos interesantes de PHP es la gestión de referencias.
Como es bien sabido en PHP, intentar acceder a una variable o una clave en un array que no existe produce un notice y generalmente hay que estar tratando continuamente con isset y con bastantes código redundante.
El caso es que PHP permite crear una referencia a una variable inexistente para crearla a posteriori. Es como crear un slot de la variable en potencia y asignarlo si así se decide. También permite comprobar la existencia de la variable referenciada usando la variable de referencia.

Es bastante típico en PHP querer obtener el valor de una variable si existe, o un valor por defecto en el caso de que no exista. Ejemplo:

$page = isset($_GET['page']) ? $_GET['page'] : 1;  

Leer más...

PHP: Trabajando con objetos en base de datos con PDO y Mongo + Demo con Twig

Trabajar con resultados de bases de datos como si fuesen objetos permite trabajar de una forma muy cómoda e intuitiva. PDO tiene soporte nativo para devolver instancias de una clase en vez de arrays. Pero con MongoDB tampoco es mucho más complicado conseguir un iterador de objetos de una clase determinada.
Aquí coloco un ejemplo con PDO, Mongo y Twig. Con twig se ve claramente la comodidad de tener objetos con los que poder llamar métodos que produzcan datos derivados sin tenerlos que generar explícitamente (on demand).
He creado una clase derivada de IteratorIterator y un método estático usando late static binding para poder castear un array/objeto a una nueva instancia de una clase especificada.

class CastModelIteratorIterator extends IteratorIterator {  
    public $class;  

    function current() {  
        return Model::cast(parent::current(), $this->class);  
    }  
}  

class Model {  
    static public function cast($array, $class = null) {  
        $object = ($class === null) ? (new static) : (new $class);  
        foreach ($array as $k => $v) $object->$k = $v;  
        return $object;  
    }  

    static public function castIterator($iterator) {  
        $iterator = new CastModelIteratorIterator($iterator);  
        $iterator->class = get_called_class();  
        return $iterator;  
    }  
}  

class TestModel extends Model {  
    public $name, $pass;  

    function __construct($name = 'test') {  
        $this->name = $name;  
        $this->pass = self::hashPassword('test');  
    }  

    static function hashPassword($password) {  
        return md5("{$password}*");  
    }  

    function checkPassword($password) {  
        return $this->pass == self::hashPassword($password);  
    }  

    function url() {  
        return sprintf('/user/%s', urlencode($this->name));  
    }  
}  

// Sqlite PDO Test  
{  
    $db = new PDO('sqlite::memory:');  
    $db->query('CREATE TABLE TestModel (name, pass);');  
    $t = $db->prepare('INSERT INTO TestModel (name, pass) VALUES (?, ?);');  
    $model = new TestModel('Test');  
    $t->execute(array($model->name, $model->pass));  
    foreach ($db->query('SELECT * FROM TestModel;', PDO::FETCH_CLASS, 'TestModel') as $e) {  
        printf("%s\n", $e->url());  
    }  
}  

// Mongo Test  
{  
    $mongo = new Mongo(); // connect  
    $db = $mongo->demo;  
    $collection = $db->TestModel;  
    $collection->remove();  
    $collection->insert(new TestModel('Test'));  

    foreach (TestModel::castIterator($collection->find()) as $e) {  
        printf("%s\n", $e->url());  
    }  
}  

// Twig  
{  
    require_once(__DIR__ . '/Twig/Autoloader.php');  
    Twig_Autoloader::register();  
    $collection->insert(new TestModel('Demo'));  

    $twig = new Twig_Environment(new Twig_Loader_String(), array(  
        'debug'       => true,  
        'auto_reload' => true,  
    ));  

    $twig->loadTemplate('  
        <ul>  
        {% for item in list %}  
            <li><a href="{{ item.url }}">User {{ item.name }}</a></li>  
        {% endfor %}  
        </ul>  
    ')->display(array(  
        'list' => TestModel::castIterator($collection->find()),  
    ));  
}  

Leer más...

PHP: Trabajando con archivos binarios en PHP

Aunque PHP no es un lenguaje de programación muy adecuado para trabajar con archivos binarios, en determinadas circunstancias puede ser de utilidad. Y explicaré aquí algunos detalles a tener en cuenta al trabajar con archivos binarios y técnicas para hacerlo con sencillez.

Leer más...

Suscribirse via RSS