A PHP programozási nyelv

Névterek

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:

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:

  1. 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.
  2. 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.
  3. 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):

  1. 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).
  2. 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.
  3. 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:

  1. Osztályok álnévvel ellátása
  2. Interfészek álnévvel ellátása
  3. 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:

  1. 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.
  2. 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.
  3. 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.
  4. 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.
  5. 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):
    1. 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)
    2. 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.
  6. 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
    1. A névtér feloldás az aktuális névtérből indul (a példában A\B\C\D)
    2. 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.