Lo imprescindible de los namespaces PHP explicado con ejemplos

Continuemos con la explicación de la clase PHP Translate del componente SimpleTranslation. Recuerda que la semana pasada nos servimos de este componente para explicar en el videocurso PHP guay (disponible en YouTube) cómo funciona el operador ternario. Pues bien, ¡hoy es el turno de los espacios de nombres PHP!

Antes de la llegada de los namespaces (característica muy notable disponible a partir de PHP 5.3) no era raro que los desarrolladores de código reutilizable, es decir, los programadores de componentes, librerías, frameworks de desarrollo, etc., pusieran nombres muy largos a sus etiquetas PHP. Así evitaban que dichos nombres colisionaran con los nombres de otros componentes desarrollados por terceros.

Así, por poner algún ejemplo, frameworks como Zend 1 ponían nombres tan largos como Zend_Translate_Adapter_Array a sus clases PHP. Sin los namespaces, todas las funciones, clases, constantes e interfaces quedaban definidos en el espacio de nombres global.

A continuación voy a explicar los ejercicios disponibles en GitHub phpguay/ejercicios/namespaces.

example-without-namespaces.php:

#!/bin/php
<?php
class Greetings
{
    static function hello()
    {
        return 'Hello, I am Alice!';
    }
    static function bye()
    {
        return 'Good bye, I am Alice!';
    }
}

class Greetings
{
    static function hello()
    {
        return 'Hello, I am Bob!';
    }
    static function bye()
    {
        return 'Good bye, I am Bob!';
    }
}

El código anterior lanza el siguiente error porque, como decimos, no se puede definir dos veces la misma cosa en el mismo espacio de nombres:

Fatal error: Cannot redeclare class Greetings in example-without-namespaces.php on line 16

Los namespaces vienen a solucionar esta limitación.

example-with-namespaces.php:

#!/bin/php
<?php
namespace Alice {
class Greetings
{
    static function hello()
    {
        return 'Hello, I am Alice!';
    }
    static function bye()
    {
        return 'Good bye, I am Alice!';
    }
}
}

namespace Bob {
class Greetings
{
    static function hello()
    {
        return 'Hello, I am Bob!';
    }
    static function bye()
    {
        return 'Good bye, I am Bob!';
    }
}
}

namespace {
echo Alice\Greetings::hello() . PHP_EOL;
echo Bob\Greetings::hello() . PHP_EOL;
echo Alice\Greetings::bye() . PHP_EOL;
echo Bob\Greetings::bye() . PHP_EOL;
}

El código anterior con namespaces genera la siguiente salida:

Hello, I am Alice!
Hello, I am Bob!
Good bye, I am Alice!
Good bye, I am Bob!

El ejemplo anterior es bastante clásico (y básico). Encontrarás variantes del mismo en otros sitios y, cómo no, en el manual oficial de PHP. Sin embargo es un ejemplo didáctico que en realidad no es nada práctico porque se desaconseja por completo combinar varios espacios de nombres en un mismo archivo. Digamos que es para entender las cosas.

Como indicábamos al principio, los namespaces están pensados para facilitar el desarrollo de librerías, para evitar las colisiones de nombres. Por lo tanto, son de interés para los programadores PHP OO en tanto que se ponen a desarrollar componentes reutilizables y no quieren que los nombres de sus etiquetas colisionen con otros nombres, creados por otros desarrolladores, en otros componentes de la app.

En phpguay/ejercicios/namespaces/oop-example está el ejemplo didáctico anterior, pero esta vez dentro de un contexto más real orientado a objetos.

Alice.php:

<?php
namespace Alice;

class Greetings
{
    static function hello()
    {
        return 'Hello, I am Alice!';
    }
    static function bye()
    {
        return 'Good bye, I am Alice!';
    }
}

Bob.php:

<?php
namespace Bob;

class Greetings
{
    static function hello()
    {
        return 'Hello, I am Bob!';
    }
    static function bye()
    {
        return 'Good bye, I am Bob!';
    }
}

Luego hay dos variantes de programa principal, que es quien utiliza las clases Alice y Bob:

main-01.php:

#!/bin/php
<?php
require 'Alice.php';
require 'Bob.php';

echo Alice\Greetings::hello() . PHP_EOL;
echo Bob\Greetings::hello() . PHP_EOL;
echo Alice\Greetings::bye() . PHP_EOL;
echo Bob\Greetings::bye() . PHP_EOL;

main-02.php:

#!/bin/php
<?php
require 'Alice.php';
require 'Bob.php';

use Alice\Greetings as A;
use Bob\Greetings as B;

echo A::hello() . PHP_EOL;
echo B::hello() . PHP_EOL;
echo A::bye() . PHP_EOL;
echo B::bye() . PHP_EOL;

main-02.php ilustra el funcionamiento de use para crear alias y no andar escribiendo repetidas veces los mismos namespaces al utilizar las clases Alice y Bob.

Bueno amigos, esto ha sido todo por hoy. Espero que esta introducción a los namespaces PHP os haya gustado y os sirva, de momento, para entender un poco mejor el funcionamiento de la clase PHP Translate. El manual de PHP explica de forma exhaustiva cómo funcionan los namespaces.

Esta explicación debería servir para entender cómo se inicializa la clase Translate:

include_once 'SimpleTranslation/Translate.php';
use \SimpleTranslation\Translate;
Translate::init('es', __DIR__ . "/lang/es.php");