Archivo de 'php'

Variables en PHP – zval

Todos los lenguajes de programación permiten almacenar valores en memoria. Estos valores generalmente pueden ser de varios tipos -numéricos, caracteres, booleanos, etc. Muchos lenguajes son de tipado estático, es decir, cuando se inicializa una variable, se declara también el tipo de dato que se va a alojar en ella; es el caso de lenguajes como C o Java. Por contra, tenemos también el tipado dinámico, donde se decide en tiempo de ejecución qué clase de dato contendrá la variable; es el caso de PHP o JavaScript.

Tipos de datos en PHP

PHP en concreto soporta los siguientes tipos:

  • Escalares
    • booleano
    • integer
    • float (o double).
    • string
  • Compuestos
    • Array
    • Object
  • Especiales
    • resource
    • NULL

Si en un momento dado quisiéramos mostrar el tipo de una variable, podemos utilizar la función gettype(). Por ejemplo:

<?php
   $a = 'hola';
   $b = 42;
   $c = true;

   echo gettype($a); // Devuelve: string
   echo gettype($b); // Devuelve: integer
   echo gettype($c); // Devuelve: boolean
?>

Para comprobar si una variable es de un tipo determinado usaríamos is_type(), por ejemplo, is_integer($a).

Por otra parte, si lo que quisiéramos es mostrar tanto el tipo como el valor de una variable, utilizaríamos la función var_dump().

Además, al ser PHP un lenguaje de tipado dinámico, podemos forzar en tiempo de ejecución la conversión de un tipo a otro, técnica conocida como casting. Para ello podemos utilizar dos métodos: (1) O bien utilizado un cast, (2) o bien mediante la función settype().

A todo esto, hay que sumarle que PHP también puede cambiar el tipo de una variable en tiempo de ejecución dependiendo de su contexto. Por ejemplo, si a un string le sumamos un entero, PHP convertirá al string en entero en tiempo de ejecución. Esto puede desencadenar en comportamientos inesperados.

Como nota curiosa… Hace poco, en una entrevista de trabajo, me hicieron una pregunta. Si mal no recuerdo era algo tal que así: Si tenemos una variable a la que le asignamos la cadena “abc123″, y la comparamos con el operador == con el entero 0, ¿qué devuelve? La respuesta, aunque sorprendente, es que el resultado es true. Esto se debe a que PHP detecta que en este contexto la cadena debe comportarse como un entero, por lo que convierte “abc123″ en un número: 0. ¿Es 0 igual a 0? Sí, por tanto el resultado el true.

zval

Pero volvamos a las variables. ¿Cómo se almacenan internamente? Cada una de ellas se almacena en un contenedor llamado ‘zval’. Un contenedor zval tiene:

  1. Tipo de la variable. Como hemos visto, PHP tiene 8 tipos primitivos de datos.
  2. Valor de la variable. Esto es, el valor que le hemos asignado.
  3. refcount. Campo numérico que lleva la cuenta del número de variables que utilizan este contenedor.
  4. is_ref. Se trata de un booleano que indica si la variable está accedida por referencia.

Los dos primeros campos están bastante claros, de modo que centrémonos en los dos últimos.

refcount

Hemos visto que el campo refcount lleva la cuenta de las variables que utilizan un determinado zval. Veámoslo con un ejemplo.

<?php
   $a = "test";
   xdebug_debug_zval($a);
   // a: (refcount=1, is_ref=0)='test'
?>

Nota: Para poder consultar el zval de una variable, tan sólo debemos llamar a la función xdebug_debug_zval() de la extensión Xdebug para PHP.

Vemos que el campo refcount está a 1, es decir, sólo hay una variable que utilice este contenedor.

Veamos un ejemplo más avanzado:

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

   $b += 'ing';
   xdebug_debug_zval( 'a' );
   // a: (refcount=1, is_ref=0)='test'
?>

¿Qué ha pasado ahora? Vayamos paso a paso. En un primer lugar, creamos la variable $a a la que asignamos el valor ‘test’. Posteriormente creamos otra variable, $b, a la que le copiamos el mismo contenido que tiene $a. Al mostrar el zval de $a vemos que el valor refcount tiene ahora un 2. Esto es porque este contenedor zval está siendo utilizado por dos variables: $a y $b. Nótese que anteriormente he resaltado el verbo copiar, pues $b es una variable distinta a $a. Pero si son dos variables distintas, ¿por qué utilizan el mismo contenedor? Porque PHP es muy inteligente :) Al tener las dos variables el mismo valor, hace que utilicen el mismo contenedor para ahorrar espacio en memoria. ¿Pero qué ocurre si modificamos ahora una de ellas? En el siguiente paso vemos cómo modificamos $b, añadiéndole el sufijo ‘ing’. PHP, que como digo es muy inteligente, ve que ya no tienen el mismo valor $a y $b, por lo que, ahora sí, crea un nuevo espacio en memoria para $b. Por esa razón si ahora vemos el zval de $a comprobaremos que su refcount está a 1.

Este valor es muy útil para la gestión de memoria de PHP. Este lenguaje, a diferencia de otros, contiene un recolector de basura, un proceso que se encarga de liberar la memoria de las variables y objetos que no se van a volver a usar. De este modo, el propio lenguaje se encarga por nosotros de limpiar la memoria. Por supuesto, el recolector de basura liberará todos los contenedores zval cuyo refcount esté a 0, pues significa que no están siendo utilizados por ninguna variable.

En cualquier momento podemos, además, forzar la eliminación de una variable, llamando a la función unset(). Ésta lo que hace es decrementar el valo refcount. Y como hemos visto, si llega a 0, se eliminará por completo el zval correspondiente.

is_ref

Si alguna vez has programado en C, sabrás bien lo que son los punteros: Se trata de variables cuyo valor es una dirección de memoria física, de modo que únicamente apuntan al lugar donde se encuentra su valor. Esto es muy útil para, por ejemplo, hacer que varias variables sean en realidad la misma. O para pasar a una función una variable, y que la función pueda modificar el valor de ésta. Esto es posible en PHP utilizando referencias.

Nota: A partir de PHP5 cada vez que pasas un objeto a una función o método, éste se pasa siempre por referencia. Es decir, las modificaciones que se hagan de un objeto dentro de una función persistirán fuera de ésta.

Veamos dos ejemplos para comprender que son las variables por referencia:

<?php
   $a = 'hello';
   $b = $a;
   echo $b; // Muestra 'test';
   $b = 'ciao';
   echo $a; // Muestra 'test';
   echo $b; // Muestra 'ciao';
?>

Simplemente copiamos a $b el valor de $a. Posteriormente modificamos $b, y comprobamos cómo sólo ha cambiado éste, pero no $a. El siguiente ejemplo es prácticamente igual, salvo que en lugar de copiar $a a $b, copiamos por referencia:

<?php
   $a = 'hello';
   $b =& $a;
   echo $b; // Muestra 'test';
   $b = 'ciao';
   echo $a; // Muestra 'ciao';
   echo $b; // Muestra 'ciao';
?>

En este caso, al haber usado el operador =&, hemos hecho que $b sea en realidad la misma variable que $a. Y aquí es donde volvemos con zval. ¿Qué contiene ahora? Comprobémoslo:

a: (refcount=2, is_ref=1)='ciao'

¡Y ahí lo tenemos! Ahora el campo is_ref tiene valor true (1). Esto es porque, como hemos visto, este zval está siendo también apuntado por una variable por referencia. Además podemos comprobar cómo el refcount se ha incrementado a 2.

zval en tipos compuestos

Hasta ahora hemos visto ejemplos de zval en variables escalares. Vamos a ver un ejemplo de qué se almacena cuando es un tipo compuesto, como por ejemplo un array:

<?php
   $a = array('foo' => 'bar', 'gen' => 'oma');
   xdebug_debug_zval('a');
?>

Y esto es lo que obtenemos:

a: (refcount=1, is_ref=0)=array (
‘foo’ => (refcount=1, is_ref=0)=’bar’,
‘gen’ => (refcount=1, is_ref=0)=’oma’
)

¡Curioso! Un array lo que hace es crear una nueva tabla, distinta de la global, de valores zval. Pero la cosa se puede complicar bastante. Para terminar, vamos a ver el siguiente ejemplo extraído de la documentación de PHP:

<?php
   $a = array( 'one' );
   $a[] =& $a;
   xdebug_debug_zval( 'a' );
?>

Creamos un array con un solo elemento. Posteriormente creamos un segundo campo en el que asignamos una referencia circular al propio array. Obtenemos el siguiente resultado:

a: (refcount=2, is_ref=1)=array (
   0 => (refcount=1, is_ref=0)='one',
   1 => (refcount=2, is_ref=1)=...
)

Supongamos que ahora hacemos una llamada a unset($a). El resultado esperado sería que se eliminara por completo de memoria el zval anterior. ¿Pero es esto lo que realmente ocurre? Como explicamos anteriormente, mediante unset() lo que hacemos es decrementar el refcount. Pero en este caso, tras llamar a unset() quedaría el siguiente zval huérfano sin ser eliminado de memoria pues su refcount vale 1:

(refcount=1, is_ref=1)=array (
   0 => (refcount=1, is_ref=0)='one',
   1 => (refcount=1, is_ref=1)=...
)

No se elimina puesto que el item 1 del array sigue apuntando al propio array. Y al no haber ningún símbolo externo apuntando a este zval, no existe ningún mecanismo que nos permita liberar esta memoria, obteniendo un memory leak. Afortunadamente, desde PHP 5.3, esta memoria se libera automáticamente al finalizar la petición. Pero hasta entonces, sigue ocupando memoria, lo cual puede ser un problema en procesos de larga duración como por ejemplo un demonio.

Resumen

En este artículo hemos pasado de lo más elemental, hasta conocer cómo se almacenan las variables internamente en PHP, y sus mecanismos de control. Conocemos los 8 típos primitivos de PHP, y cómo trabajar entre ellos. Sabemos qué es el campo refcount, lo que son los valores por referencia, el campo is_ref, y cómo afectan éstos al recolector de basuras. Del mismo modo, hemos visto cómo hay situaciones que pueden escapar a nuestro control, donde la memoria no queda libre a pesar de haber finalizado una variable.

Para más información podéis consultar:

Crear un componente en Joomla! 1.5

Joomla! es, junto con WordPress, quizás el CMS más popular que existe. Lo que no mucha gente sabe, o tiene en cuenta, es que es además un completo Framework de desarrollo web. No en vano, nos proporciona muchas de las funcionalidades que esperamos de un Framework:

  • Arquitectura basada en Modelo-Vista-Controlador.
  • Seguridad, con un completo sistema de perfiles de acceso.
  • Acceso a base de datos.
  • Sistema de cachés.
  • Gestión de errores.
  • Administración de formularios.
  • Sesiones.
  • Plantillas.
  • Utilidades para fechas, criptografía, XML, ficheros…

Con todo esto, podemos crear diferentes extensiones de Joomla!:

  • Component. Son las extensiones más complejas. Se trata de aplicaciones completas que se ejecutan dentro del espacio de Joomla!. Tenemos muchas de serie, como por ejemplo el componente Content (/components/com_content/), que es el encargado de manejar los artículos, categorías y secciones.
  • Module. Se trata de pequeñas piezas de contenido que se muestran en la página. A menudo, dependen de un componente, como es el caso del módulo “latest news” (/modules/mod_latestnews/). Otro ejemplo de módulos, son los menús. En los módulos, además, se debe especificar su posición dentro de la página.
  • Plugins. Son bloques de código que se disparan ante determinados eventos. Antes de Joomla! 1.5 se los llamaba Mambots.
  • Templates. Plantillas que modifican el aspecto visual de la aplicación web.
  • Languages. Las extensiones de este tipo contienen ficheros que definen cadenas de texto traducidas para distintas partes de Joomla!.

A partir de Joomla 1.6, además, tendremos dos nuevos tipos de extensiones. Library, que se tratará de un bloque de código que contenta un conjunto de funciones relacionadas entre sí. Por ejemplo, para acceder a la API de Twitter. Y también se incluirá la extensión Packages.

Como vemos, la extensión más compleja y flexible son los components. Con ellos podremos crear, por ejemplo, una completa agenda de eventos o un sistema de ecommerce. Veamos cómo podemos crear un componente básico “Hola Mundo”. Vamos a utilizar la primera parte del ejemplo que viene en la documentación de Joomla!.

Modelo Vista Controlador

Lo primero es conocer el patrón Modelo-Vista-Controlador. Queda fuera del alcance de este artículo explicar en qué consiste, de modo que en resumen diremos que se trata de una arquitectura que permite dividir una aplicaciones en tres apartados:

  1. Controlador, que es el que gobierna el comportamiento principal, y el que responde las peticiones del usuario. En Joomla: JController.
  2. Vista. Se trata del código que se encarga de generar el contenido que se le devolverá al usuario. Generalmente en HTML. En Joomla: JView.
  3. Modelo. Es una abstracción de los datos que usa la aplicación. Si bien normalmente encapsulan bases de datos, también pueden utilizar otros origines de datos como un feed RSS. En Joomla: JModel.

Crear árbol de directorios

El primer paso consiste en crear el siguiente árbol de directorio y ficheros:

Además de los ficheros en blanco index.html (Para prevenir accesos a los directorios), vemos que tenemos 5 ficheros:

  1. /site/hello.php – Punto de entrara a nuestro componente
  2. /site/controller.php – Fichero que contiene nuestro controlador.
  3. /site/views/hello/view.html.php – Clase que extiende JView que se encarga de construir la vista
  4. /site/views/hello/tmpl/default.php – Plantilla
  5. /hello.xml – Fichero XML que indica a Joomla! cómo instalar el componente.

Crear el punto de entrada

Vamos a editar el fichero /site/hello.php y vamos a guardar en el lo siguiente:

/**
 * @package    Joomla.Tutorials
 * @subpackage Components
 * components/com_hello/hello.php
 * @link http://docs.joomla.org/Developing_a_Model-View-Controller_Component_-_Part_2
 * @license    GNU/GPL
 */

// No direct access
defined( '_JEXEC' ) or die( 'Restricted access' );

// Require the base controller

require_once( JPATH_COMPONENT.DS.'controller.php' );

// Require specific controller if requested
if ($controller = JRequest::getWord('controller')) {
	$path = JPATH_COMPONENT.DS.'controllers'.DS.$controller.'.php';
	if (file_exists($path)) {
		require_once $path;
	} else {
		$controller = '';
	}
}

// Create the controller
$classname	= 'HelloController'.$controller;
$controller	= new $classname();

// Perform the Request task
$controller->execute( JRequest::getVar( 'task' ) );

// Redirect if set by the controller
$controller->redirect();

¿Qué es lo que hemos hecho? Veámoslo paso a paso:

  1. En primer lugar comprobamos que estamos ejecutando éste código dentro de Joomla mirando si está definido _JEXEC. En caso contrario, finalizamos la aplicación.
  2. Cargamos con require_once nuestro controlador.
  3. El siguiente bloque de código comprueba si se necesitan más controladores. Por ahora no es necesario, pero lo mantenemos ahí para un futuro.
  4. Después, creamos el controlador. En este caso, siempre se creará un controlador de la clase HelloController(), dado que en este ejemplo, como vimos en el paso anterior, no hay más.
  5. Posteriormente indicamos al controlador que lleve a cabo la tarea solicitada. Por defecto, si no hay ninguna, la tarea es ‘display’.
  6. Finalmente, pasamos el control al controlador, para que, si fuera necesario, lleve a cabo una redirección.

Creamos el controlador

Nuestro controlador sólo hace una cosa: Mostrar el mensaje Hello World, de modo que no se hace necesaria ninguna manipulación de datos. Guardamos el siguiente código en /site/controller.php

 /**
 * @package    Joomla.Tutorials
 * @subpackage Components
 * @link http://docs.joomla.org/Developing_a_Model-View-Controller_Component_-_Part_1
 * @license    GNU/GPL
 */

// No direct access

defined( '_JEXEC' ) or die( 'Restricted access' );

jimport('joomla.application.component.controller');

/**
 * Hello World Component Controller
 *
 * @package    Joomla.Tutorials
 * @subpackage Components
 */
class HelloController extends JController
{
    /**
     * Method to display the view
     *
     * @access    public
     */
    function display()
    {
        parent::display();
    }

}

Como vemos, creamos una clase llamada HelloController que hereda a JController. Tan sólo define el método display(), pues es la tarea por defecto (Salvo que no se especifique otra). Este método tan sólo invoca al método JController::display(), que determina y carga la vista y plantilla a utilizar. En nuestro ejemplo, sólo tenemos una vista y una plantilla.

Crear la vista

La vista tan sólo carga datos y los guarda para usar en la plantilla. Para ello se usa el método assignRef(), donde el primer parámetro es el nombre de la variable en la plantilla, y el segundo la variable que se comparte.

Código de /site/views/hello/view.html.php:

/**
 * @package    Joomla.Tutorials
 * @subpackage Components
 * @link http://docs.joomla.org/Developing_a_Model-View-Controller_Component_-_Part_1
 * @license    GNU/GPL
*/

// no direct access

defined( '_JEXEC' ) or die( 'Restricted access' );

jimport( 'joomla.application.component.view');

/**
 * HTML View class for the HelloWorld Component
 *
 * @package    HelloWorld
 */

class HelloViewHello extends JView
{
    function display($tpl = null)
    {
        $greeting = "Hello World!";
        $this->assignRef( 'greeting', $greeting );

        parent::display($tpl);
    }
}

Y ahora definimos el código de la plantilla en /site/views/hello/tmpl/default.php:

<?php

// No direct access

defined('_JEXEC') or die('Restricted access'); ?>

<?php echo $this->greeting; ?>

Se trata de un fichero PHP convencional en el que tenemos accesibles todas las variables que hemos compartido desde la vista.

Juntándolo todo

Nos queda definir el fichero Manifiest que define el componente para poder instalarlo fácilmente (Sin tener que copiar los ficheros a mano y actualizar la base de datos nosotros). Contiene distinta información, como por ejemplo:

  • Descripción del componente.
  • Lista de ficheros que incluye.
  • Un fichero opcional PHP que realizar tareas de instalación.
  • Un fichero opcional SQL que se ejecuta al instalar o desinstalar


 Hello
 
 2007-02-22
 John Doe
 john.doe@example.org
 http://www.example.org
 Copyright Info
License Info
 
 1.01
 
 Description of the component ...

 
 
 
  controller.php
  hello.php
  index.html
  views/index.html
  views/hello/index.html
  views/hello/view.html.php
  views/hello/tmpl/default.php
  views/hello/tmpl/index.html
 

 
  
Hello World!

  
  
   hello.php
   index.html
  

 

Finalmente, guardamos en los ficheros index.html un contenido trivial, como por ejemplo:

<html><body bgcolor="#FFFFFF"></body></html>

Por último, comprimimos en un fichero ZIP el contenido de la carpeta com_hello, y ya podemos instalar nuestro componente desde el instalador de extensiones de Joomla.

Mostrando el componente

Ahora, para poder ver el resultado, debemos consultar el componente. Para ello, debemos acceder a la siguiente URL:

/index.php?option=com_hello&view=hello

Vemos que no se define ninguna tarea (Recordemos que por defecto se usa Display). En caso de querer ejecutar otra tarea, tendríamos que definir el parámetro GET task, por ejemplo para la tarea ‘save’:

/index.php?option=com_hello&view=hello&task=save

PHP 5.3.3 liberada

Hace ya un año que PHP lanzó la versión 5.3.0, la última rama de desarrollo de la versión 5 que adelanta muchas de las funcionalidades más esperadas, como namespaces o closures. Hoy se ha liberado la versión 5.3.3. Se ha centrado sobre todo en mejorar el rendimiento y la seguridad de la rama 5.3, con más de 100 errores corregidos, por lo que se recomienda encarecidamente actualizar a la nueva versión.

Entre las novedades, se destacan:

  • Actualizada la versión de sqlite a 3.6.23.1
  • Actualizada la versión de PCRE (Expresiones Regulares Compatibles con Perl) a la versión 8.02
  • Añadido FPM (Gestor de Procesos de FastCGI) SAPI. SAPI hace referencia a los módulos de PHP que hacen de interfaz al servidor web (Server Application Programming Interface)
  • Añadido soporte para stream filter de la extensión mcrypt.

¡A disfrutarlo!