Czasami mam taki nostalgiczny nastrój tęsknoty za „starymi dobrymi czasami”, kiedy spod moich palców wyszło całkiem sporo kodu C++. Naprawdę lubię ten język i uważam za dobrą decyzję twórców wielu języków, aby lekko mówiąc „ściągnąć” składnię z tego języka do własnego rozwiązania. PHP w pewnym sensie jest jednym z nich, także istnieje pewne powiązanie pomiędzy obiema technologiami [poza faktem, że sam interpreter jest napisany w C]. W dzisiejszym wpisie chciałbym pokazać jedną rzecz, mały lukier składniowy, który był obecny w C++, a trochę brakowało mi go w PHP.
Fotografia: trekkyandy, CC-BY-SA.
PHP: Skrócony zapis kwalifikatorów widoczności metod klasy.
Aktualizacja: oczywiście niech Was nie zmyli tytuł, chodzi o atrybuty klasy, nie metody. Zagapiłem się podczas pisania, ech. ;]
Tym, którzy reagują alergicznie na zwrot „kwalifikator widoczności” śpieszę z wyjaśnieniami, że chodzi o modyfikatory public, protected i private. Artykuł Wikipedii o C++ tłumaczy wszystko w bardzo przystępny sposób:
Within a class, members can be declared as either public, protected, or private in order to explicitly enforce encapsulation. A public member of the class is accessible to any function. A private member is accessible only to functions that are members of that class and to functions and classes explicitly granted access permission by the class („friends”). A protected member is accessible to members of classes that inherit from the class in addition to the class itself and any friends.
Co zatem jest takiego fajnego w C++, czego nie ma w PHP? Otóż, możemy zapisać kod klasy w następujący sposób:
class Foo
{
public
one,
two;
protected
bar,
baz;
private
lst,
oth;
void three(void);
public:
void one(void);
int two(int param);
protected:
char bar(void);
float baz(int one);
private:
void lst(void);
void oth(void);
};
Gdzie jest zysk? Zarówno w jednokrotnym wykorzystaniu poszczególnych kwalifikatorów widoczności, pozwalających na pogrupowanie metod / atrybutów klas, jak i w pewnym sensie w czytelności kodu – słówka „public”, „protected” i „private” górują nad deklaracjami funkcji, dzięki czemu lista metod jest łatwiejsza do przyswojenia dla oka programisty. Co jest nam w stanie zaoferować PHP dla podobnego przypadku? Spójrzmy:
class FooP
{
public $one;
public $two;
protected $bar;
protected $baz;
private $lst;
private $oth;
function three() {}
public function one() {}
public function two($param) {}
protected function bar() {}
protected function baz($one) {}
private function lst() {}
private function oth() {}
};
/* Powyższy kod zawiera przykład małego dysonansu pomiędzy działaniem programów w C++ i skryptów PHP. Jak na pewno zauważyliście, obie klasy Foo zawierają po jednej metodzie bez żadnego modyfikatora. Problem polega na tym, że w C++ domyślnie zostanie przypisany kwalifikator private, a w PHP public – przykład w dalszej części artykułu. */
Jak widać, ilość słów kluczowych klasyfikatorów to prawie połowa kodu. Oczywiście w rzeczywistych przypadkach nie wygląda to aż tak źle, ponieważ metody mają swoje ciała, dobrą praktyką jest także skomentowanie odpowiednich partii kodu blokami PHPDoc, także jest to w jakiś sposób akceptowalne. Czy nie da się jednak z tym nic zrobić?
Studiując kod frameworka symfony, a dokładnie jednego z wbudowanych helperów Doctrine trafiłem w pewnym momencie na dosyć ciekawy zapis:
/**
* sfDoctrine pager class.
*
* @package sfDoctrinePlugin
* @subpackage pager
* @author Jonathan H. Wage <jonwage@gmail.com>
* @version SVN: $Id: sfDoctrinePager.class.php 28897 2010-03-30 20:30:24Z Jonathan.Wage $
*/
class sfDoctrinePager extends sfPager implements Serializable
{
protected
$query = null,
$tableMethodName = null,
$tableMethodCalled = false;
Nie chwaląc się zbytnio mogę powiedzieć, że oko programisty od razu wypatrzyło pewną anomalię – brak klasyfikatora widoczności w deklaracji atrybutów klasy. W tym momencie zaświeciła się lampka – skoro oni tak mogą, to ja też! Spójrzmy na poniższy listing:
class Foo
{
public
$var = 'var',
$bar = 'bar',
$baz = 'baz';
protected
$one = 1,
$two = 2,
$three = 3;
private
$list = array(0, 1),
$hash = array(2, 3),
$obj = array();
function goo() {}
public function baz() {}
public function bar() {}
protected function one() {}
protected function two() {}
private function obj() {}
private function hash() {}
}
W ten sposób całkiem przyjemnie zadeklarowaliśmy sobie dziewięć atrybutów klasy, używając zaledwie trzech modyfikatorów. Dla konkretnych przypadków będzie jeszcze lepiej, ponieważ zazwyczaj wszystkie dane klasy będą opatrzone modyfikatorem private albo protected. Niestety dla metod to nie działa, jeśli spróbujemy zrobić coś takiego:
(...)
public
function baz() {}
function bar() {}
protected
function one() {}
function two() {}
private
function obj() {}
function hash() {}
(...)
To niestety wszystkie metody bez wyraźnego wskazania kwalifikatora widoczności zostaną przypisane do publicznego API klasy. Możemy to zobaczyć na poniższym listingu:
echo '<pre>';
$foo = new Foo();
$fooReflection = new ReflectionClass('Foo');
$fooMethods = $fooReflection->getMethods();
foreach($fooMethods as $key => $method)
{
$modifiers = $method->getModifiers();
echo
$modifiers.' '.$method->getName()
.($modifiers & ReflectionMethod::IS_PUBLIC ? ' PUBLIC' : '')
.($modifiers & ReflectionMethod::IS_PROTECTED ? ' PROTECTED' : '')
.($modifiers & ReflectionMethod::IS_PRIVATE ? ' PRIVATE' : '')
.($modifiers & ReflectionMethod::IS_STATIC ? ' STATIC' : '')
.($modifiers & ReflectionMethod::IS_FINAL ? ' FINAL' : '')
."\n";
}
Wynik:
65792 goo PUBLIC 65792 baz PUBLIC 65792 bar PUBLIC 66048 one PROTECTED 65792 two PUBLIC 66560 obj PRIVATE 65792 hash PUBLIC
Co kończy dowód. ;]
Dzisiejszy „research” można uznać za zakończony. Zapraszam do dyskusji w komentarzach, gdzie na pewno dostanę kubeł zimnej wody na głowę za propagowanie „złych praktyk programistycznych”. A może faktycznie uznacie to za ciekawą opcję? ;]
Warto przeczytać.
Trwa ładowanie…
Przydatne dla tych, co mają wątpliwości.
Nie nazwałbym tego złą praktyką programistyczną, ale taka ilość tekstu do opisania takiej prostej rzeczy ma swoją nazwę, a Ty sam dobrze wiesz jaką ;)
Oddam ksiażkę za darmo #3 – (o)programowanie
Aha, no i zapomniałeś dodać, że takie coś:
public function two(int $param) {}
bez zdefiniowanej klasy „int” to raczej sobie nie podziała na obecną chwilę.
Oddam ksiażkę za darmo #3 – (o)programowanie
Tekstu dużo nie ma, bez przesady – listingów jest o wiele więcej. Chociaż z drugiej strony 826 słów to też nie jest mało [edytor WordPressa liczy też "słowa" w kodzie, także w pewnym sensie jestem usprawiedliwiony]. ;]
Inna sprawa, że wstawienie tylko kawałka kodu i napisanie „słuchajcie, możecie tak pisać” też do końca nie ma sensu, bo nie ma żadnej wartości dla Czytelnika – musi wszystko ogarnąć sam. Jak już tłumaczę, to kompletnie. Widzisz, problem jest też taki, że jak coś opisuję na blogu, to jest mała szansa, że ludzie tacy, jak Ty, czy batman się jakoś specjalnie zdziwią. Za dobrzy jesteście. ;]
PHP: Skrócony zapis kwalifikatorów widoczności metod klasy.
Jasne, dzięki za wypunktowanie. Jak sam widzisz, przeoczyłem to podczas przerabiania listingu C++ na PHP. Resolved, Fixed. ;]
PHP: Skrócony zapis kwalifikatorów widoczności metod klasy.
Spróbuj teraz taki kod, gdzie 5 właściwości jest niejako upchniętych w jedną deklarację jakoś udokumentować. ;) PHP przyjęło taką, a nie inną składnie i nie ma sensu z nią walczyć. Lepiej wyjdzie się zmieniając przyzwyczajenia (albo język :]).
Swoją drogą mogliby w końcu pozbyć się słówka `function` przy deklaracji. Nie wiem jaki jest cel jego istnienia.
a ja tak troszke w innej kwestii – bowiem bardzo lubie PHP ktore wciaga jak narkotyk :) czy ktos z Was pamieta moze „afere” sprzed 7-8 lat gdy w jakiejs polskiej szkole zrobila sie afera ze „dzieci na zajeciach biora PHP”? rodzice podniesli wrzask ze to jakis nowy narkotyk otumania mlodziez. pamietam to z wiadomosci na ktorejs ze stacji TV, ale nie udalo mi sie nic a nic znalezc na necie, youtubie i innych. a jak wiadomo, czego nie ma na guglu to po prostu nie istnieje :D a to pamietam istnialo :) no chyba ze moje wspomnienia sa calkowicie zfixowane i falszywe. a blog panie Kowalczyk bardzo fajny, chyba jedyny polskojezyczny ktory codziennie sprawdzam czy nie ma nowego wpisu. fajnie byloby wiecej o WordPressie, a mniej o Symphony, ale to takie moje osobiste preferencje.
Ależ ja nie próbuję z nią wcale walczyć. Dalej będę pisał:
i tak dalej. Podany przez Ciebie przykład z PHPDoc jest bardzo dobrym przykładem, dlaczego jednak tego lukru składniowego nie powinno się stosować w poważnym kodzie. Aczkolwiek dla mnie to była nowość, że tak właśnie można zrobić, ponieważ wcześniej nie widziałem, żeby ktokolwiek zastosował taki zapis.
Co do słówka „function” – nie wiem, czy pozbycie się tego słowa kluczowego będzie dobrym pomysłem – zauważ, że w PHP nie ma tak jak w C++ typu zwracanego, a więc w jakiś sposób lepiej się czyta kod, w którym masz zaznaczone, co jest „function”. Poza tym, niewiele języków porzuciło tego typu zapis – nawet bardzo skrótowy Python ma swoje def. ;]
PHP: Skrócony zapis kwalifikatorów widoczności metod klasy.
Co może być wewnątrz deklaracji klasy? Właściwość (zapisana $jakZmienna), stała (z dosyć charakterystycznym „const”) oraz metoda (z nawiasami, argumentami i klamerkami). Naprawdę wystarczająco dobrze rzuca się ona w oczy, szczególnie, że mówimy o sensownie napisanym kodzie gdzie najpierw są właściwości, a później metody.
Nie znałem tego, dobre. ;] Znalazłem link: http://forum.skryptoteka.pl/niebezpieczne-narkotyki-php-t3061.html – jak widać nie w Polsce, a w USA [Topeka]. Wstawiam, ku pamięci:
To co, witamy w gronie narkomanów? Zaciągnijmy się PHPem. ;]
Dziękuję poza tym za miłe słowa, staram się jak mogę, żeby zapewnić jak najlepsze materiały dla Was. Co do tematyki – ja jestem programistą raczej skryptów dedykowanych, także mało czasu poświęcam na grzebanie w rozwiązaniach gotowych, takich, jak WordPress, czy Joomla. Aczkolwiek jeśli masz jakieś ciekawe problemy do rozwiązania, zapraszam do kontaktu – takie zgłoszenia traktuję priorytetowo. ;]
A jeśli chodzi o codzienne sprawdzanie nowych wpisów – polecam kanał RSS – pomarańczowa ikonka na górze. ;] Wpisy według aktualnego harmonogramu ukazują się zawsze we wtorki, piątki i niedziele.
PHP: Skrócony zapis kwalifikatorów widoczności metod klasy.
Powodem może być też znaczne skomplikowanie gramatyki, jaką trzeba byłoby obsłużyć bez słowa kluczowego function. Inną sprawą jest to, że ja się nawet mogę z Tobą zgodzić, bo szczerze mówiąc nie przeszkadza mi aktualna składnia, ale nie widzę też przeciwwskazań przed takimi zmianami, aczkolwiek to tylko nasze dwa głosy. Napisz na php.internals – chłopaki z grupy developerskiej rozważą Twoje propozycje. ;]
PHP: Skrócony zapis kwalifikatorów widoczności metod klasy.
Wracając do tematu. Fajnie zostało to rozwiązane w Ruby, gdzie widoczność metody można określić na dwa sposoby. Można dodać słowo public/protected/private między metodami. Wówczas wszystkie metody znajdujące się poniżej tego słowa będą posiadały widoczność nadaną przez ów słowo, np.
Widoczność można również określić w postaci listy metod oddzielonych przecinkami. W takim przypadku określenie widoczności metody musi się odbyć za definicją metody (najlepiej na końcu pliku)
PHP i Windows – nowa seria artykułów na MSDN
Zapis do PHPDoc:
class Foo { public /** * @param something else */ $foo = array(), /** * @param something else */ $bar = array(); }Korzystam od jakiegoś czasu z takiego zapisu, aczkolwiek nie sprawdzałem go w phpdoc, lecz wydaje mi się że powinien zadziałać. Mój IDE mi w podpowiedzianej składni, pokazuje opis który stosuje w komentarzach phpdoc, to i chyba phpdoc powinno sobie dać radę. Stosuje tego typu zapis od jakiegoś czasu, jak tylko ujrzałem właśnie chyba w symfony taki zapis. Ciągłe pisanie kwalifikatorów jest niekiedy męczące ;)
Moim zdaniem, świetnie jest to rozwiązane w JS czy Scali, tam ustawiamy modyfikator dla (uwaga) „slotu”, pod który można przypisać zarówno zmienną jak i funkcję. Behold:
function Foo(){ var bar = 1; //prywatna zmienna var baz = function(){ return 1; } //prywatna metoda this.faz = function(){ return 2; } //publiczna metoda }Nowości z php.internals – PHP 5.3.7RC1 dostępne do testowania
Problem w tym, że w takim JavaScripcie właściwie nie masz klasyfikatorów dostępu, bo wszędzie jest obecny jedynie zasięg funkcyjny albo blokowy [ES Harmony]. Także nie do końca odpowiada to słówkom public, protected [które raczej nie ma w JS sensu] i private.
Chociaż z drugiej strony tego typu rozwiązanie jest ciekawą alternatywą. ;]
PHP: Skrócony zapis kwalifikatorów widoczności metod klasy.
Tak, to daje radę, aczkolwiek zauważ, że wtedy słówko public nie odnosi się praktycznie do niczego i stoi samo nie zauważone. Można je właściwie przypadkowo usunąć jako zbędny fragment kodu. ;] Jeśli korzystamy z PHPDoc, to o wiele lepszym rozwiązaniem będzie raczej pisanie klasyfikatorów dostępu razem ze zmiennymi, które są opisywane.
PHP: Skrócony zapis kwalifikatorów widoczności metod klasy.
Czyli pierwszy sposób właściwie określa blok widoczności jak w C++. Drugi zaś to ciekawy pomysł, ale wydaje mi się, że powtarzanie klasyfikatorów dostępu jest lepszym pomysłem niż późniejsze powtarzanie listy większości / wszystkich metod. Chyba, że jest jeszcze coś, o czym nie wiem, a jest wygodne w Ruby. ;]
PHP: Skrócony zapis kwalifikatorów widoczności metod klasy.
Powoli zbieram się do napisania wstępu o Ruby, więc za jakiś czas na pewno znajdziesz coś interesującego w tym języku.
PHP i Windows – nowa seria artykułów na MSDN
Chodziło mi konkretnie o ten przypadek – czy jest jakiś zysk z zastosowania tego drugiego sposobu w Ruby, o którym nie wiem, a Ty nie napisałeś?
Co do samej serii o Rubym, to bardzo chętnie poznam coś nowego, bo ostatnie moje spotkanie z tym językiem zakończyło się stwierdzeniem „nigdy więcej”. ;]
PHP: Skrócony zapis kwalifikatorów widoczności metod klasy.
Szczerze mówiąc, mi to nie przeszkadza, bo zwracam dużą uwagę na wcięcia i jeżeli widzę że ktoś nie stosuje tych wcięć, to odechciewa mi się czytania takiego kodu. Jak kto uważa zresztą ;)
W sumie wszystko sprowadza się do konwencji przyjętej w konkretnym projekcie.
Też moje pierwsze podejście do Ruby nie było zbyt udane. Ostatnio się jednak przekonałem do tego języka, a nawet zacząłem dłubać w nim pierwszy projekt.
PHP i Windows – nowa seria artykułów na MSDN
Wiesz, prywatne preferencje bardzo często wygrywają z ogólnie przyjętą konwencją. Ogólnie rzecz biorąc jeśli piszesz klasyfikator oddzielnie, to mnie by strasznie denerwowało słowo kluczowe stojące samotnie bez większego sensu w kodzie. Aczkolwiek zgódźmy się, że to jednak wspomniana prywatna preferencja, bo z kolejnego flamewara na ten temat nic nie wyniknie. ;]
BTW. Na niesformatowany kod mam już od pewnego czasu lekarstwo – [Ctrl] + [Alt] + [L] w PHPStormie. ;]
PHP: Skrócony zapis kwalifikatorów widoczności metod klasy.
Skoro nie ma zysku, o którym nie wiem, to trzymałbym się jednak tradycyjnego przypisywania klasyfikatora do danego elementu. To chyba najlepsza opcja z punktu widzenia utrzymania kodu – modyfikacja jednego słówka w kodzie nie powoduje problemów z innymi.
Jeśli przekonałeś się do Rubiego, to szczerze gratuluję – gdyby nie Redmine, to ja nie zostawiłbym na nim suchej nitki. ;] Aczkolwiek teraz masz okazję przekonać mnie do niego. Czekam niecierpliwie na wpisy. ;]
PHP: Skrócony zapis kwalifikatorów widoczności metod klasy.
Klasyfikatorów jako słówek oczywiście nie ma, natomiast ich funkcjonalność da się zrealizować – tak jak to podałem w przykładzie i jest to wg mnie mniej „magiczne”. Co do protected, to wrzuciłem kiedyś wpis na bloga (chyba nawet komentowałeś) jak zrobić wielodziedziczenie, kwalifikatory dostępu i metadane w JS. Co więcej, w którejś nowszej wersji języka wraz z Object.create() zostały dodane całkiem podobne ficzery.
Nowości z php.internals – PHP 5.3.7RC1 dostępne do testowania
Faktycznie, pisałeś, a ja komentowałem, także zwracam honor. Rzadko używam czystego JavaScriptu, także muszę uważać, co piszę, żeby nie publikować jakichś bzdur. ;]
Pozwolę sobie podlinkować: http://blog.wsoczynski.pl/2011/02/18/wlasny-model-obiektowy-w-javascripcie/ .
PHP: Skrócony zapis kwalifikatorów widoczności metod klasy.
Wg mnie, „czysty” JS jest jednym z najbardziej niedocenianych i niezrozumianych języków wszech czasów, podobnie pewnie jak Lisp i Smalltalk, dlatego warto go czasami używać i to najlepiej po stronie serwera ;)
Nowości z php.internals – PHP 5.3.7RC1 dostępne do testowania
Ależ ja wcale nie uważam, że on jest zły – po prostu dla mnie to tylko jedno z narzędzi, a główna praca odbywa się właśnie po stronie serwera, gdzie w moim przypadku prym wiedzie PHP. Po stronie frontendu muszę tylko zrobić, co do mnie należy i szybko wracam do siebie. ;] Zgadzam się, że jest on często niezrozumia(ł|n)y, ale z drugiej strony czego oczekiwać po języku, który ma takie możliwości. ;]
PHP: Skrócony zapis kwalifikatorów widoczności metod klasy.
Nie wiedziałeś, że można tak robić z atrybutami klas? Hmmm… natomiast co do metod… na litość, nie pisz takich rzeczy publicznie, bo jeszcze ktoś to przetłumaczy na angielski, albo puści twórcom PHP i jeszcze to zaimplementują :). PHP, jeśli już od jakiegoś języka zrzyna, to od C, w żadnym wypadku od C++ i od tego tworu imperium zła też niech się trzyma z daleka. Przecież jest poważna różnica między klasą w C++, gdzie nagłówek możesz mieć oddzielnie, a implementację oddzielnie, a PHP, gdzie przy takim zapisie człowiek by się w ogóle nie połapał, co jest czym, gdy modyfikator widoczności będzie mieć 2000 linijek wyżej.
O stylu kodowania
Nie wiedziałem, teraz już wiem. ;] Szczerze mówiąc, jest to pierwszy raz, kiedy stykam się z tego typu kodem „publicznie”, a trochę źródeł różnych frameworków i bibliotek czytałem – najwyraźniej ten sposób zapisu nie zyskał sobie zbyt wielu fanów. Dlatego nie traktuję tego jako jakiegoś poważnego błędu w sztuce programistycznej. Nie wiem, jak mam interpretować Twoje „hmmm”, bo chyba jednak uznałeś to za istotny brak.
Zgadzam się z Tobą jednak co do wykorzystania tej możliwości w kodzie i trochę żałuję, że nie ma w PHP możliwości oddzielenia deklaracji od implementacji klasy. Gdyby nie okienko „outline” w wielu IDE, to nie można byłoby się połapać w ogóle, co w danej klasie jest.
Porównując jednak jakikolwiek język z C, czy C++ uważam, że w wielu przypadkach można użyć ich zamiennie, albo napisać „C/C++”, ponieważ trudno, żeby ktokolwiek ściągnął ideę, czy składnię klas z C, dlatego wymagam od Czytelnika przynajmniej minimalnej wiedzy o tym, co rzeczone języki sobą reprezentują.
Doctrine: Funkcje SQL w zapytaniach do bazy danych.
To „Hmmm…” to pewnie stąd, że cała masa języków takie coś obsługuje:
Czemu PHP miałoby być tutaj wyjątkiem? :)
Jeszcze co do rozdzielania deklaracji i implementacji. W PHP już to masz – i to w znacznie lepszej formie – pod postacią interfejsów.
Chyba powoli do mnie dociera, że się trochę z tym wpisem wygłupiłem. ;] Jak teraz przeiterowałem w myślach przez składnie wszystkich języków, jakie znam, to faktycznie, ze zmiennymi zawsze można było coś takiego zrobić. Z drugiej strony w PHP nikt zmiennych w ten sposób nie deklaruje, a naprawdę pierwszy raz spotykam się z tego typu zapisem w kodzie produkcyjnym – gdzie bym nie spojrzał, kwalifikatory widoczności zawsze były powtarzane. Skromna obrona, ale zawsze coś.
PHP nie obsługuje rozdzielenia implementacji i deklaracji klasy. Obecność interfejsów niczego tutaj nie zmienia, ponieważ mogą one zawierać tylko metody publiczne, co w zdecydowanej większości przypadków jest niewystarczające. Ja nie mówiłem o wydzieleniu do oddzielnego tworu samego publicznego API klasy, tylko do konkretnego zdefiniowania całości „deklaracji” klasy, dzięki czemu tak jak w C++ mamy całkiem przyjemny plik pokazujący, czego mamy się spodziewać w implementacji.
Doctrine: Funkcje SQL w zapytaniach do bazy danych.
Ale tu też wracamy do problemu, że PHP musiałby być zupełnie innym językiem, wykonywanym w zupełnie inny sposób, aby taki zapis miał w ogóle jakiś sens. C i C++ ma bardzo ważny powód, dla którego deklaracja jest oddzielona od implementacji. W PHP ten powód nie występuje, czegoś podobnego nie masz też np. w Javie.
O edytorach kodu
Zgadzam się ze stwierdzeniem, że PHP nie musi mieć tego typu możliwości. Kwestia tego, że gdyby istniała możliwość „wyciągnięcia” deklaracji klasy gdzieś w odosobnione miejsce bez wykorzystywania różnych funkcjonalności oferowanych przez IDE, wydaje mi się, że byłoby to z korzyścią dla programistów PHP.
Zdaję sobie też sprawę z tego, że każdy może mieć w tej sprawie inne zdanie dlatego nie uważam tego za coś bardzo istotnego, raczej za swojego rodzaju ciekawostkę, aczkolwiek gdyby ktoś „ważny” w świecie PHP taką propozycję wysunął, to głosowałbym za.
197, 198, 199… 200 wpisów na blogu!
Pingback: Linkdump #47: WordPress, Jaśnie Panie! « Tomasz Kowalczyk