PSR-4

Dawno temu, za górami, za lasami, żyła sobie grupa ludzi, zwana PHP Framework Interop Group (PHP-FIG). Zajmowali się pierdołami w stylu idiotycznej wojny o to, czy wcięcia powinny być tabem, czy spacjami, a jeśli spacjami, to iloma. Wyniki są, jakie są i pomimo, że nie ze wszystkimi się zgadzam, to jednak wspomniana grupa spłodziła kilka standardów, które wprowadzają pewną spójność w projektach różnych developerów. Jednym z takich standardów był PSR-0.

PSR-0 był piękny, definiował bowiem spójny sposób automatycznego ładowania klas. Nazwa klasy (interfejsu, cechy) była możliwa do przełożenia na ścieżkę i nazwę pliku, co umożliwiło użycie jednego autoloadera we wszystkich projektach (dziękujemy ci, SC Johns… tfu, Composerze). Taki cudowny standard, aż się chce żyć i programować!

Aż tu spadł na nas jak grom z jasnego nieba PSR-4 i siłą wdarł się w świat PHP. Composer szybciutko wprowadził jego wsparcie, a adwokaci nowego standardu zaczęli coraz głośniej nawoływać do porzucenia pięknego PSR-0 na rzecz PSR-4.

O co tyle szumu?

No właśnie, dlaczego niby PSR-4 miałby być lepszy od PSR-0? Czy poprzedni standard nie działał? Działał! Czy ktoś się skarżył? No dobra, może i ktoś się skarżył — inaczej nie powstałby jeszcze lepszy standard. Ale co w zasadzie jest w nim lepszego?

Generalnie chodzi o to, że PSR-0 wymuszało nie do końca sensowną strukturę katalogów. W wielkim skrócie chodzi o to, że:

  1. Każda klasa (interfejs, cecha) ma nazwę odpowiadającą strukturze <Vendor Name>(\<Subnamespace>)*\<Class name>.
  2. Każdy poziom przestrzeni nazw odpowiada kolejnemu zagnieżdżonemu katalogowi, podczas gdy końcowa nazwa klasy odpowiada nazwie pliku zawierającego ją.

Za to w PSR-4 następuje istotna zmiana:

  1. Każda klasa (interfejs, cecha) ma nazwę odpowiadającą strukturze <Vendor Name>(\<Subnamespace>)*\<Class name>.
  2. Każdy poziom przestrzeni nazw poniżej określonego prefiksu odpowiada kolejnemu zagnieżdżonemu katalogowi, podczas gdy końcowa nazwa klasy odpowiada nazwie pliku zawierającego ją.

Zatem w PSR-4 do katalogu bazowego (np. src/) mapujemy nie całą przestrzeń nazw (wymuszając podkatalogi dla każdego jej członu), a jedynie część po prefiksie — np. po części przestrzeni nazw odpowiadającej nazwom vendora i paczki.

W praktyce w PSR-0 wygląda to tak:

  • \Doctrine\Common\IsolatedClassLoader - /Doctrine/Common/IsolatedClassLoader.php
  • \Zend\Acl - /Zend/Acl.php

a w PSR-4 tak:

  • \Doctrine\Common\IsolatedClassLoader - /IsolatedClassLoader.php
  • \Zend\Acl - /Acl.php

Albo weźmy jako przykład jeden z moich projektów, chociażby najświeższy uCrop. Struktura katalogów PSR-0 uCrop wyglądała (albo może wyglądałaby, bo nazwa vendora doszła dopiero po przejściu na PSR-4):

  • src/
    • Mingos/
      • uCrop
        • Exception/
          • ImageFormatException.php
          • MissingLibraryException.php
        • Image/
          • GD.php
          • Imagick.php
        • AbstractImage.php
        • ImageInterface.php
        • uCrop.php

Jak to wygląda w PSR-4?

  • src/
    • Exception/
      • ImageFormatException.php
      • MissingLibraryException.php
    • Image/
      • GD.php
      • Imagick.php
    • AbstractImage.php
    • ImageInterface.php
    • uCrop.php

Plusem jest tutaj wyeliminowanie dwóch zagnieżdżonych katalogów: odpowiadającego nazwie vendora (Mingos) i pierwszemu członowi przestrzeni nazw, odpowiadającej w tym przypadku nazwie paczki (uCrop). Sama przestrzeń nazw nie została zmieniona, a jedynie zmieniło się mapowanie katalogu bazowego.

Tylko tyle? Warto w ogóle zawracać sobie głowę?

Moim zdaniem warto. Czystsza, prostsza struktura katalogów nie gwarantuje co prawda łatwiejszego pisania kodu, czy też wyższej jego jakości, ale na pewno jest czytelniejsza dla człowieka, a to juz coś. Pamiętajmy, że w katalogu vendor/ i tak mamy już nazwę vendora i nazwę paczki. Powielanie tego jest redundancją, która nie dodaje absolutnie żadnej wartości, a jest tylko pozostałością po wymogach zgodności PSR-0.

Poza tym przejście z PSR-0 na PSR-4 może być tak proste jak zmiana zaledwie jednej linijki composer.json. Zamiast tego:

"autoload": {
    "psr-0": {
        "Mingos\\uCrop\\": "src/"
    }
}

wystarczy zmiana na to:

"autoload": {
    "psr-4": {
        "Mingos\\uCrop\\": "src/Mingos/uCrop/"
    }
}

W tej sytuacji nie trzeba nawet zmieniać struktury katalogów, a jedynie mapowanie przestrzeni nazw do katalogu bazowego. Pójście o krok dalej wymaga następującej zmiany w composer.json:

"autoload": {
    "psr-4": {
        "Mingos\\uCrop\\": "src/"
    }
}

oraz zmiany struktury katalogów analogicznie do tego, co podałem wyżej.


Komentarze

Konrad

2015-03-11, 22:30

Dzięki, przydało się :)

Szymon

2015-04-14, 14:49

"Za to w PSR-4 następuje istotna zmiana:

Każda klasa (interfejs, cecha) ma nazwę odpowiadającą strukturze (\)*\."

Wkleiłeś tutaj ten sam przykład co w akapicie o PSR-0

Dominik Marczuk

2015-04-14, 14:52

Pierwszy punkt pozostaje taki sam. Istotna zmiana jest wytłuszczona i znajduje się w drugim punkcie.

Dominik

2015-05-09, 22:18

Na początku miałem własne schematy podczas pisania kodu, jednak PSR umożliwia łatwe czytanie kodu od różnych programistów. Słowem dla mnie same zalety.

Pozdrawiam :)

Kamil

2015-06-11, 18:08

Nieciekawie wytłumaczone ;/ Niestety po dwóch przeczytaniach art. nie mogę nadal pojąć co PSR-4 wnosi do mojego życia. Przykro mi.

Dominik Marczuk

2015-06-11, 18:38

Przykro mi, że tak to widzisz. Chętnie bym poprawił wymagające tego fragmenty, ale niestety nie napisałeś, co dokładnie jest niewystarczająco dobrze wyjaśnione.

Krzysiek

2016-12-31, 16:06

Zgadzam się że temat nie wyjaśnia dokładnie wyższości PSR4 nad PSR0. Napisz co to daje programiście, który będzie analizował kod w PSR-4. Może to wyjaśni sprawę.

Czegoś tutaj brakuje: ale na pewno jest czytelniejsza dla człowieka, a to juz coś. Pamiętajmy, że w katalogu vendor/ i tak mamy już nazwę vendora i nazwę paczki. Powielanie tego jest redundancją, która nie dodaje absolutnie żadnej wartości, a jest tylko pozostałością po wymogach zgodności PSR-0.


Napisz komentarz


Szukaj wpisów


Chmura tagów