Korzystanie z przestrzeni nazw znacznie usprawnia pracę z kodem, a także eliminuje problem kolizji, gdzie dwie klasy mogą mieć identyczne nazwy. Przestrzenie nazw pozwalają również na stworzenie bardzo logicznej i przejrzystej hierarchi naszego kodu. W tym krótkim tutorialu dowiemy się czym są i jak poprawnie używać przestrzeni nazw w PHP.
Funkcjonalność ta niczym nie różni się od tego samego rozwiązania stosowanego od zarania dziejów w językach takich jak C, czy Java. Przestrzenie nazw w PHP zostały dodane dość późno, bo dopiero w wersji 5.3.
Przestrzeń globalna
Jest główną przestrzenią nazw. Zauważmy, że bardzo często piszemy sobie jakieś klasy, czy też funkcje, bądź też korzystamy z tych wbudowanych w silnik PHP. Korzystamy np. z funkcji header() służacej do ustawienia nagłówka, funkcji strip_tags() służącej do obcięcia tagów HTML i wielu innych. Nazewnictwo tych funkcji jest proste i składa się z jednego członu określającego nazwę funkcji. Funkcje takie istnieją w domyślnej przestrzeni nazw i są dostępne wszędzie w naszym kodzie.Podobnie jest z klasami, ich metodami i właściwościami. Nie wymagają poprzedzania ich nazwy nazwą przestrzeni w jakiej występują, gdyż globalna przestrzeń jest tą domyślną, bazową. Wyobraźmy sobie teraz, że tworzymy własną klasę, np. niech to będzie klasa o nazwie:
- MyClass {}
Utworzymy sobie plik z klasą, który będzie zawierał definicję jednej klasy, jednej funkcji i jednej stałej, niech będzie to np. plik /classes/MyClass.php. W pliku tym stworzymy sobie następującą definicję dla klasy MyClass:
- <?php
- // /classes/MyClass.php
- const MYCONST = 'classes/MyClass.php';
- function MyFunction()
- {
- return __FUNCTION__;
- }
- class MyClass {
- public function MyMethodName()
- {
- return __METHOD__;
- }
- public function MyClassName()
- {
- return __CLASS__;
- }
- public static function SayHello()
- {
- return 'Hello from ' . __CLASS__ . '::' . __METHOD__;
- }
- }
- ?>
Plik z klasą dołączymy do naszego głównego pliku, niech będzie to plik index.php.
Następnie utworzymy nowy obiekt tejże klasy i wywołamy metodę GetMethodName(), która zwróci nam nazwę naszej metody w klasie MyClass:
- <?php
- // index.php
- require_once(__DIR__.'/classes/MyClass.php');
- echo MYCONST; // WYNIK: classes/MyClass.php
- echo 'Function name:' . MyFunction(); // WYNIK: MyFunction
- $c = new MyClass;
- echo 'Method name:' . $c->MyMethodName(); // WYNIK: Method name: GetMethodName
- echo 'Class name:' . $c->MyClassName(); // WYNIK: Class name: MyClass
- echo MyClass::SayHello(); // WYNIK: Hello from MyClass::SayHello
- ?>
Jak widzimy, na początku wyświetlamy stałą, potem nazwę funkcji, następnie klasę - jest to prościutka klasa, z trzema metodami zwracającymi odpowiednio:
- nazwę wywoływanej metody
- nazwę wywoływanej klasy
- tekst przywitania z przedstawieniem się
W tym prostym przypadku nie zdeklarowaliśmy żadnej przestrzeni nazw, nasza klasa istnieje zatem w przestrzeni globalnej.
Możemy utworzyć jej obiety w dowolnym miejscu w kodzie. A co jeśli teraz stworzymy kolejną klasę o takiej samej nazwie? Stwórzmy plik /other_classes/MyClass.php zawierający identyczną definicję klasy co /classes/MyClass.php i dołączmy do naszego index.php:
- <?php
- // index.php
- require_once(__DIR__.'/classes/MyClass.php');
- require_once(__DIR__.'/other_classes/MyClass.php');
- echo MYCONST;
- echo 'Function name:' . MyFunction();
- $c = new MyClass;
- echo 'Method name:' . $c->MyMethodName();
- echo 'Class name:' . $c->MyClassName();
- echo MyClass::SayHello();
- // WYNIK: Fatal error: Cannot redeclare class Users
- ?>
Działanie takie spowoduje błąd, gdyż staramy się zdefiniować dwie takie same klasy w jednej i tej samej przestrzeni nazw (tutaj - w globalnej). Do rozwiązania tego problemu wykorzystamy właśnie oddzielne przestrzenie nazw.
namespace
Przestrzeń nazw definiujemy za pomocą wyrażenia:
- namespace NAZWA_PRZESTRZENI;
Deklaracja taka musi się znaleźć zaraz na początku pliku - należy o tym pamiętać.
Blok kodu jaki wystąpi po deklaracji przestrzeni nazw może należeć jednocześnie jedynie do jednej przestrzeni, jednakże możemy w jednym pliku zawrzeć kilka przestrzeni i bloków kodu:
- <?php
- namespace MyApp1;
- // blok kodu dla MyApp1
- namespace MyApp2;
- // blok kodu MyApp2
- namespace MyApp3;
- // blok kodu dla MyApp3
- ?>
Nie polecam jednak takiego rozwiazania - my będziemy stosować po jednej przestrzeni na jeden plik.
Wyobraźmy sobie, że nasza aplikacja webowa ma nazwę MyApp i jest podzielona na jakieś elementy. Jednym z elementów moze być obsługa kont użytkowników, innym dostęp do bazy danych, jeszcze innym moduł do wyświetlania artykułów.
Kod takich elementów zapewne rozmieścilibyśmy sobie w aplikacji mniej więcej w taki sposób:
- /classes/Users.php
- /classes/DB.php
- /classes/Articles.php
- itd...
Idąc takim pomysłem, stwórzmy sobie nową klasę, o nazwie np. Users i zapiszmy w pliku /classes/Users.php, ale tym razem dołączmy do niej przestrzeń nazw o nazwie np. MyApp\Users.
Stwórzmy naszą klasę:
- <?php
- // /classes/Users.php
- namespace MyApp\Users;
- class Users {
- public function MyMethodName()
- {
- return __METHOD__;
- }
- public function MyClassName()
- {
- return __CLASS__;
- }
- public static function SayHello()
- {
- return 'Hi, I\'m users class: ' . __CLASS__;
- }
- }
- ?>
Na samym początku stworzyliśmy tutaj przestrzeń nazw o nazwie MyApp\Users.
Zauważmy, że na początku podaliśmy MyApp, następnie po backslashu (\) podaliśmy Users.
Taka właśnie jest konwencja nazewnictwa w przestrzeniach nazw, nic nie stoi na przeszkodzie, abyśmy następne przestrzenie nazywali jeszcze bardziej hierarchicznie, jak np.:
MyApp\Users\Actions\AddUsers.
Załączmy teraz utworzoną klasę do naszego index.php i spróbujmy wywołać metodę Users::SayHello():
- <?php
- // index.php
- require_once(__DIR__.'/classes/Users.php'); // dołączamy klasę Users
- echo Users::SayHello(); // WYNIK: Fatal error: Class 'Users' not found!
- ?>
Wywołanie pliku index.php wyświetliło nam błąd informujący o tym, że klasa Users nie istnieje!
Ale zaraz, zaraz - przecież właśnie ja stworzyliśmy i załączyliśmy do pliku index.php. W czym więc problem? Otóż problem w tym, że nasza klasa istnieje, ale w przestrzeni nazw o nazwie MyApp\Users. Nie istnieje natomiast w przestrzeni globalnej naszego kodu.
Zmodyfikujmy teraz lekko nasz index.php i zmienmy jego kod na:
- <?php
- // index.php
- require_once(__DIR__.'/classes/Users.php'); // dołączamy klasę Users
- echo \MyApp\Users\Users::SayHello(); // WYNIK: Hi, I'm users class: MyApp\Users\Users
- ?>
Voila! Zadziałało, mamy dostęp do klasy Users.
Czego więc tutaj naprawdę dokonaliśmy? Otóż poprzedziliśmy nazwę wywoływanej klasy jej przestrzenią nazw.
Zauważmy więc, że nasza klasa Users istnieje TYLKO I WYŁĄCZNIE w swojej przestrzeni i tylko w taki sposób możemy się do niej odwołać.
Wyjaśnienia wymaga:
- \MyApp\Users\
Otóż tym zapisem podaliśmy prefix naszej klasy, czyli jej przestrzeń nazw, dopiero po tym prefixie właściwą nazwę klasy. Zapis ten rozpoczęliśmy jednak od backslasha (\) - co to oznacza? Oznacza to to, że pierwszy backslash zawsze oznacza przestrzeń główną, bazową, tą położoną najwyżej w hierarchi. Jej nazwa jest pusta, więc podajemy ją jedynie za pomocą jednego backslasha na początku.
Wykonajmy teraz podobny manewr co wcześniej i stwórzmy drugą, identyczną klasę o nazwie Users, ale w innej przestrzeni nazw. Stwórzmy np. plik /other_classes/Users.php i wpiszmy do niego to samo co w poprzedniej klasie Users, zmieńmy jednak jego przestrzeń nazw:
- <?php
- // /other_classes/Users.php
- namespace MyApp\OtherUsers;
- class Users {
- public function MyMethodName()
- {
- return __METHOD__;
- }
- public function MyClassName()
- {
- return __CLASS__;
- }
- public static function SayHello()
- {
- return 'Hi, I\'m users class: ' . __CLASS__;
- }
- }
- ?>
Załączmy definicję klasy do naszego index.php i zobaczmy co się stanie:
- <?php
- // index.php
- require_once(__DIR__.'/classes/Users.php'); // dołączamy klasę Users (z przestrzeni MyApp/Users)
- require_once(__DIR__.'/other_classes/Users.php'); // dołączamy klasę Users (z przestrzeni MyApp/OtherUsers)
- echo \MyApp\Users\Users::SayHello(); // WYNIK: Hi, I'm users class: MyApp\Users\Users
- echo '<br />';
- echo \MyApp\OtherUsers\Users::SayHello(); // WYNIK: Hi, I'm users class: MyApp\OtherUsers\Users
- ?>
Jak widać - tym razem nie spowodowaliśmy błędu załączając dwie identyczne klasy o takich samych nazwach. Stało się to dlatego, iż obie klasy Users istnieją w oddzielnych przestrzeniach nazw.
Dostęp do każdej z nich natomiast uzyskujemy za pomocą podania ich przestrzeni nazw przed właściwą nazwą.
use
Dostęp do klasy lub funkcji z danej przestrzeni nazw można uzyskać również w formie prostrzej, wykorzystując do tego celu dyrektywę use. Na czym to polega? Otóż dyrektywa use podana w kodzie informuje PHP o tym, z jakiej przestrzeni nazw obecnie chcemy korzystamy. Prześledźmy to na przykładzie, modyfikując nasz plik index.php:
- <?php
- // index.php
- use MyApp\Users\Users; // informujemy o używaniu przestrzeni \MyApp\Users
- require_once(__DIR__.'/classes/Users.php'); // dołączamy klasę Users (z przestrzeni MyApp/Users)
- require_once(__DIR__.'/other_classes/Users.php'); // dołączamy klasę Users (z przestrzeni MyApp/OtherUsers)
- echo Users::SayHello(); // WYNIK: Hi, I'm users class: MyApp\Users\Users
- ?>
Jak widać powyżej, poinformowaliśmy PHP o tym, że korzystamy obecnie z przestrzeni \MyApp\Users i dzięki temu w odwołaniu do klasy Users nie musimy już podawać jej przestrzeni nazw.
Dyrektywa use zaczyna obowiązywać od miejsca jej użycia w kodzie.
Co za tym idzie, zapis:
- <?php
- // index.php
- require_once(__DIR__.'/classes/Users.php'); // dołączamy klasę Users (z przestrzeni MyApp/Users)
- require_once(__DIR__.'/other_classes/Users.php'); // dołączamy klasę Users (z przestrzeni MyApp/OtherUsers)
- echo Users::SayHello();
- use MyApp\Users\Users;
- ?>
będzie niepoprawny i spowoduje błąd.
Uwaga: po słowie use nie podajemy backslasha na początku nazwy.
Możemy jednocześnie korzystać z kilku przestrzeni nazw, podając je po przecinku, wg zasady:
- use namespace1, namespace2, namespace3.....;
Aliasy nazw
Istnieje jeszcze jedno ciekawe usprawnienie związane z przestrzeniami nazw, mianowicie aliasy nazw. Wyobraźmy sobie, że stworzyliśmy sobie długą, hierarchiczną przestrzeń nazw:
- MyApp\UsersAccounts\Actions\AddUsers
a w niej dopiero klasę o nazwie Users.
Wywołanie tej klasy w naszym kodzie będzie dość długie, np:
- $user = new \MyApp\UsersAccounts\Actions\AddUsers\Users;
Aby nie pisać tak długich konstrukcji (a przecież mogą być jeszcze dłuższe) wykorzystamy aliasy, czyli chwilowe zastąpienie nazwy naszej przestrzeni za pomocą skróconej nazwy pod jaką będzie ona dostępna w kodzie. Użycie aliasu wygląda następująco:
- use NAZWA_PRZESTRZENI as ALIAS
w naszym przypadku zróbmy więc:
- use MyApp\UsersAccounts\Actions\AddUsers as U;
Od tej chwili do przestrzeni MyApp\UsersAccounts\Actions\AddUsers możemy odwoływać się stosując formę skróconą U. Prześledźmy w praktyce (zakładając, że dołączamy klasę z przestrzeni MyApp\UsersAccounts\Actions\AddUsers):
- <?php
- // index.php
- use MyApp\UsersAccounts\Actions\AddUsers as U; // informujemy o używaniu przestrzeni MyApp\UsersAccounts\Actions\AddUsers pod aliasem U
- require_once(__DIR__.'/classes/Users.php'); // dołączamy klasę Users (z przestrzeni nazw MyApp\Users\Actions\AddUsers)
- echo U\Users::SayHello(); // WYNIK: Hi, I'm users class: MyApp\UsersAccounts\Actions\AddUsers\Users
- ?>
Możemy też stworzyć alias bezpośrednio dla klasy (pamiętajmy, że sposób działa tylko dla klas, dla funkcji i zmiennych nie zadziała!):
- <?php
- // index.php
- use MyApp\UsersAccounts\Actions\AddUsers\Users as Obj; // tworzymy alias do klasy
- require_once('classes/Users.php'); // dołączamy klasę Users (z przestrzeni nazw MyApp\UsersAccounts\Actions\AddUsers)
- echo Obj::SayHello(); // WYNIK: Hi, I'm users class: MyApp\UsersAccounts\Actions\AddUsers\Users
- ?>
__autoload
Jak zapewne już zauważyliśmy, hierarchiczne nazewnictwo przestrzeni nazw przypomina coś co znamy na codzień - strukturę folderów. Spostrzeżenie jak najbardziej poprawne, gdyż właśnie w ten sposób jest to wykorzystywane. Standardem w obecnych frameworkach jest rozmieszczenie plików z klasami w folderach odpowiadającym ich hierarchicznej strukturze. Przykładowo, klasę Users zdeklarowaną w przestrzeni nazw MyApp\UsersAccounts\Actions\AddUsers zapisujemy' w folderze /classes/MyApp/UsersAccounts/Actions/AddUsers/:classes
|__MyApp
| |__UsersAccounts
| | |__Actions
| | |__AddUsers
| | |__Users.php
Jest to bardzo wygodne, proste i efektywne rozwiązanie - pozwala zachować strukturę umiejscowienia naszych plików z klasami odpowiadającą ich nazewnictwu w przestrzeni nazw.
Ponadto jest to pomocne również dla samego programisty, który "z automatu" już wie, gdzie należy szukać definicji danej klasy. Rozwiązanie takie stosuje każda współczesna aplikacja napisana w PHP.
Wykorzystajmy więc teraz funkcję __autoload() do zautomatyzowania sobie procesu dołączania naszych klas. Na początek prześledżmy jak wygląda taki standardowy __autoload() dla zwykłych klas:
- <?php
- function __autoload($class_name)
- {
- require_once(__DIR__.'/classes/' . $class_name. '.php');
- }
- ?>
Usprawnijmy więc jego działanie, tak aby podążał za hierarchią naszej przestrzeni nazw w folderach:
- <?php
- function __autoload($class_name)
- {
- $path_to_class = __DIR__.'/classes/' . str_replace('\\', DIRECTORY_SEPARATOR, $class_name) . '.php';
- require_once($path_to_class);
- }
- ?>
Jak widzimy, wszystkie backslashe są zamieniane na slashe rozdzielające nam ścieżkę do pliku z klasą. I tym sposobem np. wywołanie klasy Users z przestrzeni nazw:
- MyApp\UsersAccounts\Actions\AddUsers
spowoduje, że funkcja __autoload() załaduje nam definicję klasy z pliku:
- /classes/MyApp/UsersAccounts/Actions/AddUsers/Users.php
Jak widać rozwiązanie jest proste i zarazem bardzo efektywne.
Ze swojej strony gorąco polecam stosowanie przestrzeni nazw nawet w mniejszych projektach, aby wyrobić sobie nawyk ich używania, gdyż przy większych projektach korzystanie z przestrzeni nazw jest wręcz konieczne.
Poza tym, w roku 2009 została po raz pierwszy obuplikowana konwencja o nazwie PSR-0, która jasno określa sposoby nazewnictwa przestrzeni nazw i odpowiedniego rozmieszczenia klas w folderach projektu. Konwencja ta ustala pewne reguły, dzięki którym nie występuje kolizja pomiędzy poszczególnymi aplikacjami, szczególnie tyczy to się funkcji __autoload(). Ciekawe rozwiązanie na autoload ma także Composer - polecam się zapoznać.
Więcej o konwencji przeczytać możecie np. tutaj: http://phpedia.pl/wiki/Konwencja_nazewnictwa_PSR-0
No i to tyle, mam nadzieję, że opisałem cały mechanizm prosto i zrozumiale, zachęcam do własnoręcznego eksperymentowania.
Nareszcie kompletny i porządny opis tego problemu. Wielkie dzięki.
OdpowiedzUsuńCieszę się, że się przydało i pozdrawiam :)
UsuńDzięki, bardzo dobry opis! Wreszcie zrozumiałem
OdpowiedzUsuń:) ¡ɯɐıʍɐɹpzod ,ǝuoıusɐɾʎʍ ǝıuɾɐɟ ,ozpɹɐq ıʞǝızp
OdpowiedzUsuńPrzydało się, opisane jak dla chłopa od łopaty. Zrozumiałe i działa :-)
OdpowiedzUsuńice code bro follow me www.gajabwap.blogspot.in
OdpowiedzUsuńCzęsto takie proste wytłumaczenie pozwala bardzo dobrze zrozumieć podstawy programowania i w tym przypadku języka PHP. Wiem, że dla osób pracujących w firmie tj. https://craftware.pl czyli zajmującej się profesjonalnymi aplikacjami, taki poziom jest zapewne podstawowy. Jednak dla osób początkujących z pewnością się przyda i pozwoli wdrożyć się w języki programowania.
OdpowiedzUsuńCóż za marketing xD
Usuńjest super! ale scisnalbym np kod troche
OdpowiedzUsuńW 4 bloku kodu (powyżej opisu namespace) powinno być chyba "// WYNIK: Fatal error: Cannot redeclare class MyClass" zamiast "// WYNIK: Fatal error: Cannot redeclare class Users". Tak czy inaczej super wytłumaczone, świetna robota ;)
OdpowiedzUsuńWreście jasno i przejrzyście opisane, dziękuję że CI się chciało :)
OdpowiedzUsuńPhp Majster: [Php] Namespaces - Przestrzenie Nazw W Php >>>>> Download Now
OdpowiedzUsuń>>>>> Download Full
Php Majster: [Php] Namespaces - Przestrzenie Nazw W Php >>>>> Download LINK
>>>>> Download Now
Php Majster: [Php] Namespaces - Przestrzenie Nazw W Php >>>>> Download Full
>>>>> Download LINK
görüntülüshow
OdpowiedzUsuńücretli show
HHSQO
شركة صيانة افران بالاحساء Apo1Mu5LbR
OdpowiedzUsuń