Los namespaces de PHP 5.3, una buena forma de tener un código más limpio y organizado

Balu4 Marzo 2011 - 9:04pm 2 comentarios
Enviar por Email Imprimir

Los namespaces de PHP 5.3, una buena forma de tener un código más limpio y organizado¿Utilizas namespaces en el desarrollo de tus aplicaciones con PHP? Con este artículo, que hemos traducido de devoperworks, tendremos una visión general de la sintaxis de un namespace (Espacio de nombre), aprenderemos sus mejores prácticas de uso y veremos una pequeña muestra de una aplicación Model-View-Controller que usa namespaces.

Conan es mi modelo a seguir”. Si hago esta declaración durante la cena, mi hijo inmediatamente pensaría que estoy haciendo alusión a Conan el Bárbaro; mientras que mi esposa creería que quisiera ser como el anfitrión del programa nocturno de entrevistas Conan O’Brien. Esta confusión de contexto es conocida en el mundo IT como name collision (Colisión de nombres). Muchos lenguajes de programación tienen estrategias para evitar el conflicto de nombres y, con su versión 5.3, PHP también tiene una. PHP resuelve el problema de los conflictos de nombres con su nueva herramienta de espacios de nombres. Desde luego, los nombres, con los que PHP resuelve la colisión, no son nombres de personas, sino más bien, nombres de clases, funciones y constantes.

En este artículo explicamos por qué deberíamos considerar el uso de los namespaces en nuestro próximo proyecto. Daremos un vistazo general a la semántica de los espacios de nombres, comentaremos las mejores prácticas y mostraremos una aplicación MVC que utiliza espacios de nombres...

¿Son necesarios los namespaces?

Una de las ventajas de PHP es su sencillez. Por lo que si eres nuevo en PHP, este es otro concepto que deberás conocer. Pero, si alguna de las siguientes consideraciones es cierta, debes considerar su uso:

  • Estás desarrollando una aplicación grande con cientos de archivos PHP.
  • Tu aplicación se esta desarrollando por un grupo de programadores.
  • Estas planeando usar frameworks que usen PHP 5.3 y nombres de espacio.
  • Haz usado namespaces (o similares, como paquetes) en otros lenguajes, tales como Java, Ruby o Python.

Si eres un desarrollador de aplicaciones relativamente pequeñas, los espacios de nombre tal ves no te sean tan útiles. Pero, para el resto de desarrolladores, los espacios de nombres proporcionan una forma limpia de organizar la estructura de clases y, por supuesto, evitar la colisión de nombres. Estas dos razones explican por qué muchos desarrolladores de frameworks están adoptando el uso de namespaces. Zend Framework (el gorila de 800 kilos de los frameworks en PHP), por ejemplo, esta usando namespaces en Zend Framework v2.0.

Un vistazo rápido

Un espacio de nombre proporciona un contexto para un nombre. Por ejemplo, las dos clases que se muestran en el listado 1, tienen una colisión de nombres.

Listado 1. Dos clases con el mismo nombre ocasionan una colisión de nombres, sin nombres de espacio

class Conan {
	var $bodyBuild = "extremely muscular";
	var $birthDate = 'before history';
	var $skill = 'fighting';
}

class Conan {
	var $bodyBuild = "very skinny";
	var $birthDate = '1963';
	var $skill = 'comedy';
}

Para especificar un namespace, simplemente debemos añadir una declaración namespace en la primera línea del código, como se muestra en el listado 2.

Listado 2. Dos clases con el mismo nombre pero con namespaces resuelven la colisión

namespace barbarian;
class Conan {
	var $bodyBuild = "extremely muscular";
	var $birthDate = 'before history';
	var $skill = 'fighting';
}
namespace obrien;
class Conan {
	var $bodyBuild = "very skinny";
	var $birthDate = '1963';
	var $skill = 'comedy';
}
$conan = new \barbarian\Conan();
assert('extremely muscular born: before history' == 
   "$conan->bodyBuild born: $conan->birthDate");

$conan = new \obrien\Conan();
assert('very skinny born: 1963' == "$conan->bodyBuild born: $conan->birthDate");

El código anterior funciona bien, pero antes de describir por qué ambos Conans funcionan bien juntos, vamos a señalar un par de cosas. En primer lugar, estamos usando assertions (afirmaciones o aserciones) para demostrar que el código funciona como se esperaba. Y en segundo lugar, estamos haciendo algo que nunca debes hacer: Declarar varios namespaces en un único archivo de código fuente.

El namespace proporciona una clasificación única para los dos Conans. El código define claramente cuando me estoy refiriendo al fornido destructor y cuando al programa de entrevistas nocturno de espectáculos. Observa que la sintaxis para la creación de instancias utiliza una barra diagonal inversa (\) seguida por el nombre de espacio:

$conan = new \barbarian\Conan();

y

$conan = new \obrien\Conan();

Estos tipos de clasificadores tienen un aspecto muy parecido al estilo de directorios de Windows. Por lo que no es mala idea compararlos. Así, por una parte, los namespaces soportan tanto referencias relativas como absolutas (igual que los directorios), y por otra, una de las mejores prácticas de programación es poner el código de las clases en directorios cuyos nombres coincidan con los namespaces.

Usando namespaces

Sería más del mundo real separar las dos clases Conan en sus directorios llamados “barbarian” y “obrien”, y luego referenciarlos desde otros archivos PHP. Hay tres maneras de referenciar un nombre de espacio en PHP:

  • Prefijar el nombre de la clase con el namespace
  • Importar el namespace
  • Definir un Alias del namespace

Para usar la primera opción, simplemente añadimos, como prefijo del nombre de la clase, el namespace (después de, obviamente, incluir el archivo de código fuente).

include "barbarian/Conan.php";
$conan = new \barbarian\Conan();

Esto es bastante sencillo, pero el problema con la primera opción es que, dada una aplicación grande, estaremos constantemente escribiendo el namespace. Y, además de todo lo que se escribe, oscureceremos el código base. con la segunda opción, podemos importar el espacio de nombre con el uso de la plabra reservada en PHP 5.3:

include "barbarian/Conan.php";
use barbarian\Conan;  
$conan = new Conan();

La opción 3 nos permite definir un alias para el namespace:

include "barbarian/Conan.php";
use \barbarian\Conan as Cimmerian;
$conan = new Cimmerian();

(Cimmerian, por cierto, es otro apodo de Conan el bárbaro)

Uno de los problemas que tengo con los tres ejemplos anteriores es el uso de la instrucción include. Podemos solucionar esto mediante el uso de la función __autoload(). El método mágico __autoload() llama a una función cuando una clase es referenciada sin haber incluido el archivo con su código fuente. Observemos el código del Listado 3 en un archivo llamado autoload.php.

Listado 3. Una función mágica __autoload dinámicamente incluye los archivos de código fuente

function __autoload($classname) {
  $classname = ltrim($classname, '\\');
  $filename  = '';
  $namespace = '';
  if ($lastnspos = strripos($classname, '\\')) {
    $namespace = substr($classname, 0, $lastnspos);
    $classname = substr($classname, $lastnspos + 1);
    $filename  = str_replace('\\', '/', $namespace) . '/';
  }
  $filename .= str_replace('_', '/', $classname) . '.php';
  require $filename;
}

Luego, importamos el archivo autoload.php en nuestro código fuente:

require_once "autoload.php"; 
use \barbarian\Conan as Cimmerian;

La gran ventaja de usar el auto-loader es que no necesitamos escribir declaraciones include para cada clase. Ten en cuenta que a pesar de los espacios de nombres en PHP pueden ser usados tanto para funciones y constantes cuanto para clases, la técnica del auto-loader sólo funciona para clases. El auto-loader es tan práctico que en lugar de programar funciones, podemos crear métodos en una clase apropiadamente llamada y poner las constantes en clases invariables.

Haciéndolo real con MVC

Para beneficiarse de los espacios de nombres, debemos definir las convenciones de los nombres antes de escribir una línea de código. Una buena práctica es usar un árbol de espacios de nombres (namespace  tree), considerando que los nombres de espacio tendrán nombres de espacio de alto nivel y sub - nombres de espacio. Si nuestra empresa tiene múltiples aplicaciones, puede ser útil tener un espacio de nombre de alto nivel, que será el nombre de la empresa. Luego, deberá utilizar un sub-namespace para la aplicación. Luego, tendremos un nivel que contendrá directorios que tendrán nombres para las clases funcionales de la aplicación PHP. Por ejemplo, digamos que el namespace de alto nivel de la empresa es Denoncourt, el primer nivel es “retail” y el tercer nivel tiene nombres funcionales, como se muestra en el Listado 4.

Listado 4. Un diseño de namespaces puede incluir sub-namespaces anidados

/denoncourt
	/retail
		/common
		/controller
		/model
		/utility
		/view

Los sub-namespaces controller, model y view se refieren, evidentemente, a la arquitectura MVC, pero a los sub-namespaces utility y common, los hemos considerado como clases generales que no encajan claramente en uno de los otros sub-namespaces.

Demos un salto al código de la aplicación mini-MVC. El listado 5 nos muestra el código del archivo index.php, el que se encuentra ubicado en el directorio raíz.

Listado 5. El index PHP de la aplicación MVC usa la clase controller

require "autoload.php";
use denoncourt\retail\controller as Control;
$controller = new Control\Controller();
$controller->execute();

Noten lo largo que es el namespace y el uso del alias Control. El uso del alias es mi método preferido para usar namespaces por dos razones: Primero, si posteriormente queremos renombrar el namespace, sólo tenemos que cambiar una línea del código por archivo fuente. Y en segundo lugar, es una buena práctica para calificar nuestro namespace como una instancia de clases, mi uso de Control\Controller() es efectivamente igual que \denoncourt\retail\controller\Controller(). Observen que también pude pude haber creado un alias para el namespace de alto nivel, y luego usarlo para nombrar el sub-namespace de la clase de instanciación.

use denoncourt\retail as Retail;
$controller = new retail\controller\Controller();

Esta es una característica útil para esos momentos en que queremos referirnos a varios niveles de namespaces en el mismo archivo de código fuente. En el directorio denoncourt/retail/controller, hemos creado el archivo Controller.php, que se muestra en el listado 6.

Listado 6. La clase controller MVC realiza acciones basadas en el input del usuario

namespace denoncourt\retail\controller;
use denoncourt\retail as retail;

class Controller {
  public function execute() {
    switch ($_GET['action']) {
    case 'showItem' :
      $item = new retail\model\Item();
      require "denoncourt/retail/utils/format.php";
      require "denoncourt/retail/view/item.php";
      break;
    }
  }
}

En denoncourt/retail/model, he creado el archivo Item.php. El listado 7 nos muestra el código.

Listado 7. La clase Item MVC esta en el sub-namespace model

namespace denoncourt\retail\model;
class Item {
  public $itemNo = '123';
  public $price = 2.45;
  public $qtyOnHand = 87;
}

En denoncourt/retail/utils, he creado el archivo format.php, que se muestra en el listado 8.

Listado 8. La función PHP dollar muestra como una función puede ser namespaciada

namespace denoncourt\retail;
function dollar($dollar) {
    return "\$$dollar";
}

Noten que, como dijimos anteriormente, hubiera sido preferible poner la función de formato en una clase utility (por lo que el auto-loader se ocuparía de importar su código y habría que hacer declaraciones requiere para el format.php).

Finalmente, la página de vista item.php esta en denoncourt/retail/views. El listado 9 muestra el código.

Listado 9. La página item muestra el modelo instanciado en el controlador

<html>
<head>
<style>
dt {
  float:left; clear:left;
  font-weight:bold;
  margin-right:10px;
  width:15%;
  text-align: right;
}
dd { text-align:left; }
</style>
</head>
<body>
<dl>
  <dt>Item No:</dt><dd><?php echo "$item->itemNo"; ?></dd>
  <dt>Price:</dt><dd>
       <?php echo \denoncourt\retail\dollar($item->price); ?>
       </dd>
  <dt>Quantity On Hand:</dt><dd><?php echo "$item->qtyOnHand"; ?></dd>
</dl>
</body>
</html>

Observen como la página item cualifica la función dollar con el namespace \denoncourt\retail\.

Fall back

Si un archivo fuente contiene una declaración namespace, todas las declaraciones a clases, funciones y constantes usarán la semántica del namespace. Cuando PHP se encuentra con nombre no cualificado en un contexto de nombre de clase, función o constante, esto es conocido como fallback. Un fallback en una clase de usuario hace que el compilador asuma el namespace actual. Para referirnos a clases sin namespaces, necesitamos poner una barra invertida sola. Por ejemplo, para referirnos a la clase Exception de PHP, podríamos usar $error = new \Exception();. Hay que tener esto en cuenta al utilizar cualquier clase estándar de la libería de PHP (Como ArrayObject, FindFile y KeyFilter).

Para las funciones y constantes, si el namespace actual no contiene esa función o constante, el mecanismo de fallback de PHP, como alternativa, llamará a una función estándar de PHP. Por ejemplo, si hemos programado nuestra propia función strlen, PHP utilizaría nuestra función. Pero, si también queremos usar la función strlen standard de PHP (por ejemplo, dentro de nuestra propia implementación strlen), necesitamos preceder el nombre de la función con una barra invertida, como lo muestra el listado 10.

Listado 10. Las funciones standard de PHP pueden ser culificadas con un backslash para identificar el namespace global

namespace denoncourt\retail;
function strlen($str) {
    return \strlen();
}

La variable namespace global y strings

Si te gusta programar métodos dinámicos, podrías estar tentado a ubicar un namespace en un string bajo doble comillas: "denoncourt\retail\controller". Pero recuerda que que necesitas escapara los slashes: "denoncourt\\retail\\controller". Una alternativa simple es usar comillas simples: 'denoncourt\retail\controller'.

Al igual que lo hace esta programación dinámica, hay que tener en cuenta que PHP 5.3 tiene una variable global llamada __NAMESPACE__. Hay que considerar el uso de la variable global en lugar de escribir lo siguiente:

$echo 'Estoy usando el nombre de espacio: ' . __NAMESPACE__;

IDE con soporte de namespaces

La mayoría de IDEs ya brindan soporte a PHP 5.3. NetBeans 6.8 brinda un gran soporte a namespaces. No sólo ofrece el autocompletado de código, también nos ofrece sugerencia para mejorar nuestro código con mejores prácticas. Por ejemplo, es una buena práctica con las namespaces de PHP para cualificar completamente los namespaces en nuestro código usarlas con referencias absolutas, en lugar de con relativas. Si tenemos un código clave que usa cualificadores de namespace relativos, NetBeans muestra una ícono  de alerta al lado del código. Si uno posa el mouse sobre el ícono, NetBeans muestra un tool tip describiendo el cambio sugerido. Y si luego hacemos clic en el ícono, NetBeans realiza los cambios en el código.

Zend Studio también provee de características similares. Si eres reticente a usar namespaces, considera actualizar tu IDE y trata con los namespaces con un poco de ayuda de tu IDE favorito. Tenga en cuenta que es posible que ni siquiera tengas que actualizar el IDE, ya que muchos de ellos ofrecen soporte a PHP 5.3 desde hace más de un año.

PHP Development Tools (PDT) 2.1 también ofrece un sólido soporte a namespaces. PDT es un plug-in para Eclipse.

Para activar el soporte de namespace, primero tenemos que decirle a Eclipse/PDT que use la versión 5.3 de PHP. Para hacer esto, vamos al menú principal de la aplicación, damos clic en Windows > Preferences, como muestra la figura 1. Expandimos PHP en el panel de la izquierda, y seleccionamos PHP Interpreter. Luego, cambiamos la versión a PHP 5.3, y damos clic en OK.

Figura 1. El plugin PDT de Eclipse necesita que definamos el interprete de PHP a la versión 5.3

El plugin PDT de Eclipse necesita que definamos el interprete de PHP a la versión 5.3

Podemos crear un proyecto PHP dando clic en File > New Project, expandir el nodo PHP, y luego dar clic en PHP Project. Para crear un archivo PHP, simplemente damos clic derecho en el explorador PHP y luego en PHP file. PDT usa apropiadamente el resaltado de sintaxis para las palabras claves namespace y use (ver figura 2).

Figura 2. PDT usa syntax highlighting para las palabras claves de espacio de nombre y muestra namespaces en el explorador de PHP y el panel Outline

PDT usa syntax highlighting para las palabras claves de espacio de nombre y muestra namespaces en el explorador de PHP y el panel Outline

Es útil que PDT nos muestre los espacios de nombres en el explorador de PHP y Outline views, ya que nos ayuda a visualizar como se han asignado nuestros namespaces en varias clases. PDT nos provee algo que esperamos en los IDEs: Autocompletado de código (ver figura 3). El autocompletado de código es invocado por PHP cuando escribimos la declaración use.

Figura 3. PDT provee autocompletado de código para nombres de espacio

PDT provee autocompletado de código para nombres de espacio

PDT también nos mostrará un pop up de autocompletado de código cuando escribamos los nombres de las clases. Por ejemplo, si escribimos new Item, PDT también nos mostrará una ventana listando Item - denoncourt\retail\item.

Cuando seleccionamos denoncourt\retail\item, PDT inserta la declaración use requerida y el cualificador en una línea de instanciación:

use denoncourt\retail\model;
new model\Item();

Lo que es cool es cuando escribimos new Conan, PDT también nos muestra una ventana con el listado:

Conan – obrien
Conan – barbarian

Nos permite seleccionar el Conan apropiado. Y ahora que he divagado con mi enamoramiento por los dos Conans, quizás es hora de terminar.

Finalizando

Si aún tienes reticencias para trabajar con espacios de nombres, antes de posponer el aprendizaje de los namespaces por una año más, sería buena idea que cargues tu IDE favorito con el soporte para PHP 5.3 y escribamos algunos namespace. En cuanto a las convenciones de nomenclatura, lo importante es definir algunas ideas simples antes que dar con una estrategia perfecta. Personalmente, si tienes experiencia en Java, sería conveniente seguir las convenciones de nomenclatura de Java. Podemos usar nombres Camel Case en los namespace de PHP y mantenernos alejados de los guiones bajos. Con el uso de los namespaces en nuestro próximo proyecto, nuestro código será más limpio y organizado. Nos familiarizaremos con una característica que es común en los principales lenguajes de desarrollo. Y estaremos preparados para utilizar la riqueza de los frameworks que ya están usando PHP 5.3 y los espacios de nombre, en particular.

Descargar | Scripts de ejemplo (previa aceptación de condiciones de uso)

Comentarios

Imagen de Wladimir Avila
Imagen de José Luis Rosales Serrato
José Luis Rosal...

Saludos ! Obvio que no soy porgramador, pero; espero que alguién se apiade de mi, estoy instalando el script de Open School (open-school(dot)org/community_edition(dot)php).

Mi servidor corre actualmente en PHP Version 5.3.17 y al parecer este sistema esta hecho para correr sobre PHP 5.2

/home/uvdhidqu/public_html/escuela/core/YiiBase.php(421)

409 {
410 foreach(self::$_includePaths as $path)
411 {
412 $classFile=$path.DIRECTORY_SEPARATOR.$className.'.php';
413 if(is_file($classFile))
414 {
415 include($classFile);
416 break;
417 }
418 }
419 }
420 else
421 include($className.'.php');
422 }
423 else // class name with namespace in PHP 5.3
424 {
425 $namespace=str_replace('\\','.',ltrim($className,'\\'));
426 if(($path=self::getPathOfAlias($namespace))!==false)
427 include($path.'.php');
428 else
429 return false;
430 }
431 return class_exists($className,false) || interface_exists($className,false);
432 }
433 return true;

Gracias de antemano
José Luis

Tutoriales

Cómo descargar videos de VK.com
En este artículo voy a explicar como descargar videos y películas...
Descargar Facebook Móvil Gratis
Por si aún no lo han hecho, es posible descargar Facebook Móvil...
Cómo generar tráfico web con las redes sociales - Paso a Paso
Muchas empresas están publicando contenidos como la forma de crear...

Artículo Recomendado

3 Tips cruciales para recuperar archivos eliminados
¿Te imaginas perder el trabajo de toda una semana en tan solo unos segundos? Todos hemos pasado por este problema. Quizás eliminamos por error un archivo importante o lo borramos sin pensar que era valioso para otro... más