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.
<?php namespace Test; //...
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
:
<?php /** * Questa è una dichiarazione di namespace con commento, * spazi bianchi e declare per stabilire la codifica del file */ declare(encoding='UTF-8'); namespace Test; //...
All’interno di un namespace possiamo raggruppare classi, interfacce, funzioni e costanti.
Una volta dichiarato, l’ambito del namespace è applicato all’intero file.
<?php namespace Test; const TEST_CON = '1'; function testFun () {} class testClass {} ?>
I Namespaces che contengono parole chiave PHP genereranno un errore di analisi:
<?php /** * Class e Function sono parole chiave PHP e non possono essere * utilizzati per nominare un namespace */ namespace Function; // errore di analisi namespace Project\Class; //errore di analisi
Infine i namespaces non possono iniziare con un numero:
<?php namespace Test\Sub\1Level; // errore namespace Test\Sub\Level1; // namespace valido
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
.
<?php namespace Test\Sub\Level; const TEST_CON = '1'; function testFun () {} class 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:
<?php namespace Test { const TEST_CON = '1'; function testFun () {} class testClass {} } namespace Test2 { const TEST_CON = '2'; function testFun () {} class testClass {} } ?>
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:
<?php namespace Test { const TEST_CON = '1'; function testFun () {} class testClass {} } namespace { session_start(); Test\Bis\testFun(); } ?>
<?php namespace Test { namespace Sub { class TestClass { } } }
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
, alloratestFun()
sarà risolto conTest\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 funzionetestFun()
nel namespaceTest\Sub\Level
. In pratica iniziano sempre con un namespace separator.
Facciamo un esempio, consideriamo il file file1.php:
<?php //file1.php namespace Test\Sub\Level; const TEST_CON = '1'; function testFun () {} class testClass {} ?>
Qua abbiamo definito una costante un funzione ed una classe nel namespace Test\Sub\Level.
Ora consideriamo il file file2.php:
<?php //file2.php namespace Test\Sub; require_once 'file1.php'; const TEST_CON = '2'; function testFun () {} class testClass {} // Unqualified name echo TEST_CON; // risolto in Test\Sub\TEST_CON testFun(); // risolto in Test\Sub\testFun $a = new testClass(); // risolto in Test\Sub\testClass // Qualified name Level\TEST_CON; // risolto in Test\Sub\Level\TEST_CON Level\testFun(); // risolto in Test\Sub\Level\testFun // Fully-qualified names \Test\Sub\testFun(); // risolto in Test\Sub\testFun \Test\Sub\Level\testFun(); // risolto in Test\Sub\Level\testFun ?>
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:
<?php namespace Test\Sub; function strtoupper ($string) { return $string; } /* Risolto in Test\Sub\strtoupper() */ echo strtoupper('ciao'); //ciao /* Risolto nella funzione nativa */ echo \strtoupper('ciao'); //CIAO /* Risolto nella funzione nativa visto che Test\Sub\strtolower() non esiste */ echo strtolower('CIAO'); //ciao //Fatal error: Class 'Test\Sub\mysqli' not found in... $mysqli = new mysqli("host", "user", "psw", "db"); //Ok $mysqli = new \mysqli("host", "user", "psw", "db"); ?>
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.
<?php namespace Test\Sub; use Test\Sub\Level; //identico a scrivere Test\Sub\Level as Level Level\testFun(); //chiama la funzione Test\Sub\Level\testFun $a = new Level\testClass(); //instanzia un oggetto della classe Test\Sub\Level\testClass ?>
E’ possibile importare namespace, classi ed interfacce, ma non funzioni o costanti.
<?php use Test\Sub\Level\testClass; $a = new testClass(); ?>
Naturalmente una situazione del genere non è valida…
<?php use Test\Sub\Level\testClass; use Test2\Sub2\Level2\testClass; $a = new testClass(); $b = new testClass(); ?>
…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:
<?php use Test\Sub\Level\testClass as classe1; use Test2\Sub2\Level2\testClass as classe2; $a = new classe1; $b = new classe2; ?>
E’ supportata anche questa sintassi di dichiarazione multipla:
<?php use Test\Sub\Level\testClass as classe1, Test2\Sub2\Level2\testClass as classe2; //..
<?php use Test\Sub\Level\testClass; $a = new \testClass(); ?>
Namespaces dinamici
PHP è un linguaggi dinamico per esempio ci permette di istanziare classi o chiamare funzioni dinamicamente:
<?php class test { public function __construct() { echo __CLASS__; } } $class = 'test'; $a = new $class; //test ?>
I namespaces non ne fanno eccezione possiamo utilizzare indifferentemente sia qualified che fully-qualified name, non vi è nessuna differenza:
<?php namespace Test\Sub\Level; const TEST_CON = '1'; function testFun () { return __FUNCTION__; } class testClass { public function __construct() { echo __METHOD__; } } //utilizzo qualified name echo constant('Test\Sub\Level\TEST_CON'); //1 // utilizzo fully-qualified name $ns = 'Test\Sub\Level'; $class = $ns . '\testClass'; $fun = $ns . '\testFun'; echo $fun(); //Test\Sub\Level\testFun $a = new $class; //Test\Sub\Level\testClass::__construct ?>
Dobbiamo ricordarci tuttavia che possiamo utilizzare soltanto nomi completi:
<?php //file1.php namespace Test\Sub\Level; const TEST_CON = '1'; ?>
<?php //file2.php namespace Test\Sub; require_once 'file1.php'; echo Level\TEST_CON; //1 echo constant('Level\TEST_CON'); //errore echo constant('Test\Sub\Level\TEST_CON'); //1 ?>
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__
.
<?php namespace Test\Sub\Level; const TEST_CON = '1'; class testClass { function __construct() { echo __CLASS__; } } echo namespace\TEST_CON; //1 echo __NAMESPACE__; //Test\Sub\Level $class = __NAMESPACE__ . '\testClass'; $a = new $class; //Test\Sub\Level\testClass ?>
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:
- Raggruppare classi, funzioni e costanti al fine di evitare una collisione di denominazione.
- Evitare nomi di classi molto lunghi, facilitando la lettura del codice sorgente.
Sono contemplate tre modalità di risoluzione:
- Unqualified name: Un identificatore senza nessun separatore namespace, es:
test
. - Qualified name: Un identificatore con almeno un separatore namespace che non sia la prima lettera es:
test\sub
. - Fully qualified name: Un identificatore che inizia con un separatore namespace, es
\test\sub
(anchenamespace\test
è un fully qualified name).
Questo è in definitiva tutto quello che è necessario sapere per iniziare ad utilizzarli nelle proprie applicazioni.
8 risposte su “PHP – I namespaces”
L’unico articolo chiaro sul web sui namespace! Complimenti!
Grazie 😉
Infatti, è davvero l’unico articolo chiaro e completo. Ora ho imparato cosa sono i namespace, e grazie a questo posso cominciare a studiare Symfony 🙂
E a distanza di altri 3 anni confermo quanto detto sopra: continua ad essere il più chiaro e completo.
Tks
Ma esiste un modo del tipo:
use Namespace\*
per includere tutti i componenti del namespace con il loro effettivo nome (senza specificare Namespace\…) ???
Ottimo articolo, sempre utile!!
Articolo ben fatto e completo, finalmente non solo un copia e incolla di “sono introdotti con il php 5.3” e basta come fanno tutti