Ostatnio – po długiej przerwie – wszedłem na blog Zyxa wierząc, że znajdę tam coś ciekawego do przeczytania. Nie zawiodłem się. Znalazłem dwie recenzje najpopularniejszych w Polsce frameworków PHP tj. Symfony 1.4 okiem Zyxa i Zend Framework także okiem Zyxa.
Ostatnio przerzuciłem się na Pythona i Django, a wcześniej przez co najmniej dwa lata budowałem aplikacje w oparciu o Kohanę, jednak od czasu do czasu – w tak zwanym międzyczasie – próbowałem też coś sklecić na ZF i polubić Symfony dlatego też orientuję się przynajmniej pobieżnie w ich konstrukcji. Artykuły Zyxa i własne doświadczenie skłoniły mnie do kilku refleksji na temat ogólnej konstrukcji frameworków, sensu użycia ORM-ów a także systemów szablonów.
Architektura
Dobrze zaprojektowana struktura plików to podstawa porządnego frameworka. Każdy szanujący się projektant stara się utrzymać porządek w swoim projekcie i lubi bez zbędnego zastanawiania się wiedzieć gdzie ma czego szukać. Kohana umożliwia tworzenie modułów z których każdy może mieć swoje kontrolery, modele, widoki, pliki konfiguracyjne, a nawet biblioteki. Układ katalogów jest rozbudowany. Kaskadowość z jednej strony umożliwia elastyczność np. nadpisanie konfiguracji domyślnej, konfiguracją specyficzną dla danego modułu. (w Kohanej nie tylko konfigurację można nadpisać ale też „wymienić” klasy z zachowaniem ich dotychczasowej nazwy. Założenie to w PHP wyklucza proste użycie dziedziczenia dlatego też w Kohanie 2 było to zrobione po chamsku z użyciem evala. W wersji 3 tego frameworka mechanizm ten został bardziej elegancko zaimplementowany). Z drugiej strony przy bardziej rozbudowanych projektach człowiek zaczyna się gubić nie pamiętając niekiedy skąd się wzięła bieżąca wartość danej zmiennej konfiguracyjnej. Zaczyna się szukanie. Pół biedy, kiedy struktura katalogów jest płaska, ale jeśli musimy przeklikać się przez kilka poziomów zagłębień staje się to męczące.
Nie wiem kto na to wpadł, aby każdą klasę trzymać w osobnym pliku, nie wiem też kto wymyślił aby nazwa klasy odzwierciedlała położenie pliku w strukturze katalogów (patrz ZF, PEAR), ale doprowadziło to do powstania całej masy katalogów i podkatalogów i jeszcze większej liczby plików, z których niektóre np. zawierają jedynie jednolinijkową definicję wyjątku. W Django w poszczególnych app-sach znajdziemy z reguły pliki (__init__.py, views.py, models.py, urls.py, admin.py, tests.py) i to w 90% przypadków wystarcza. W Kohanie jeden moduł to kilka katalogów. Python jakoś obywa się bez autoloadera i kiedy czytam to co wyżej sam napisałem zaczynam rozumieć dlaczego. Na marginesie tylko wspomnę, że w związku z wprowadzeniem namespace-ów, sposób organizacji klas w plikach PHP się zmieni.
PHP w wersji piątej poszło wyraźnie w kierunku „magi”. Wszystkie poprzedzone podwójnym podkreśleniem metody są bardzo wygodnym rozwiązaniem i osobiście bardzo je lubię ale nadmiar czarów daje się we znaki w chwili kiedy zachodzi potrzeba prześledzenia procesów zachodzących w aplikacji. Debugowanie przesyconych „magią” klas jest znacznie utrudnione przede wszystkim przez niejednoznaczne komunikaty błędów. Dodatkową wadą użycia metod magicznych jest to, że dynamiczne settery i gettery nie będą podpowiadane przez żadne IDE typu NetBeans, czy Eclipse mimo w sumie dobrze zrealizowanej w nich funkcji podpowiadania składni.
Inną kwestią jest uniwersalność kodu. Klas Kohanej można używać tylko w ramach tego frameworka, z kolei biblioteki eZ Components lub Zend Frameworka można używać niezależnie lub w ramach zupełnie innej platformy. Sam wielokrotnie w projektach opartych na Kohanej sięgałem do wybranych komponentów Zend Frameworka. Klasy ZF są jak to zauważył Zyx dopracowane i przetestowane i jedyną ich wadą jest z reguły to, że są zbyt dobre. Zamiast w najprostszy sposób realizować banalną funkcjonalność, autorzy poszczególnych bibliotek prześcigają się w wymyślaniu wariantów zastosowań i sposobów użycia. Tak właśnie z noża powstał szwajcarski scyzoryk – fajny ale do smarowania chleba najlepszy jest zwykły nóż kuchenny. Ponoć w nowej wersji ZF ma to ulec zmianie.
ORM
Symfony zniechęciło mnie do siebie przede wszystkim ORM-em. Do Propela nawet nie startowałem, natomiast z Doctrine-m walczyłem jakiś czas. Niestety na etapie kiedy przeprowadzałem moje eksperymenty Doctrine było mocno niedopracowane w związku z czym wielokrotnie wzbudzało to moją irytacją. Z tego co pamiętam nie mogłem nawet dowolnie wskazać miejsca generowania modeli tylko było to z góry narzucone. Dlatego między innymi zarzuciłem pomysł „dokooptowania” Doctrine do Kohany.
Na tę chwilę o wiele większe doświadczenie mam z ORM-em Django uważanym za wzorcowy. Jest on o wiele bardziej dopracowany niż wyżej wspomniane PHP-owe odpowiedniki w związku z czym da się z nim w miarę sprawnie pracować. Mimo to pozostaję sceptyczny w kwestii użycia tego typu narzędzi. Opanowanie django-wego ORM-a kosztowało mnie sporo czasu, a do tego nie wyobrażam sobie aby można było efektywnie go używać nie znając wcześniej SQL-a. Przy prostych konstrukcjach jest miło i przyjemnie przy bardziej skomplikowanych użycie obiektów rzutuje przede wszystkim na wydajność, do czego przyczyniają się w dużej mierze bajery typu „lazy loading”. Zmiana ORM-owej konstrukcji w celu optymalizacji i tak poprzedzana jest napisaniem zapytania w SQL więc człowiek nie ma żadnej korzyści z użycia ORM-a, która by rekompensowała nakłady poniesione w celu jego poznania.
O wiele bardziej przemawiają do mnie składacze zapytań SQL takie jak Zend_Db – choć jak większość bibliotek Zenda jest ona przedobrzona. Mając prostą klasę do konstruowania zapytań SQL – taką która ułatwia, nie ogranicza i bynajmniej nie zmusza do uczenia się zamienników w stylu „annotate” zamiast „group by”, wspartą wzorcem projektowym DAO można stworzyć prosty w utrzymaniu, debugowaniu, elastyczny, wcale nie pracochłonny i przede wszystkim odseparowany od warstwy logiki i widoku mechanizm dostępu do bazy danych.
ORM-y mogą być w moim mniemaniu jedynie dodatkami używanymi np. do automatycznego generowania backedu (czytaj panelu administracyjnego), ale w żadnym wypadku nie powinny być jedynym ani nawet głównym sposobem pracy z danymi pobieranymi z bazy danych.
Szablony
Symfony a także Zend Framework, jak równierz Kohana używają natywnych szablonów PHP. Podobnie jak Zyx zgadzam się, że można znaleźć lepszą alternatywę dla warstwy widoku. W odróżnieniu jednak od niego nie stawiałbym na Open Power Template’a, które uważam za trudne i pracochłonne w użyciu (z samej swojej xml-owej natury), a do tego mniej elastyczne i intuicyjne niż kontestowane przez niego Smarty.
System szablonów Smarty w wersji 3 zostało wzbogacone o kilka ciekawych funkcjonalności jak np. dziedziczenie szablonów i możliwość nadpisywania bloków kodu znane z szablonów Django. W przeciwieństwie do ORM-ów uważam, że warto potrudzić się troszkę ze składnią Smarty i pogodzić się z narzutem na wydajność bo w zamian można zyskać szereg usprawnień jakich jesteśmy pozbawieni w przypadku użycia gołych szablonów PHP. Wspomnę tylko wygodniejszą składnię czy zaimplementowany cache, ale też zwiększone bezpieczeństwo wymuszone ograniczoną dostępnością funkcji PHP wewnątrz szablonu czy też encapsulacją zmiennych. Smarty 3 umożliwia też użycie natywnych szablonów PHP ale jest to alternatywa nie ograniczenie.
Do niedawna pracowałem jeszcze z Django w wersji 9.6, w której szablony w stosunku do wersji 1.2 były mocno ograniczone. Obecne szablony Django w wersji 1.x przypominają Smarty (albo na odwrót jak ktoś woli) – z tym, że Smarty 3 są po prostu lepsze. Jestem fanem składni i możliwości języka Python mimo to na polu szablonów PHP u mnie wygrywa.
Reasumując
We wpisie tym nie dążyłem bynajmniej do porównania frameworków między sobą, a już na pewno się do porównywania rozwiązań stosowanych w języku PHP czy Python. Poruszyłem trzy zagadnienia, które w mniejszym lub większym stopniu dotyczą wszystkich z wyżej wymienionych platform. Wspomniałem o tym, że jestem zwolennikiem rozwiązań uniwersalnych ale elastyczność nie może być realizowana kosztem przejrzystości i prostoty. Dałem też do zrozumienia, że mapowanie na siłę relacyjnej bazy danych do postaci obiektów jest raczej wyrazem fundamentalizmu ideologicznego niż pragmatycznym działaniem. Z kolei w użyci systemów szablonów dostrzegłem wiele zalet.