Utilizando las nuevas características del lenguaje PHP V5, la mantenibilidad y confiabilidad del código se pueden mejorar significativamente. Al leer este artículo, aprenderá cómo aprovechar estas nuevas características para migrar código desarrollado en PHP V4 a PHP V5.
PHP V5 ha realizado mejoras significativas basadas en PHP V4. Las nuevas características del lenguaje facilitan la creación de bibliotecas de clases confiables y su mantenimiento. Además, reescribir la biblioteca estándar ayudó a que PHP estuviera más en línea con sus compañeros web, como el lenguaje de programación Java™. Echemos un vistazo a algunas de las nuevas funciones orientadas a objetos de PHP y aprendamos cómo migrar el código PHP V4 existente a PHP V5.
Primero, veamos cómo las nuevas características del lenguaje y el creador de PHP han cambiado la forma en que se crean los objetos con PHP V4. La idea con V5 era crear un lenguaje industrial para el desarrollo de aplicaciones web. Eso significa comprender las limitaciones de PHP V4, luego extraer buenas arquitecturas de lenguaje conocidas de otros lenguajes (como Java, C#, C++, Ruby y Perl) e incorporarlas a PHP.
La primera y más importante característica nueva es la protección de acceso a métodos de clase y variables de instancia: las palabras clave pública, protegida y privada. Esta nueva característica permite a los diseñadores de clases mantener el control sobre las propiedades intrínsecas de una clase mientras les dice a los usuarios de la clase qué clases son accesibles y cuáles no.
En PHP V4, todo el código es público. En PHP V5, los diseñadores de clases pueden declarar qué código es visible para el mundo exterior (público) y qué código solo es visible dentro de la clase (privado) o solo para las subclases de la clase (protegido). Sin estos controles de acceso, el desarrollo de código en un equipo grande o la distribución de código como una biblioteca se ve obstaculizado porque es probable que los usuarios de esas clases utilicen métodos incorrectos o código de acceso que deberían ser variables miembro privadas.
Otra gran novedad es la interfaz de palabras clave y el resumen, que permiten la programación por contrato. La programación por contrato significa que una clase proporciona un contrato a otra clase; en otras palabras: "Esto es lo que voy a hacer y no necesitas saber cómo se hace". Todas las clases que implementan la interfaz cumplen con este contrato. Todos los usuarios de una interfaz aceptan utilizar únicamente los métodos especificados en la interfaz. La palabra clave abstract hace que trabajar con interfaces sea muy fácil, como explicaré más adelante.
Estas dos características clave (control de acceso y programación de contratos) permiten que grandes equipos de codificadores trabajen más fluidamente con grandes bases de código. Estas funciones también permiten que el IDE proporcione un conjunto más rico de funciones inteligentes en el lenguaje. Este artículo no solo aborda varios problemas de migración, sino que también dedica algo de tiempo a explicar cómo utilizar estas nuevas funciones importantes del lenguaje.
Control de acceso
Para demostrar las nuevas características del lenguaje, utilicé una clase llamada Configuración. Esta clase simple contiene elementos de configuración para la aplicación web, por ejemplo, la ruta al directorio de imágenes. Lo ideal sería que esta información residiera en un archivo o base de datos. El Listado 1 muestra una versión simplificada.
Listado 1. access.php4
<?php
configuración de clase
{
var $_items = matriz();
función Configuración() {
$this->_items[ 'imgpath' ] = 'imágenes';
}
función obtener ($ clave) {
devolver $this->_items[ $clave ];
}
}
$c = nueva Configuración();
echo( $c->get( 'imgpath' )."n" );
?>
Esta es una clase PHP V4 completamente ortodoxa. La variable miembro contiene la lista de elementos de configuración, el constructor carga los elementos y el método de acceso denominado get() devuelve el valor del elemento.
Después de ejecutar el script, aparecerá el siguiente código en la línea de comando:
%php access.php4
imágenes
%
¡muy bien! Este resultado significa que el código se ejecuta normalmente y el valor del elemento de configuración imgpath se establece y se lee normalmente.
El primer paso para convertir esta clase a PHP V5 es cambiar el nombre del constructor. En PHP V5, el método para inicializar un objeto (constructor) se llama __construct. Este pequeño cambio se muestra a continuación.
Listado 2. access1.php5
<?php
configuración de clase
{
var $_items = matriz();
función __construct() {
$this->_items[ 'imgpath' ] = 'imágenes';
}
función obtener ($ clave) {
devolver $this->_items[ $clave ];
}
}
$c = nueva Configuración();
echo( $c->get( 'imgpath' )."n" );
?>
Los cambios esta vez no son grandes. Acabo de pasar a la convención PHP V5. El siguiente paso es agregar control de acceso a la clase para garantizar que los usuarios de la clase no puedan leer ni escribir directamente la variable miembro $_items. Este cambio se muestra a continuación.
Listado 3. access2.php5
<?php
configuración de clase
{
privado $_items = matriz();
función pública __construct() {
$this->_items[ 'imgpath' ] = 'imágenes';
}
función pública obtener ($ clave) {
devolver $this->_items[ $clave ];
}
}
$c = nueva Configuración();
echo( $c->get( 'imgpath' )."n" );
?>
Si un usuario de este objeto accediera directamente a la matriz de elementos, se denegaría el acceso porque la matriz está marcada como privada. Afortunadamente, los usuarios han descubierto que el método get() proporciona los tan bienvenidos permisos de lectura.
Para ilustrar cómo utilizar permisos protegidos, necesito otra clase, que debe heredar de la clase Configuración. Llamé a esa clase DBConfiguration y supuse que la clase leería los valores de configuración de la base de datos. Esta configuración se muestra a continuación.
Listado 4. access3.php
<?php
configuración de clase
{
protegido $_items = matriz();
función pública __construct() {
$esto->cargar();
}
carga de función protegida() { }
función pública obtener ($ clave) {
devolver $this->_items[ $clave ];
}
}
clase DBConfiguration extiende la configuración
{
carga de función protegida() {
$this->_items[ 'imgpath' ] = 'imágenes';
}
}
$c = nueva configuración de base de datos();
echo( $c->get( 'imgpath' )."n" );
?>
Este listado muestra el uso correcto de la palabra clave protegida. La clase base define un método llamado load(). Las subclases de esta clase anularán el método load() para agregar datos a la tabla de elementos. El método load() es interno a la clase y sus subclases, por lo que el método no es visible para todos los consumidores externos. Si todas las palabras clave son privadas, el método load() no se puede anular.
Realmente no me gusta este diseño, pero lo elegí porque tenía que darle acceso a la clase DBConfiguration a la matriz de elementos. Me gustaría que la clase Configuración siga manteniendo la matriz de elementos en su totalidad, de modo que a medida que se agreguen otras subclases, esas clases no necesiten saber cómo mantener la matriz de elementos. Hice los siguientes cambios.
Listado 5. access4.php5
<?php
configuración de clase
{
privado $_items = matriz();
función pública __construct() {
$esto->cargar();
}
carga de función protegida() { }
función protegida agregar ($ clave, $ valor) {
$this->_items[ $clave ] = $valor;
}
función pública obtener ($ clave) {
devolver $this->_items[ $clave ];
}
}
clase DBConfiguration extiende la configuración
{
carga de función protegida() {
$this->add( 'imgpath', 'imágenes' );
}
}
$c = nueva configuración de base de datos();
echo( $c->get( 'imgpath' )."n" );
?>
Las matrices de elementos ahora pueden ser privadas porque las subclases usan el método add() protegido para agregar elementos de configuración a la lista. La clase Configuración puede cambiar la forma en que almacena y lee los elementos de configuración sin tener en cuenta sus subclases. Siempre que los métodos load() y add() se ejecuten de la misma manera, la creación de subclases no debería tener problemas.
Para mí, el control de acceso agregado es la razón principal para considerar pasar a PHP V5. ¿Es simplemente porque Grady Booch dijo que PHP V5 es uno de los cuatro principales lenguajes orientados a objetos? No, porque una vez acepté una tarea para mantener el código 100KLOC C++ en el que todos los métodos y miembros estaban definidos como públicos. Me tomó tres días limpiar estas definiciones y, en el proceso, reduje significativamente la cantidad de errores y mejoré la capacidad de mantenimiento. ¿Por qué? Porque sin control de acceso, es imposible saber cómo los objetos usan otros objetos y es imposible realizar cambios sin saber qué obstáculos superar. Con C++, al menos todavía tengo el compilador disponible. PHP no viene con compilador, por lo que este tipo de control de acceso se vuelve aún más importante.
Programación por contrato
La siguiente característica importante que se debe aprovechar al migrar de PHP V4 a PHP V5 es la compatibilidad con la programación por contrato a través de interfaces, clases abstractas y métodos. El Listado 6 muestra una versión de la clase Configuración en la que los codificadores PHP V4 intentaron crear una interfaz básica sin utilizar la palabra clave interfaz en absoluto.
Listado 6. interface.php4
<?php
clase IConfiguración
{
función obtener ($ clave) {}
}
clase Configuración extiende IConfiguration
{
var $_items = matriz();
función Configuración() {
$esto->cargar();
}
carga de función() { }
función obtener ($ clave) {
devolver $this->_items[ $clave ];
}
}
clase DBConfiguration extiende la configuración
{
carga de función() {
$this->_items[ 'imgpath' ] = 'imágenes';
}
}
$c = nueva configuración de base de datos();
echo( $c->get( 'imgpath' )."n" );
?>
El listado comienza con una pequeña clase IConfiguration que define todas las interfaces proporcionadas por la clase Configuration o las clases derivadas. Esta interfaz definirá el contrato entre la clase y todos sus usuarios. El contrato establece que todas las clases que implementan IConfiguration deben estar equipadas con un método get() y que todos los usuarios de IConfiguration deben insistir en usar sólo el método get().
El siguiente código se ejecuta en PHP V5, pero es mejor utilizar el sistema de interfaz proporcionado como se muestra a continuación.
Listado 7. interface1.php5
<?php
interfaz IConfiguración
{
función obtener ($ clave);
}
clase Configuración implementa IConfiguration
{
...
}
clase DBConfiguration extiende la configuración
{
...
}
$c = nueva configuración de base de datos();
echo( $c->get( 'imgpath' )."n" );
?>
Por un lado, los lectores pueden comprender más claramente el estado de ejecución; por otro lado, una sola clase puede implementar múltiples interfaces; El Listado 8 muestra cómo extender la clase Configuración para implementar la interfaz Iterator, que es la interfaz interna de PHP.
Listado 8. interface2.php5
<?php
interfaz IConfiguración {
...
}
clase Configuración implementa IConfiguration, Iterador
{
privado $_items = matriz();
función pública __construct() {
$esto->cargar();
}
carga de función protegida() { }
función protegida agregar ($ clave, $ valor) {
$this->_items[ $clave ] = $valor;
}
función pública obtener ($ clave) {
devolver $this->_items[ $clave ];
}
función pública rebobinar() { reset($this->_items }
función pública actual() {retorno actual($this->_items);
tecla de función pública() {tecla de retorno($this->_items});
función pública siguiente() { return siguiente($this->_items }
función pública válida() { return ($this->current() !== false }
}
clase DBConfiguration extiende la configuración {
...
}
$c = nueva configuración de base de datos();
foreach( $c como $k => $v ) { echo( $k." = ".$v."n" );
?>
La interfaz Iterator permite que cualquier clase parezca una matriz de sus consumidores. Como puede ver al final del script, puede usar el operador foreach para reiterar todos los elementos de configuración en el objeto Configuración. PHP V4 no tiene esta funcionalidad, pero puede usarla de varias maneras dentro de su aplicación.
La ventaja del mecanismo de interfaz es que los contratos se pueden unir rápidamente sin tener que implementar ningún método. La etapa final es implementar la interfaz, donde debes implementar todos los métodos especificados. Otra nueva característica útil en PHP V5 son las clases abstractas, que facilitan la implementación de la parte central de una interfaz con una clase base y luego usan esa interfaz para crear clases de entidad.
Otro uso de las clases abstractas es crear una clase base para múltiples clases derivadas en las que nunca se crea una instancia de la clase base. Por ejemplo, cuando DBConfiguration y Configuration existen al mismo tiempo, solo se puede usar DBConfiguration. La clase Configuración es sólo una clase base, una clase abstracta. Por lo tanto, puede forzar este comportamiento utilizando la palabra clave abstracta como se muestra a continuación.
Listado 9. abstract.php5
<?php
Configuración de clase abstracta
{
protegido $_items = matriz();
función pública __construct() {
$esto->cargar();
}
carga de función protegida abstracta();
función pública obtener ($ clave) {
devolver $this->_items[ $clave ];
}
}
clase DBConfiguration extiende la configuración
{
carga de función protegida() {
$this->_items[ 'imgpath' ] = 'imágenes';
}
}
$c = nueva configuración de base de datos();
echo( $c->get( 'imgpath' )."n" );
?>
Ahora, todos los intentos de crear una instancia de un objeto de tipo Configuración producirán errores porque el sistema considera que la clase es abstracta e incompleta.
Métodos y miembros estáticos
Otra característica nueva importante en PHP V5 es el soporte para miembros y métodos estáticos en clases. Al utilizar esta funcionalidad, puede utilizar el popular patrón singleton. Este patrón es ideal para la clase Configuración porque la aplicación debe tener solo un objeto de configuración.
El Listado 10 muestra la versión PHP V5 de la clase Configuración como un singleton.
Listado 10. static.php5
<?php
configuración de clase
{
privado $_items = matriz();
estático privado $_instancia = nulo;
función pública estática get() {
si (self::$_instance == nulo)
self::$_instance = nueva Configuración();
devolver uno mismo::$_instancia;
}
función privada __construct() {
$this->_items[ 'imgpath' ] = 'imágenes';
}
función pública __get( $clave ) {
devolver $this->_items[ $clave ];
}
}
echo( Configuración::get()->{ 'imgpath' }."n" );
?>
La palabra clave estática tiene muchos usos. Considere usar esta palabra clave cuando necesite acceder a algunos datos globales para todos los objetos de un solo tipo.
Método mágico
Otra gran característica nueva en PHP V5 es la compatibilidad con métodos mágicos, que permiten que los objetos cambien rápidamente la interfaz del objeto, por ejemplo, agregando variables miembro para cada elemento de configuración en el objeto Configuración. No es necesario utilizar el método get(), simplemente busque un elemento en particular y trátelo como una matriz, como se muestra a continuación.
Listado 11. magic.php5
<?php
configuración de clase
{
privado $_items = matriz();
función __construct() {
$this->_items[ 'imgpath' ] = 'imágenes';
}
función __get ($ clave ) {
devolver $this->_items[ $clave ];
}
}
$c = nueva Configuración();
echo( $c->{ 'imgpath' }."n" );
?>
En este ejemplo, creé un nuevo método __get() que se llama cada vez que el usuario busca una variable miembro en el objeto. Luego, el código dentro del método usará la matriz de elementos para encontrar el valor y devolver ese valor como si hubiera una variable miembro allí específicamente para esa palabra clave. Suponiendo que el objeto es una matriz, al final del script puede ver que usar el objeto de configuración es tan simple como encontrar el valor de imgpath.
Al migrar de PHP V4 a PHP V5, debe tener en cuenta estas características del lenguaje que no están disponibles en absoluto en PHP V4, y debe revalidar las clases para ver cómo se pueden usar.
Las excepciones
finalmente finalizan este artículo presentando el nuevo mecanismo de excepción en PHP V5. Las excepciones proporcionan una forma completamente nueva de pensar en el manejo de errores. Todos los programas generan inevitablemente errores: archivo no encontrado, falta de memoria, etc. Si no se utilizan excepciones, se debe devolver un código de error. Mire el código PHP V4 a continuación.
Listado 12. archivo.php4
<?php
función parseLine ($l)
{
//...
devolver matriz ('error' => 0,
datos => matriz() // datos aquí
);
}
función readConfig ($ ruta)
{
si ($ruta == nulo) devuelve -1;
$fh = fopen( $ruta, 'r' );
si ($fh == nulo) devuelve -2;
mientras(!feof($fh)) {
$l = fgets( $fh );
$ec = parseLine($l);
if ( $ec['error'] != 0 ) return $ec['error'];
}
fclose ($fh);
devolver 0;
}
$e = readConfig( 'myconfig.txt' );
si ( $e != 0 )
echo( "Hubo un error (".$e.")n" );
?>
Este código de E/S de archivo estándar leerá un archivo, recuperará algunos datos y devolverá un código de error si se encuentra algún error. Tengo dos preguntas sobre este script. El primero es el código de error. ¿Qué significan estos códigos de error? Para saber qué significan estos códigos de error, debe crear otro sistema para asignar estos códigos de error a cadenas significativas. El segundo problema es que el resultado devuelto por parseLine es muy complicado. Solo necesito que devuelva datos, pero en realidad tiene que devolver un código de error y datos. La mayoría de los ingenieros (incluido yo mismo) a menudo se vuelven perezosos y simplemente devuelven datos e ignoran los errores porque los errores son difíciles de gestionar.
El Listado 13 muestra cuán claro es el código cuando se usan excepciones.
Listado 13. archivo.php5
<?php
función parseLine ($l)
{
// Analiza y lanza una excepción cuando no es válida
devolver matriz(); // datos
}
función readConfig ($ ruta)
{
si ( $ ruta == nulo )
lanzar nueva excepción ('argumento incorrecto');
$fh = fopen( $ruta, 'r');
si ($fh == nulo)
lanzar nueva excepción ('no se pudo abrir el archivo');
mientras (! Feof ($fh)) {
$l = fgets( $fh );
$ec = parseLine($l);
}
fclose ($fh);
}
intentar {
readConfig('miconfig.txt');
} captura(Excepción $e) {
eco( $e );
}
?>
No necesito preocuparme por los códigos de error porque la excepción contiene texto descriptivo del error. Tampoco tengo que pensar en cómo rastrear el código de error devuelto por parseLine porque la función simplemente arrojará un error si ocurre uno. La pila se extiende hasta el bloque try/catch más cercano, que se encuentra en la parte inferior del script.
Las excepciones revolucionarán la forma de escribir código. En lugar de tener que lidiar con el dolor de cabeza de los códigos de error y las asignaciones, puede concentrarse en los errores que desea manejar. Dicho código es más fácil de leer, mantener y yo diría que incluso lo alentaría a agregar manejo de errores, ya que generalmente rinde dividendos.
Conclusión
Las nuevas características orientadas a objetos y la adición de manejo de excepciones brindan fuertes razones para migrar código de PHP V4 a PHP V5. Como puede ver, el proceso de actualización no es difícil. La sintaxis que se extiende a PHP V5 se parece a PHP. Sí, estas sintaxis provienen de lenguajes como Ruby, pero creo que funcionan muy bien juntas. Y estos lenguajes amplían el alcance de PHP desde un lenguaje de secuencias de comandos para sitios pequeños a un lenguaje que puede usarse para completar aplicaciones de nivel empresarial.