A PHP nyelv az 5.3-as verziótól támogatja a névtereket.
A névterek a hosszú függvénynevek leváltására és a névütközések elkerülésére lettek bevezetve.
Névterek definiálása
Minden PHP kód névterekbe szervezhető, viszont a névterek csak az alábbi 4 nyelvi elemet foglalják egységbe:
- Osztályok
- Interfészek
- Függvények
- Konstansok
Névtereket a namespace kulcsszó használatával lehet definiálni. Névteret csak a forrásfájl elején lehet meghatározni. Egy névteret több forrásfájl is leírhat, illetve egy fájlon belül több névtér is megadható.
A php és PHP nevekkel kezdődő névterek (pl. php/Classes) belső használatra vannak fenntartva. Ezen névterek (és alnévtereik) használata kerülendő.
<?php
namespace MyProject;
const CONNECT_OK = 1;
class Connection { /* ... */ }
function connect() { /* ... */ }
?>
Az alábbi kód hibás, mivel névtér csak a fájl elején adható meg:
<html>
<?php
namespace MyProject;
?>
Több névtér definiálása egy fájlon belül
Lehetőség van több névtér deklarálására egy fájlon belül. Ezt két módon lehet megtenni:
1. Névterek egymás alatti definiálása (nem javasolt):
<?php
namespace MyProject;
const CONNECT_OK = 1;
class Connection { /* ... */ }
function connect() { /* ... */ }
namespace AnotherProject;
const CONNECT_OK = 1;
class Connection { /* ... */ }
function connect() { /* ... */ }
?>
2. Névterek blokkokba zárása:
<?php
namespace MyProject {
const CONNECT_OK = 1;
class Connection { /* ... */ }
function connect() { /* ... */ }
}
namespace AnotherProject {
const CONNECT_OK = 1;
class Connection { /* ... */ }
function connect() { /* ... */ }
}
?>
Névterek blokkba zárt megadásánál a blokkokon kívül nem szerepelhet PHP kód (ezalól a declare utasítás kivételt képez):
<?php
declare(encoding='UTF-8');
namespace MyProject {
const CONNECT_OK = 1;
class Connection { /* ... */ }
function connect() { /* ... */ }
}
?>
Több névtér megadásánál a globális névtér is elérhető marad (bővítés céljából). Ezt egy név nélküli namespace blokk megadásával lehet bővíteni:
<?php
namespace MyProject {
const CONNECT_OK = 1;
class Connection { /* ... */ }
function connect() { /* ... */ }
}
namespace { // globális névtér
session_start();
$a = MyProject\connect();
echo MyProject\Connection::start();
}
?>
Alnévterek definiálása
A PHP-ban a névterek egymásba ágyazhatóak (ahogy a fájlrendszerben a könyvtárak), ezzel hierarchiát lehet kialakítani. Az alábbi példában a MyProject névtéren belül található a Sub névtér, amin belül definiálunk egy Level nevű alnévteret:
<?php
namespace MyProject\Sub\Level;
const CONNECT_OK = 1;
class Connection { /* ... */ }
function connect() { /* ... */ }
?>
Névterek használata
A PHP a névterekben való mozgása hasonló a fájlrendszerek könyvtárainak felépítéséhez. A fájlrendszerekben 3 fajta módon tudunk elérni egy megadott fájlt:
- Relatív fájlnév
Ha a fajl.txt állományt keressük azt az aktuális könyvtárban keresi az operációs rendszer. Így ha épp a /home/user könyvtárban tartózkodunk akkor a /home/user/fajl.txt állomány kerül kiválasztásra.
- Relatív útvonallal
Ekkor ha a konyvtar/fajl.txt állományt keressük, és a /home/user könyvtárban állunk, akkor az operációs rendszer a /home/user/konyvtar/fajl.txt állományt választja ki.
- Abszolút hivatkozással
Abszolút hivatkozásnál az aktuális tartózkodási hely nem játszik szerepet a kiválasztsában. Ekkor ha a /root/secret.txt fájlt keressük az a /root/secret.txt állományt fogja kiválasztani.
A PHP a fenti példának megfelelően a következő módon választja ki a megfelelő névteret, és azon belül az osztályt (valamint egyéb elemeket):
- Nem minősített névvel (unqualified name)
Ebben az esetben a névterek által befolyásolt elemek az aktuális névtérből lesznek kiválasztva, így ha az aktuális névtér a \app\core, akkor az $a = new Foo(); osztályt a PHP \app\core\Foo -ként fogja keresni. Ha az aktuális névtér a globális névtér, akkor a Foo osztály lesz kiválasztva.
Ha egy névtér által befolyásolt elem nem található meg az aktuális névtérben, akkor a PHP a globális névtérben kezdi keresni. Az előző példát felhasználva így ha az \app\core névtéren belül nem található a Foo osztály, akkor a globális \Foo osztály kerül példányosításra (amennyiben ez létezik).
- Minősített névvel (qualified name)
Minősített névvel ellátott elemnél az aktuális könyvtárhoz képest lesz megállapítva az elem keresési helye. Ha az aktuális névtér a \app\core, és történik egy példányosítás, hogy $a = new utils\Foo();, akkor a PHP a \app\core\utils\Foo osztályt fogja megtalálni.
- Teljesen minősített névvel (fully qualified name)
Ekkor az osztályok, interfészek, konstansok és függvények a globális névtérhez képest lesznek megadva. Ha például a $a = new \app\core\Foo(); utasítással példányosítunk, akkor a Foo osztályt a PHP a \app\core névtéren belül fogja keresni.
<?php
namespace Foo\Bar;
/* Nem minősített névvel */
foo(); // visszatér a Foo\Bar\foo függvénnyel
foo::staticmethod(); // visszatér a Foo\Bar\foo osztály statikus metódusával
/* Minősített névvel */
subnamespace\foo(); // visszatér a Foo\Bar\subnamespace\foo függvénnyel
/* Teljesen minősített névvel */
\Foo\Bar\foo(); // visszatér a Foo\Bar\foo függvénnyel
?>
Lehetőség van a névtereken belül a globális függvények, osztályok és konstansok elfedésére is. Ekkor az eredeti PHP által szolgálatott függvényeket teljesen minősített névvel, a saját függvényeket nem minősített névvel lehet elérni:
<?php
namespace Foo;
function strlen() {}
const INI_ALL = 3;
class Exception {}
$a = \strlen('hi'); // globális strlen függvény hívása
$b = new \Exception('error'); // globális Exception osztály példányosítása
$c = strlen('hi'); // saját strlen függvény hívása
$d = new Exception('error'); // saját Exception osztály példányosítása
?>
Aktuális névtér lekérdezése
Szükség lehet az aktuális névtér lekérdezésére is. Ezt a __NAMESPACE__ konstans segítsévégel tehetjük meg:
<?php
namespace MyProject;
echo '"', __NAMESPACE__, '"'; // outputs "MyProject"
?>
Illetve globális névtérben:
<?php
echo '"', __NAMESPACE__, '"'; // outputs ""
?>
Az aktuális névtér egy osztályát a namespace kulcsszó segítségével érhetjük el explicit módon:
<?php
namespace MyProject;
blah\mine(); // MyProject\blah\mine()
namespace\blah\mine(); // MyProject\blah\mine()
?>
Névtér álnevek (alias) és névtér importálás
A PHP lehetőséget nyújt a teljesen minősített névvel megadott névterek álnevesítésére. Az álneveken keresztül így könnyebben elérhetőek a kiválasztott névtér osztályai és interfészei.
Három fajta álnév áll rendelkezésre a PHP-ban:
- Osztályok álnévvel ellátása
- Interfészek álnévvel ellátása
- Névterek álnévvel ellátása
Függvények és konstansok esetében az álnevek nem használhatóak.
Névteret álnévvel a use kulcsszóval lehet ellátni:
<?php
namespace foo;
use My\Full\Classname as Another;
// ez a megadási mód azonos ezzel: use My\Full\NSname as NSname
use My\Full\NSname;
// globális osztály importálása
use ArrayObject;
$obj = new namespace\Another; // a foo\Another osztály példányosítása
$obj = new Another; // a My\Full\Classname osztály példányosítása
NSname\subns\func(); // a My\Full\NSname\subns\func függvény hívása
$a = new ArrayObject(array(1)); // az ArrayObject osztály példányosítása
// a "use ArrayObject" importálás nélkül ez a kód a foo\ArrayObject osztályt példányosítaná
?>
Mivel névterek álnevesítésénél és importálásánál csak teljesen minősített névvel lehet hivatkozni az álnevesíteni vagy importálni kívánt névtérre, ezért ott a kezdő \ nem szükséges (és nem is ajánlott).
Több névtér megadására is van lehetőség egyetlen use kulcsszó segítségével. Ez a következőképp tehető meg:
<?php
use My\Full\Classname as Another, My\Full\NSname;
$obj = new Another; // a My\Full\Classname osztály példányosítása
NSname\subns\func(); // a My\Full\NSname\subns\func függvény hívása
?>
A névtér álnevesítése és importálása csak a nem minősített és minősített nevekre van érvényben. A teljesen minősített névvel ellátott osztályok és interfészek elérése abszolút módon történik, így ezeket nem befolyásolják.
Álneveket csak a fájl globális területén lehet megadni. Blokkokon belül fordítási hibát fog okozni, ahogy ez a példa is helytelen:
<?php
namespace Languages;
class Greenlandic
{
use Languages\Danish;
...
}
?>
A névtér álnevek nem öröklődnek fájl include-olásánál! Ezek használatához minden fájl elején meg kell adni a használni kívánt álneveket!
Névtér feloldási szabályok
Definíciók
Nem minősített név (unqualified name): azonosító névtér elválasztó nélkül, pl. Foo.
Minősített név (qualified name): azonosító névtér elválasztóval, pl. Foo\Bar.
Teljesen minősített név (fully qualified name): azonosító, amely névtér elválasztóval kezdődik, pl. \Foo\Bar. Teljesen minősített név továbbá a namespace\Foo megadási mód is.
Feloldási szabályok
A névterek feloldása a következő szabályok alapján történik:
- Teljesen minősített névvel ellátott függvények, osztályok és konstansok fordítási időben kerülnek kiértékelésre.
- Minden minősített és nem minősített név szerkesztési időben kerül fordításra. Ezek megadásánál a névtér álnevek figyelembe lesznek véve. Példa:
Az A/B/C névtér C álnéven van importálva, egy kódrészlet hivatkozik a C\D\e() függvényre, akkor a A\B\C\D\e() függvény lesz meghívva.
- Egy névteren belül a minősített névvel ellátott névtér feloldás ha nem esik névtér importálás hatókörébe, akkor az aktuális névtér lesz a feloldás kiindulópontja. Példa:
Ha az aktuális névtér a A\B, és a C\D\e() függvény kerül meghívásra, akkor a A\B\C\D\e() függvény lesz ami meghívódik.
- Nem minősített névvel ellátott névtér feloldásnál ha a keresett osztály egy álnévre hivatkozik, akkor az álnév lesz feloldva. Példa:
Ha az A/B/C névtér álneve C, akkor a new C() az A\B\C() osztályt fogja példányosítani.
- Nem minősített névvel ellátott függvény esetében a névtér megállapítása a következő sorrend alapján megy végbe (az aktuális névtér legyen A\B):
- Ha a megadott nevű függvény definiálva van az aktuális névtérben, akkor az meghívásra kerül (ha a függvény neve foo, akkor a A\B\foo függvény kerül meghívásra)
- Ha a függvény nincs definiálva az aktuális névtérben, akkor a globális névtérből lesz kiválasztva.
- Ha egy nem minősített névvel vagy minősített névvel ellátott függvény vagy osztály kerül meghívásra (például az A\B névteren belül a C\D osztály példányosítása), akkor
- A névtér feloldás az aktuális névtérből indul (a példában A\B\C\D)
- Betöltés megkísérlése a megtalált névtérből
Egy névtéren belül a globális névtér egy függvényének eléréséhez teljesen minősített névvel kell hivatkozni.