L’introduzione dei namespaces in PHP rappresenta un’innovazione nel linguaggio ed ha legami stretti con lo standard PSR-4 e l’autoloading delle classi. A livello teorico l’argomento può sembrare semplice, ma nell’implementazione del codice non è così immediato.

Cosa sono i namespaces in php?

What are namespaces? In the broadest definition namespaces are a way of encapsulating items.

I namespaces, introdotti in PHP 5.3 e migliorati in PHP 7, permettono di raggruppare classi, interfacce, funzioni e costanti per evitare ambiguità di denominazione. In questo modo potremo far coesistere classi con lo stesso nome “incapsulate” in namespace differenti esattamente come avviene per file con lo stesso nome posti in cartelle diverse del filesystem. La possibilità di creare una gerarchizzazione (sub-namespace) fornisce una chiara indicazione sull’organizzazione delle risorse del nostro applicativo. Ad esempio, assegnando i namespaces Framework\Core\Main ad un file contenente una classe e Framework\Core\Main\Libs ad un altro contenente un’altra classe con il medesimo nome, è facilmente intuibile come la prima si riferisca alle funzionalità di base del nostro ipotetico framework e l’altra sia presente in una libreria secondaria.

Pur essendo forte l’analaogia con il filesystem non sussite alcuna relazione diretta fra un namespace ed il percorso dei file .php. E’ però intuitivo utilizzare questa analogia per creare vincoli convenzionali e sfruttarli nella pratica dell’autoloading la cui implementazione più diffusa è sintetizzata nello standard PSR-4 che approfondirò in un altro articolo.

Namespaces: vantaggi di utilizzo

  • Risolvono il problema di omonimia di classi, interfacce, funzioni e costanti tra il codice scritto dallo sviluppatore, quello integrato in librerire di terze parti e quello nativo di Php.
  • Facilitano lo sviluppo di codice ordinato, organizzato ed “eloquente”.
  • Migliorano la leggibilità e la scrittura del codice permettendo di utilizzare degli alias nel momento in cui la precisa ed inevitabile lunghezza del namespace non fosse necessaria.

Namespaces: dichiarazione

Compresi i vantaggi del loro utilizzo vediamo come utilizzare i namespaces iniziando dalla loro dichiarazione attraverso l’apposita keyword namespace.
Uno script che faccia uso di namespaces deve dichiararlo all’inizio del listato fatta eccezione per i commenti ed eventuali istruzioni declare.

<?php
declare(encoding='UTF-8');

// Dichiaro il namespace...
namespace Framework\Core\Main;

Attenzione!

  • I namespaces non possono contenere keywords del linguaggio php.
    Framework\Core\Class
  • I namespaces non possono iniziare con un numero.
    Framework\Core\1Class
  • E’ possibile, ma altamente sconsigliato, dichiarare più namespaces nello stesso file utilizzando le parentesi graffe.
    <?php
    Framework\Core\Main { // codice }
    Framework\Core\Libs { // codice }

Php Namespaces: dalla teoria alla pratica

Immaginiamo di creare ed includere due file php contenenti la dichiarazione di una classe denominata nello stesso modo (Es: MyClass). Una volta in esecuzione otterremmo questo errore:
Fatal error: Cannot declare class MyClass, because the name is already in use

Aggiungiamo ora ai due file una dichiarazione namespace…

<?php // File 1: /Framework/Core/Main/MyClass.php

namespace Framework\Core\Main;

Class MyClass {
    public function __construct() {
        echo '<p>MAIN: MyClass ready!</p>';
    }
}
<?php // File 2: /Framework/Core/Main/Libs/MyClass.php

namespace Framework\Core\Main\Libs;

Class MyClass {
    public function __construct() {
        echo '<p>LIBS: MyClass ready!</p>';
    }
}

Creiamo ora il file principale ex01.php che li includerà e tenterà di creare un oggetto.

<?php // File: /ex01.php

// Includo i file
require_once('Framework/Core/Main/MyClass.php');
require_once('Framework/Core/Main/Libs/MyClass.php');

// Creo l'oggetto
$objOne = New MyClass();

Mandandolo in esecuzione ci troveremo davanti ancora un errore…
Fatal error: Uncaught Error: Class 'MyClass' not found in...

Questo si verifica poichè le nostre due classi “MyClass” sono assegnate ai rispettivi namespaces, mentre il nostro file principale non ha alcun namespace specificato e si riferisce quindi a quello globale in cui la classe MyClass() non è definita.

Namespaces: tipi di riferimento

Sappiamo definire i namespaces, ma per richiamarli correttamente dobbiamo conoscere quali sono i tipi di riferimento che possiamo utilizzare e che nuovamente hanno una forte analogia con i percorsi dei filesystem:

  • Unqualified name (nomi non qualificati): si riferiscono solo al namespace corrente. È come cercare un fille nella cartella in cui ci si trova.
    MyClass()
  • Qualified name (nomi qualificati): paragonabile al percorso relativo di un file.
    Main\Libs\MyClass()
  • Fully-qualified name (nomi completamente qualificati): inizia sempre con un backslash iniziale ed è paragonabile ad un percorso assoluto. È il modo più sicuro anche se più verboso.
    \Framework\Core\Main\Libs\MyClass()

Unqualified name – Nel file principale (ex02.php) dichiariamo lo stesso namespace di uno dei due file contenenti la dichiarazione di MyClass() che vorremmo richiare, ad esempio Libs: Framework\Core\Main\Libs. Ecco finalmente il primo risultato positivo!

<?php // File: /ex02.php

// Dichiaro il namespace
namespace Framework\Core\Main\Libs;

// Includo i file
require_once('Framework/Core/Main/MyClass.php');
require_once('Framework/Core/Main/Libs/MyClass.php');

// UNQUALIFIED NAME...
$objOne = New MyClass();

Qualified name – Nel file principale (ex03.php) dichiariamo un namespace più generico Framework\Core ed utilizzamo nomi qualificati per riferirci ad entrambe le nostre classi ed ottenere i relativi oggetti.

<?php
// File: /ex03.php

// Dichiaro il namespace
namespace Framework\Core;

require_once('Framework/Core/Main/MyClass.php');
require_once('Framework/Core/Main/Libs/MyClass.php');

// QUALIFIED NAME...
$objOneMain = New Main\MyClass();
$objOneLib = New Main\Libs\MyClass();

Nota: una volta dichiarato un namespace, per riferirsi al contesto globale è necessario anteporre il backslash.

Fully-qualified name – Nel file principale (ex04.php) omettiamo il namespace, ma utilizziamo un nome completamente qualificato per riferirci alle due classi.

<?php // File: /ex04.php

// Includo i file
require_once('Framework/Core/Main/MyClass.php');
require_once('Framework/Core/Main/Libs/MyClass.php');

// FULLY-QUALIFIED NAME...
$objOneMain = New \Framework\Core\Main\MyClass();
$objOneLib = New \Framework\Core\Main\Libs\MyClass();

Php Namespaces: importazione e aliasing tramite l’operatore “use”

Una caratteristica importante dei namespaces è la possibilità di essere importati e di creare degli alias per renderne più pratico l’utilizzo vista la loro “eloquenza”. Queste funzioni trovano un parallelismo nella capictà dei filesystem di creare link simbolici a file o cartelle.
A tale scopo Php mette a disposizione la keyword use con la quale è possibile eseguire i seguenti tipi di seguenti tipi di importazione/aliasing:

  • nomi di classi o interfacce (use Name\Space\ClassName),
  • nomi di classi o interfacce globali (use DateTime)
  • namespaces (use Name\Space\For\...)
  • nomi di funzioni (use function Name\Space\FunctionName)
  • nomi di costanti (use const Name\Space\CONSTANT).

E’ possibile importare anche più elementi nello stesso file purché non si generino delle ambiguità, in tal caso si potrà ricorrere agli alias.

Applichiamo use al nostro scenario di esempio (ex05.php) per importare i namespaces comuni alla due classi e sintetizzarli in “Main”.

<?php // File: /ex05.php

use Framework\Core\Main as Main;

// Includo i file
require_once('Framework/Core/Main/MyClass.php');
require_once('Framework/Core/Main/Libs/MyClass.php');

// QUALIFIED NAME...
$objOneMain = New Main\MyClass();
$objOneLib = New Main\Libs\MyClass();

Nell’esempio seguente (ex06.php) utilizziamo use per superare l’ambiguita dei nomi delle classi e creare un alias dei namespaces.

<?php // File: /ex06.php

use Framework\Core\Main\MyClass as MainMy;
use Framework\Core\Main\Libs\MyClass as LibsMy;

// Includo i file
require_once('Framework/Core/Main/MyClass.php');
require_once('Framework/Core/Main/Libs/MyClass.php');

// UNQUALIFIED NAME...
$objOneMain = New MainMy();
$objOneLib = New LibsMy();

Direi sia tutto! Qui trovate i file di esempio visti nella lezione e se l’articolo è stato utile… condividilo!