De jegyezd meg jól, míg a Föld kerek, mindig lesznek névterek!
Tudom, hogy tartozom még mindenféle cikkel, ezért rangsorolnom kell őket, aszerint, hogy épp mennyire nagy a baj az adott területen vagy épp mennyire sürgető lenne megismerni olyan dolgokat, amik így 2015-ben a “zalapműveltséghez” tartoznak bizonyos körökben.
A címből gondolom már mindenki rájött, hogy most a Pokolgép zenekar tündöklése és bukása névterek kerülnek terítékre és mindaz, amit használatukkal és bevezetésükkel elérhetünk.
Mi is az a névtér?
Amikor valaki nekiáll programozni és sorban ontja magából az osztályokat, függvényeket, akkor lehet ő is belefutott már abba a problémába, amit névütközésnek nevezünk, miszerint ugyanazon névvel csak egy függvényt/osztályt lehet definiálni (még jó, különben runtime érkezési sorrendben hívódnának meg, mint a háziorvosnál). Ennek a kiküszöbölésére variáljuk a függvényünk nevét kis- és nagybetűkkel, alsóvonásokkal hozták létre még régebben a névterek fogalmát.
Amikor valaki nem használ névtereket, akkor is használja őket, illetve csak egyet, mégpedig a globális névteret, ami minden más névtér felett áll. A namespace-ek (igen, a névtér már kissé uncsi volt) arra szolgálnak, hogy ezt az ütközést elkerüljük és csoportosítsuk, rendszerezzük a kódunkat.
Deklaráljunk hát egy névteret!
1
2
3
4
<?php
namespace System;
class MeinClass {
}
Fontos megjegyezni, hogy névtereket mindig a fájl elején deklaráljunk, különben nyávogni fog a parser. Ez a névtér az egész fájlra érvényes lesz, ha egy fájlon belül több névtért szeretnénk deklarálni, akkor kapcsos zárójeleket használjunk a következő módon:
1
2
3
4
namespace System {
class MeinClass {}
}
namespace AnotherNamespace {}
Fentebb deklaráltunk egy System névre hallgató namespace-t és azon belül pedig egy MeinClass osztályt.
Ezt egyazon névtéren belül úgy szimplán a MeinClass
névvel tudunk elérni, viszont a névtéren kívülről a System\MeinClass
-al, amit úgy hívunk, hogy fully qualified classname.
A globális névtérben deklarált osztályokat és függvényeket (kivéve a beépített függvényeket és eljárásokat) egy \ prefixel tudjuk a névtéren belülről elérni. Vagyis ha a MeinClass-t a globális névtérben deklaráltuk, akkor a
System
névtéren belülről a\MeinClass
-al tudjuk elérni. Erre kivételek hajigálásánál figyeljünk!
Kihasználni a névtereket
A névterek használatával szorosan összefügg a use
kulcsszó. A use kulcsszóval tudjuk használatba venni/importálni az adott osztályt, amit a már előbb említett fully qualified classname-el kell megadni, vagyis minden névtér előtaggal együtt. Apropó, a névtereket lehet egymásba ágyazni, ilyen formán csomagokat, modulokat tudunk létrehozni magunk számára.
1
2
3
4
<?php
namespace System\Nested\ReallyNested; // több névtért egymásba lehet ágyazni ám!
use OAuth2\Server; // az OAuth2 névtérben található Server osztályt importáljuk, ami Server-ként érhető el.
use System\MeinClass as Mc; // az előbbi System névtérben található MeinClass-t importáljuk és egy álnévvel látjuk el, így a kód többi részében Mc-ként hivatkozhatunk rá.
Importáláskor az autoloaderjeink (amik ugye vannak?) sietnek segítségünkre és megkapják az osztály nevét, a névterekkel együtt, ám escapelik a stringben szereplő backslash-t, így az autoloader a System\\MeinClass string-et fogja megkapni a fenti esetben.
Ha egy egyszerű autoloadert összedobunk a projektünk gyökerében (ami ugye egy index.php-t tartalmazó public folder felett van egy szinttel, ugye?) és az nemes egyszerűséggel így néz ki:
1
2
3
4
5
6
7
8
9
10
11
12
<?php
define('DS', DIRECTORY_SEPARATOR); oprendszere vállógatja
define('APP_ROOT', __DIR__. DS); // definiáltuk a gyökérkönyvtárat
spl_autoload_register('TestAutoload');
function TestAutoload($className) { // a fully qualified classname-t kapja meg
$segments = explode("\\", $className);
if (file_exists(APP_ROOT. implode(DS, $segments) . ".php" )) {
include_once(APP_ROOT. implode(DS, $segments). ".php");
}
}
Akkor a System\MeinClass-t a projektgyökere\System\MeinClass.php-ben fogja keresni. Ilyen formában a névtereinket és a könyvtárstruktúránkat szinkronba hozhatjuk.
Ezzel a projektenként újra és újra előforduló kódokat csoportosíthatjuk, csinálhatunk magunknak egyfajta standard library-t és oda gyűjtögethetjük azt. FQC = Fully qualified classname
1
2
3
4
5
6
7
8
9
10
11
12
- Module // az adott modul, netán projekt neve namespace Module;
| - Controller // namespace Module\Controller;
| | - AjaxController.php // FQC Module\Controller\AjaxController;
| | - FrontendController.php // FQC Module\Controller\FrontendController
| - Model // namespace Module\Model;
| | // Nem, ide nem kerülnek modulspecifikus modellek, itt jöhet a trükk
- System // namespace System;
| - Model // namespace System\Model;
| | - UserModel // FQC System\Model\UserModel;
| | - User // FQC System\Model\User
| - Db // namespace System\Db;
| | - DbAdapter // FQC System\Db\DbAdapter;
A view fájlokkal ez esetben nem foglalkozok, vegyük úgy, hogy a routerünk már ott is teremtett bennünket a FrontendControllerünkben.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
namespace Module\Controller;
use System\Model\UserModel; // importáltuk a UserModelt.
use System\Db\DbAdapter; // importáljuk az adatbázis adaptert
class FrontendController {
public function indexAction() {
$um = UserModel::getInstance(); // elérjük a usermodelt
$um->setAdapter(new DbAdapter($valamiDbConfig)); // és beállítjuk az adatbázis adaptert rajta
try {
$um->commitSuicide();
catch (\Exception $e) { // \Exception-ként adtuk meg, mivel ha nem írjuk elé, akkor alapesetben az aktuális névtérre vonatkozik, tehát Module\Controller\Exception
die('die, die my darling!');
}
}
}
A fenti példában és könyvtárszerkezeten remélem jól látszik, hogy valójában mennyire egyszerű és praktikus a névterek használata. Létrehozhatunk portábilis kódrészleteket, amiket ha ugyanazon autoloadereket használunk, a többi projektből is pontosan ugyanúgy érünk el. Viszont vannak a fenti kódban csúnyaságok, ha már a dependency injection-t kitárgyaltam múltkor, de akkor ha több időm és energiám lesz, visszatérek a ServiceLocator-okkal!