PHP – I namespaces

22 Feb

Out Of Date Warning

Questo post è stato pubblicato più di 2 anni fa (il 22 febbraio 2012). Le idee vanno avanti velocemente, le prospettive cambiano quindi i contenuti potrebbero non essere aggiornati. Ti prego di tenere in considerazione questo, e di verificare le informazioni tecniche presenti nell'articolo prima di farne affidamento per i tuoi scopi.

I namespaces, sono un’importante novità, da tempo agognata e promessa, e finalmente disponibile da PHP 5.3, colmando così una della lacune che vengono ancora rinfacciate al linguaggio.
Anche se questa caratteristica è ormai disponibile da qualche tempo, probabilmente ancora non tutti ne hanno capito a pieno l’importanza e magari, continuano ad utilizzare le varie soluzioni alternative che erano nate in attesa della loro implementazione.

Cosa sono i namespaces?

I namespaces, forniscono un modo in cui raggruppare classi, funzioni e costanti correlate tra loro al fine di evitare una collisione di denominazione. Si potrà così avere due classi con lo stesso nome in due posti diversi se vengono incapsulate all’interno dei namespaces. La documentazione ufficiale li definisce infatti così:

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

Il loro scopo principale è quello di risolvere il problema degli autori di librerie o applicazioni quando creano elementi riutilizzabili come classi o funzioni. Cosa succederebbe se aggiungessimo una libreria esterna ad un nostro progetto, e la libreria avesse all’interno classi denominate allo stesso modo delle nostre? Stesso discorso per progetti di grosse dimensioni, sviluppati da più persone e composti da centinaia se non migliaia di componenti. La possibilità di scontri di denominazione sarebbe molto alta. Bene i nemespaces servono proprio ad evitare questo problema.

Alternative

Prima della loro implementazione, gli sviluppatori hanno adottato tecniche di emulazione, soprattutto riguardo la programmazione object-oriented. Una standard piuttosto comune, per esempio, è quello adottato da Zend Framework 1 in cui il problema della collisione di denominazione viene risolto attraverso nomi di classe extra lunghi, in cui viene anteposto al nome della classe i nomi delle directories in qui essa si trova, separate da un underscore es: My_Controller_Helper_Acl (in My\Controller\Helper\Acl.php). Utilizzando questa tecnica naturalmente i nomi di classe sono univoci, ma a volte tendono a risultare esageratamente lunghi.

Difinizione

I namespaces sono definiti attraverso la parola chiave namespace seguita da un nome.

Un file che ne contiene uno deve dichiararlo in cima alla pagina, prima di qualsiasi altra riga di codice, ad eccezione dei commenti e della parola chiave declare:

All’interno di un namespace possiamo raggruppare classi, interfacce, funzioni e costanti.
Una volta dichiarato, l’ambito del namespace è applicato all’intero file.

I Namespaces che contengono parole chiave PHP genereranno un errore di analisi:

Infine i namespaces non possono iniziare con un numero:

Sub-namespace

Come per directories e files, i namespaces possono essere definiti con sotto-livelli. Il seguente codice produrrà la costante Test\Sub\Level\TEST_CON, la funzione Test\Sub\Level\testFun e la classe Test\Sub\Level\testClass.

Namespaces nello stesso file

Anche se sconsigliato è possibile dichiarare più namespace nello stesso file. In questo caso è buona norma utilizzare la sintassi a parentesi graffe:

Nota: Non è possibile mischiare namespace dichiarati con sintassi senza parentesi con namestace dichiarati con sintassi a parentesi graffe.

Per combinare, nello stesso file, codice di uno spazio di nomi e codice globale dobbiamo utilizzare la sintassi con parentesi. Il codice globale, dovrebbe essere racchiuso in una dichiarazione namespace senza nome:

Nota: In caso di multi-namespaces nello stesso file l’ambito di ogni namespace termina alla dichiarazione del successivo.
Nota:La definizione di sub-namespaces con blocchi di codice annidati non è supportata. Il seguente codice genererà un errore fatale: “Namespace declarations cannot be nested”.

Utilizzo dei namespace

Dopo averli definiti dobbiamo capire come utilizzarli realmente. Questa è probabilmente la parte più importante da capire. In questo ci può aiutare il filesystem, difatti la logica è praticamente la stessa.
Prendiamo come esempio il seguente file test\sub\level\myFile.php.

  • Posso accedervi attraverso il nome, se la directory corrente fosse level allora potrei accedere al file semplicemente attraverso il nome: myFile.php.
  • Posso accedervi attraverso percorso relativo, se mi trovassi dentro sub potrei accedervi con: level\myFile.php.
  • Posso accedervi attraverso percorso assoluto, qualunque sia la directory corrente, con: \test\sub\level\myFile.php.

Lo stesso principio lo possiamo applicare con i namespaces, avremo quindi le seguenti tre possibilità:

  • Unqualified name: (nomi non qualificati) si riferiscono solo al namespace corrente, se per esempio mi trovo nel namespace Test, allora testFun() sarà risolto con Test\testFun(). In pratica sono privi di un namespace separator (\).
  • Qualified name: (nomi qualificati) paragonabile al percorso relativo del filesystem. Per esempio se il namespace corrente fosse Test\Sub, Level\testFun() sarebbe risolto con: Test\Sub\Level\testFun().
  • Fully-qualified name: (nomi completamente qualificati) paragonabile al percorso assoluto, è probabilmente il modo più sicuro ed inequivocabile anche se più prolisso. Per esempio \Test\Sub\Level\testFun() cercherà la funzione testFun() nel namespace Test\Sub\Level. In pratica iniziano sempre con un namespace separator.

Facciamo un esempio, consideriamo il file file1.php:

Qua abbiamo definito una costante un funzione ed una classe nel namespace Test\Sub\Level.
Ora consideriamo il file file2.php:

Riferirsi all’ambito globale

Una volta dichiarato un namespace, se si ha la necessita di riferirsi a classi o inerfacce globali bisogna anteporre al nome un backslash. Questo invece non è necessario per le funzioni e le costanti, sempre che queste non siano state dichiarate a loro volta nel namespace. Es:

Importazione di namespaces

Attraverso la parola chiave use, possiamo importare un namespace in un file diverso.
Questa è una caratteristica che ci permetterà poi di far riferimento al namespace importato attraverso un alias anzichè con suo nome completo, rendendoci la vita più facile soprattutto per namespace profondamente nidificati.

E’ possibile importare namespace, classi ed interfacce, ma non funzioni o costanti.

Naturalmente una situazione del genere non è valida…

…in quanto non è chiaro quale classe testClass deve essere istanziata:
Fatal error: Cannot use Test2\Sub2\Level2\testClass as testClass because the name is already in use in…
In questi casi (ma anche in altri) può tornarci comodo fornire noi stessi un alias al namespace:

E’ supportata anche questa sintassi di dichiarazione multipla:

Nota: Non è possibile utilizzare Fully-qualified nameper riferirsi a degli alias. Il seguente codice produrrà un errore fatale in quanto come detto cercherà la classe nell’ambito globale: 

Namespaces dinamici

PHP è un linguaggi dinamico per esempio ci permette di istanziare classi o chiamare funzioni dinamicamente:

I namespaces non ne fanno eccezione possiamo utilizzare indifferentemente sia qualified che fully-qualified name, non vi è nessuna differenza:

Dobbiamo ricordarci tuttavia che possiamo utilizzare soltanto nomi completi:

Sono inoltre supportati altri due metodi per accedere agli elementi del namespace corrente in maniera astratta. Questi sono la parola chiave namespace e la costante magica __NAMESPACE__.

La parola chiave namespace è un equivalente dell’operatore self delle classi, mentre la costante __NAMESPACE__ è una stringa contenente il nome del namespace corrente.
In ambito globale conterrà una stringa vuota.

Riepilogo

I namespace sono quindi un’importante caratteristica di PHP. Essi ci permettono di:

  1. Raggruppare classi, funzioni e costanti al fine di evitare una collisione di denominazione.
  2. Evitare nomi di classi molto lunghi, facilitando la lettura del codice sorgente.

Sono contemplate tre modalità di risoluzione:

  1. Unqualified name: Un identificatore senza nessun separatore namespace, es: test.
  2. Qualified name: Un identificatore con almeno un separatore namespace che non sia la prima lettera es: test\sub.
  3. Fully qualified name: Un identificatore che inizia con un separatore namespace, es \test\sub (anche namespace\test è un fully qualified name).

Questo è in definitiva tutto quello che è necessario sapere per iniziare ad utilizzarli nelle proprie applicazioni.

Risorse

www.php.net/manual/en/language.namespaces.php

3 Commenti su “PHP – I namespaces”

  1. Vicnenzo 7 novembre 2012 at 19:00 #

    L’unico articolo chiaro sul web sui namespace! Complimenti!

    • grimax 7 novembre 2012 at 20:29 #

      Grazie 😉

    • Saddy 19 febbraio 2015 at 15:01 #

      Infatti, è davvero l’unico articolo chiaro e completo. Ora ho imparato cosa sono i namespace, e grazie a questo posso cominciare a studiare Symfony 🙂

Lascia un commento