Creando un framework con PHP ( II ) : Introspección

A la hora de usar frameworks los programadores esperan cierta magia, que las cosas funcionen sin tener que preocuparse por ciertos detalles nimios, y eso en gran parte se consigue gracias a los métodos de introspección (o reflexión, aunque prefiero el primer término), que en este caso nos son brindados por PHP.

Se podría decir que en PHP hay dos caminos para usar la introspección, el uso de ciertas funciones que aparecieron a partir de la versión 4 de PHP (siguiendo el paradigma de la programación estructurada) , o bien el uso de ciertas clases (y sus respectivos objetos, obviamente) que hicieron su aparición estelar con PHP 5.

Por diversos motivos, en el framework que estoy desarrollando he escogido el segundo camino (en realidad un mix, pero predomina la segunda opción), pero aquí comentaré los dos.

Introspección al estilo de PHP 4

Los primeros métodos que deberíamos conocer son los que nos permiten saber qué clases hay declaradas, o si una determinada clase está cargada ya en el contexto en el que nos encontramos, tenemos estos métodos:

  • get_declared_classes () : retorna una lista que contiene los nombres de todas las clases declaradas.
  • class_exists ($nombre_clase): retorna verdadero o falso en función de si la clase pasada como parametro ha sido declarada o no. Como parámetro podemos pasar una cadena, o la propia clase si ya la conocemos. Ejemplo:
    1
    2
    3
    4
    
    class Prueba {
    }
    class_exists ('Prueba'); // Retorna verdadero
    class_exists (Prueba);   // También retorna verdadero

Una vez sabemos qué clases hay declaradas, también podríamos querer cómo son éstas por dentro, tenemos los siguientes métodos (creo que esta lista no es exhaustiva):

  • get_class_methods ($nombre_clase) : Nos devuelve una lista con los nombres de todos los métodos de la clase
  • get_class_vars ($nombre_clase) : Nos devuelve una lista asociativa con los nombres de las propiedades como claves, y los respectivos valores de las propiedades como elementos de la lista.
  • property_exists ($nombre_clase, $nombre_propiedad) : Nos indica si la clase tiene la propiedad que buscamos
  • get_parent_class ($nombre_clase) : Nos devuelve el nombre de la clase padre (si no hay clase padre, entonces devuelve una cadena vacía). También podemos usar objetos en este caso, no solo clases.
  • is_subclass_of ($nombre_clase, $nombre_clase_padre) : Nos indica si la clase hereda de la clase padre, también podemos usar objetos en este caso, no solo clases.

Podemos hacer prácticamente lo mismo, pero con objetos:

  • get_object_vars ($objeto) : Se parece mucho a get_class_vars , pero se diferencia en que los valores de las propiedades de los objetos pueden variar en tiempo de ejecución, y además se pueden añadir propiedades también en tiempo de ejecución.
  • method_exists ($objeto, $nombre_metodo) : Nos indica si el objeto tiene o no un método concreto

Y aquí nos quedamos, más o menos eso es todo. Antes había ciertas funciones que completaban la parrilla y eran bastante útiles, que servían, básicamente, para hacer uso de callbacks o ejecutar métodos de forma "dinámica", pero fueron marcadas como obsoletas a partir de la versión 4.1 de PHP, por lo que no las comentaré.  El hecho de que "falten" ciertas funciones nos podría obligar a agudizar el ingenio.. o a abusar de funciones como eval , pero por suerte, en PHP 5 se introdujo una nueva forma de trabajar.

Introspección al estilo PHP 5 (clase Reflection)

En PHP 5 se introdujeron diversas mejoras, entre otras el tratamiento de errores se ha orientado más hacia el manejo de excepciones con bloques try-catch , y se han creado más clases que permiten trabajar de una forma mucho más orientada al paradigma de Orientación a Objetos, aumentando en cierta medida la cohesión del código resultante.

Una de las clases que fueron introducidas en esta versión fue la clase Reflection (y algunas otras que derivan de ésta). Todas ellas, cuando se produce algún error, lo que hacen es lanzar una excepción, simplificando la gestión y evitando que tengamos que interpretar el significado de los valores de retorno. Estas son las clases de las que hablaremos (un poco por encima):

Para hacer introspección o reflexión lo que debemos es construir un objeto a partir de una de estas clases, usando como parámetro en el constructor aquello que queramos "ver por dentro". Por ejemplo:

1
$metainformacion_clase = new ReflectionClass ("nombre_de_la_clase");

a partir de aquí contamos con un montón de métodos (muchos más que los que había disponibles en PHP 4) que nos servirán para conocer todo cuanto queramos sobre la susodicha clase. Podéis ver los métodos disponibles en los enlaces que he hecho para cada una de ellas. Es interesante comentar que, algunos métodos devuelven objetos que a su vez también sirven para aplicar introspección, como getMethod ($name) o getProperty ($name) , que devuelven sendos ReflectionMethod i ReflectionProperty.

Uno de los métodos más interesantes de ReflectionMethod es el método invoke ($object, $params) , al cual le podemos pasar un objeto para el que queremos que se ejecute el método reflejado con los parámetros indicados. Esto sería "equivalente" a hacer la llamada (atentos, el ==== no es código, lo uso para indicar una cierta equivalencia):

1
$metainformacion_metodo->invoke($object, $params); ====  $object->metodo_reflejado($params);

Con ésto no es que se pueda zanjar el asunto de la introspección o reflexión, pero hay poco más interesante por decir, casi todo lo demás es simplemente conocer los nombres de las funciones y aplicarlas, así pues, deberíamos pensar en cómo toda esta parafernalia nos puede resultar útil para crear un framework con PHP. Lo veremos en el siguiente artículo, que este ya es suficientemente largo.

Creando un framework con PHP ( I )

Tengo una serie de artículos sobre optimización web a medio terminar, pero creí conveniente parar y tratar un poco la creación de frameworks para introducir posteriormente en la serie de artículos sobre optimización algo sobre cache.

Por eso mismo he estado desarrollando un mini framework en php (con la intención de hacerlo más ligero que, por ejemplo, cakephp) , y voy a ir contando mis experiencias sobre el asunto.

Introducción

El framework que por ahora mejor conozco es cakephp y ha influenciado bastante en mis decisiones a la hora de escoger un diseño u otro (tanto a la hora de escoger qué quería que tuviera, como a la hora de decidir qué me parece poco importante).

Sobretodo voy a listar qué es lo que no quiero para el framework que estoy haciendo (pues por querer, quiero demasiadas cosas y es una tontería escribirlas cuando ni tan siquiera sé si llegaré a implementarlas):

  • La excesiva "magia" que acababa implicando convenciones demasiado fuertes en cuanto a la nomenclatura, y una penalización en cuanto a rendimiento.
  • El acoplamiento excesivo entre modelos, controladores y vistas.
  • Que funcione sobre cualquier versión de PHP : utilizaré las características más novedosas que encuentre y no me preocuparé por la compatibilidad.
  • La forma en que está estructurado CakePHP provoca que no se puedan aprovechar correctamente las características del modelo de orientación a objetos.
  • Quiero evitar el abuso que se hace de los helpers, a veces me asusto cuando leo ciertos mensajes en la lista de correo de CakePHP española pidiendo ayuda sobre como crear simples enlaces o_o .

Pondré algunos detalles que sí quiero implementar, pero pocos:

  • Un mejor modelo de seguridad.
  • Un mejor modelo de control de errores.
  • Facilitar el unit testing.

Pasando a la acción

Todos los que hemos usado algún tipo de framework sabemos ya que éstos nos pueden llegar a facilitar mucho la vida a la hora de programar, pero lo que no todos saben es cuanta complejidad hay detrás de esas maravillas que nos simplifican el trabajo.

Empezemos por la parte "facil", la creación de rutas bonitas para las subsecciones de nuestra página. Esta parte solo funcionará sobre Apache o servidores que utilicen ficheros de configuración compatibles, y que tengan el módulo mod_rewrite activado. Escribimos las siguientes líneas en el fichero .htaccess :

1
2
3
4
5
6
<IfModule mod_rewrite.c>
 RewriteEngine On
 RewriteCond %{REQUEST_FILENAME} !-d
 RewriteCond %{REQUEST_FILENAME} !-f
 RewriteRule ^(.*)$ index.php?url=$1 [QSA,L]
</IfModule>

Lo que hacen estas líneas es redireccionar cualquier petición de la forma http://www.tuweb.com/seccion/subseccion/blabla a una petición de la forma http://www.tuweb.com/index.php?url=seccion/subseccion/blabla . Esto nos servirá para poder crear código que nos lleve a una sección u otra dependiendo del valor de la variable url, encapsulando en diferentes archivos el código necesario para gestionar cada sección.

Si queremos realizar una redirección similar en el servidor Cherokee debemos hacerlo desde el panel de administración (desgraciadamente Cherokee, aun siendo mucho más ligero que Apache y con un modo de configuración sorprendentemente fácil, no permite gestionar tan fácilmente las configuraciones en hostings compartidos).

En el siguiente artículo trataré el asunto de la introspección (o reflexión). Para los curiosos, podéis descargar e ir mirando el código del framework que estoy escribiendo en http://gitorious.org/pajarillo/ .