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:
[code]MyClass {}[/code]
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:
[code]
<?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__;
}
}
?>
[/code]
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:
[code]
<?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
?>
[/code]
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:
[code]
<?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
?>
[/code]
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:[code]namespace NAZWA_PRZESTRZENI;[/code]
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:
[code]
<?php
namespace MyApp1;
// blok kodu dla MyApp1
namespace MyApp2;
// blok kodu MyApp2
namespace MyApp3;
// blok kodu dla MyApp3
?>
[/code]
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ę:
[code]
<?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__;
}
}
?>
[/code]
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():
[code]
<?php
// index.php
require_once(__DIR__.'/classes/Users.php'); // dołączamy klasę Users
echo Users::SayHello(); // WYNIK: Fatal error: Class 'Users' not found!
?>
[/code]
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:
[code]
<?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
?>
[/code]
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:
[code]\MyApp\Users\[/code]
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:
[code]
<?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__;
}
}
?>
[/code]
Załączmy definicję klasy do naszego index.php i zobaczmy co się stanie:
[code]
<?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
?>
[/code]
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:[code]
<?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
?>
[/code]
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:
[code]
<?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;
?>
[/code]
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:
[code]use namespace1, namespace2, namespace3.....;[/code]
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:[code]MyApp\UsersAccounts\Actions\AddUsers[/code]
a w niej dopiero klasę o nazwie Users.
Wywołanie tej klasy w naszym kodzie będzie dość długie, np:
[code]$user = new \MyApp\UsersAccounts\Actions\AddUsers\Users;[/code]
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:
[code]use NAZWA_PRZESTRZENI as ALIAS[/code]
w naszym przypadku zróbmy więc:
[code]use MyApp\UsersAccounts\Actions\AddUsers as U;[/code]
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):
[code]
<?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
?>
[/code]
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!):
[code]
<?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
?>
[/code]
__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:
[code]
<?php
function __autoload($class_name)
{
require_once(__DIR__.'/classes/' . $class_name. '.php');
}
?>
[/code]
Usprawnijmy więc jego działanie, tak aby podążał za hierarchią naszej przestrzeni nazw w folderach:
[code]
<?php
function __autoload($class_name)
{
$path_to_class = __DIR__.'/classes/' . str_replace('\\', DIRECTORY_SEPARATOR, $class_name) . '.php';
require_once($path_to_class);
}
?>
[/code]
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:
[code]MyApp\UsersAccounts\Actions\AddUsers[/code]
spowoduje, że funkcja __autoload() załaduje nam definicję klasy z pliku:
[code]/classes/MyApp/UsersAccounts/Actions/AddUsers/Users.php[/code]
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