Gdyby ktoś dwa dni temu zapytał mnie o system szablonów w PHP prawdopodobnie użyłbym wszystkich poznanych środków perswazji, aby „wyleczyć” go z takich bibliotek. Zarzekałem się, że PHP zostało stworzone do wypełniania HTML-a danymi i że tak naprawdę nic więcej nie potrzeba. A to, że w głębi duszy przeklinałem każdy dzień spędzony przy tworzeniu szablonów niespecjalnie wpływało na moją opinię. Nie wiem, jakaś męka w imię wyższej sprawy czy coś.
W każdym razie ten stan minął po przeczytaniu ostatniego wpisu na blogu Fabiena Potenciera (twórca symfony, a także wielu świetnych komponentów które znajdują u mnie zastosowanie na co dzień – o tym jednak w innym wpisie). Czy jesteście zwolennikami ułatwiania sobie życia takimi metodami, czy też uważacie za bezsensowną naukę kolejnego „języka”, naprawdę gorąco zachęcam do przeczytania.
Wstęp
Problemy z systemami szablonów zna raczej większość osób, które są już na etapie „nie, bo mi się to do niczego nie przyda, bo PHP może więcej i w podobnym stylu”. Przecież nie ma znaczenia czy frontendowiec będzie się musiał nauczyć Smarty, czy ułamka PHP. Czasu mu to zajmie tyle samo. A wygląd? Czy jest jakaś większa różnica między pisaniem <?php ?> a {{ }}? Okej, tutaj się można spierać czas pracy itd, ale ja nie o tym. A do tego narzut podczas wykonywania skryptu, konieczność przystosowania kodu pod konkretny silnik.
Stosując PHP możemy szybko wyeliminować długie pisanie poprzez zastosowanie <?= ?>, to w końcu jedna linijka w pliku konfiguracyjnym. Czasem się da, czasem nie. Możliwość jest i trudno się z tym sprzeczać. Wielu takie rozwiązanie preferuje, ja jednak sugerowałbym domyślne i zalecane znaczniki. Istotną częścią każdego kodu jest przenośność (chociaż sam ładuję PHP 5.3 przy każdej nadarzającej się okazji). Niektórzy jednak zalecają stosowanie takiego podeścia, bo przecież krótkie znaczniki powstały po to, by je stosować. Z drugiej jednak strony Zend Framework i PEAR wręcz ich zabraniają.
Wielu w tym momencie uznaje temat systemów szablonów za wyczerpany. Tak myślałem i ja. A że tak nie jest uświadomił mnie dopiero wyżej wspomniany post. Argumentów wielu nie ma, ale bardzo często mogą one przesądzić o decyzji zastosowania takiego a nie innego szablonu.
Piaskownica
Po pierwsze tzw. „sandbox”. I tu przypuszczam, że jeżeli ktoś się spotkał z tą potrzebą to systemu szablonów już używa. O co chodzi? Chodzi o to, żeby część wizualna działała w swoim wyodrębnionym środowisku, tylko z danymi, które przekazał programista, bez dostępu do wnętrzności. Po co? Dochodzimy do sedna. Wyobraźmy sobie system blogowy pokroju WordPress.com (nie mam na myśli systemu blogowego samego w sobie [na którym działa i PHP Geek], ale usługę blogów dla „mas”). Wielu preferuje takie rozwiązanie ze względu na większe bezpieczeństwo, regularne backupy, natychmiastowe wsparcie, czy po prostu wygodę. Usługa taka sprawia jednak wiele problemów jej twórcom, system musi być zabezpieczony na wszelkie możliwe sposoby. Spróbujmy dać bloggerom możliwość edycji skórek strony. Z tego co się orientuję akurat ten serwis tego nie umożliwia, no ale na potrzeby przykładu wybrałem ten najpopularniejszy. I co? Mamy dać możliwość wykonywania własnego kodu PHP? Nigdy w życiu! To w linii prostej prowadzi do zagłady. Systemy szablonów pozwalają ograniczyć możliwości designerów do niezbędnego minimum. A to jest jak najbardziej pożądane!
Ponowne wykorzystanie kodu
Kolejnym argumentem jest dziedziczenie szablonów. Nie ma najmniejszego problemu z implementacją tego w czystym PHP… dla nas. Każcie grafikowi zakodować coś takiego:
<!-- base.html -->
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
{% block head %}
<link rel="stylesheet" href="main.css" />
{% endblock %}
</head>
<body>
{% block content %}{% endblock %}
</body>
</html>
<!-- index.html -->
{% extends "base.html" %}
{% block head %}
{{ block.super }}
<link rel="stylesheet" href="main.css" />
{% endblock %}
{% block content %}
Index content
{% endblock %}
Nie jest to niezbędna konstrukcja ale niewątpliwie ciekawa i wiele ułatwiająca. Mam nadzieję, że każdy załapał naturalną zbieżność z dziedziczeniem klas i wytłumaczenie jest zbędne.
Filtrowanie, formatowanie
Na koniec zostawiłem sobie filtrowanie danych, usuwanie z nich szkodliwych fragmentów. Dlaczego, skoro to jedno z ważniejszych zagadnień? Otóż dlatego, że dobre nawyki każą nam wykonywać takie rzeczy w kontrolerze a nie widoku. Niestety czasem trzeba. Choćby w wypadku, gdyby poprzedni programista pozostawił po sobie źle zaprojektowany kod i praktycznie nie ma realnych szans na większy refactoring (bo się po prostu nie opłaca). Każdy z nas wie, że wbudowane funkcje PHP wyglądają jak wyglądają. Dopatrzeć się można całkiem ścisłej konwencji nazewniczej, naprawdę… problem w tym, że co kategoria to te konwencje są inne (i jeszcze raz, naprawdę są zastosowane pewne schematy w nazewnictwie i przekazywaniu parametrów w powiązanych ze sobą funkcjach). Kolejne wydania wprowadzają nowe, obiektowe podejście. W każdym razie spróbujcie wymagać od grafika znajomości tych wszystkich niuansów. Sam miewam z tym problemy. Załóżmy jednak, że nasz kod jest dobrze napisany, a problem filtrowania zupełnie obcy. Zostają jednak wszelkie modyfikacje na potrzeby danej podstrony, a tych w zależności od projektu może być cała masa (pierwszy lepszy przykład: formatowanie daty, zmiana ścieżki względnej na bezwzględną). I tu systemy szablonów również mogą wprowadzić pewne ułatwienia. Niewielkie bo niewielkie, ale jeżeli już wprowadzamy nowe rozwiązanie warto z niego czerpać garściami.
Wybór odpowiedniej biblioteki
Podsumowując chciałem skupić się na wyborze i problemach dotyczących pojedynczych bibliotek. Najpopularniejszy Smarty 2.x nie jest pisany obiektowo co u mnie skreśla go w pierwszej chwili. Natomiast Smarty 3, który jest tuż za rogiem, według Potenciera są wolne (? no ale skoro to twórca „wolnego” symfony to to musi być naprawdę krowa, w każdym razie nigdy tego systemu nie lubiłem). Rozwiązaniem może być Twig. Twig jest bardzo lekką alternatywą dla Smarty, posiada niemal identyczną składnię i wszystkie wyżej wymienione funkcje. Projekt niedawno został przejęty przez Fabiena (może moje ubóstwienie jest chore, ale tym samym wróżę przedsięwzięciu sukces). Posida naprawdę dopracowane API co pozwala na bezproblemowe rozszerzanie i modyfikowanie zachowania tego skryptu. Dla mnie bomba.
Niestety, korzystanie z nawiasów klamrowych ma jedną podstawową wadę – klamerki. Rozwalą kompletnie podgląd tworzonej strony, niektóre edytory mogą psioczyć. Osobiście dla mnie nie jest to problemem. Tnąc szablon piszę czysty HTML, klamerki stosuję dopiero na etapie podpinania pod system. A wtedy problem znika. Niemniej jednak, dla osób którym mogą przeszkadzać jest inna, bardzo dobra alternatywa: PHPTAL.
P.S. Zdaję sobie sprawę, że podjąłem nieco wrażliwy temat, może to nieco rozrusza społeczność i przyczyni się do komentowania wpisów na PHP Geek. :)
Trochę mało przykładów. Co chociażby z OPTem? A XSLT? Strasznie po łebkach opisujesz ten temat ;)
Oj… Zyx mnie zabije, że zapomniałem o rodzimym OPT. Prawdę mówiąc nie zajmowałem się nim nigdy, nie chciałbym więc wprowadzić zbędnego zamieszania.
XSLT jest dobre, to fakt. Ale chyba niezbyt przyjazne dla przeciętnego webdesignera. Do tego konieczność stosowania Tidy, bo nie daj Boże coś nam się w drzewie DOM powali. Znowu, może jest fajna biblioteka do tego, nigdy mnie nie ciągnęło do tego typu rozwiązań. Kłopotliwe to trochę i co, jeśli nie zamierzamy korzystać tylko ze standardów W3C.
Po łebkach? Celem tego wpisu nie było wymienić wszystkich popularnych/sensownych systemów szablonów, bo na ten temat jest już miliony tektów w internecie. Chciałem tylko wskazać kilka ważnych, często zapominanych kwestii podczas rozważania nad sensem stosowania tego typu rozwiązań. Bo nie da się ukryć, że dziedziczenie szablonów jest rzeczą bardzo przyjemną, a sandboxing wręcz zalecaną (ileż ja razy widziałem ludzi, którym się nie chciało przeglądać API i na chama podpinali się do bazy danych…). Moim celem było skłonić do ponownego rozważenia tematu i dyskusji.
A Twig? No cóż, elastyczność tego systemu, w połączeniu z komponentem Templating (oba od Sensio Labs) mnie urzekła.
Więcej na temat Smarty 3 na przykład tutaj: http://technology.mediovski.pl/2009/11/02/smarty-3/
Składnia Smarty’ego to jedno z gorszych rozwiązań, bo co ona tak naprawdę daje w porównaniu z czystym PHP? Nie dość, że nic, to jeszcze wprowadza kupę ograniczeń. Najgorsze jest to, że Twig robi dokładnie ten sam błąd. Potencier najpierw rozwodził się, jakie to brzydkie są PHP i inne systemy szablonów, że każdą głupotę trzeba kodować ręcznie, a później sam zaprezentował dokładnie to samo, które niby ma być lepsze tylko dlatego, że udostępnia znaczniki „extends” oraz funkcję „escape”.
Nawiasem mówiąc benchmark Potenciera można o kant czterech liter rozbić. Udowadnia on jedynie, że Twig bardzo szybko potrafi wyświetlić 10000 razy pod rząd ten sam szablon w obrębie tego samego żądania HTTP – rzecz średnio przydatna z praktycznego punktu widzenia. A wszystko przez to, że Potencier zapomniał o izolacji każdej iteracji, co Twig bardzo sprytnie wykorzystuje do zawyżenia swego wyniku :).
Jeśli chodzi o rozdział „Formatowanie, filtrowanie”. Tutaj mocno nie zgadzam się z Tobą. Przede wszystkim w prawdziwym MVC kontroler się nawet nie powinien zbytnio interesować, jakie właściwie dane widok pobiera z modelu. Twórcy 99% frameworków zapominają, że droga danych z modelu do widoku nie musi, a nawet nie powinna prowadzić przez kontroler, który powinien co najwyżej wybrać model, wybrać widok i skonfigurować je do pracy ze sobą. Dlaczego właśnie model lub kontroler mają zajmować się filtrowaniem, kiedy to, czy dany kod jest niebezpieczny, zależy w dużej mierze od tego, gdzie go wyświetlimy? Wstawka HTML jest niebezpieczna tylko, gdy ją wstawiamy w kod HTML. Generując zapytania SQL (backup) czy dokument PDF (wersja do wydruku), jest ona zupełnie nieszkodliwa, natomiast inne kawałki kodu mogą nieźle nabruździć. Co więcej, nawet w obrębie kodu HTML inne filtrowanie należy zastosować, gdy wyświetlamy jakąś wartość jako atrybut, a inne – gdy jako tekst między znacznikami. Ja bym właśnie kod z filtrowaniem w kontrolerze uznał za źle napisany :P.
A OPT… moim zdaniem warty uwagi z takiego powodu, że staram się w nim nie popełniać tego błędu ze składnią, który popełniają inni. Czy mi się to udaje, pozostawiam ocenę innym.
Potencier głównie wniósł świetnie zaprojektowany kod. Bez żadnego problemu jesteś w stanie wymienić i rozszerzyć większość dostarczonej funkcjonalności, gdy tylko zajdzie taka potrzeba. Nowo przybyłym ze Smarty daje domyślne zachowania.
Faktycznie, kontroler nieco nam zmienił formę w stosunku do pierwotnych założeń MVC. Tu racja jest całkowicie po Twojej stronie. Trochę zapatrzyłem się na popularne frameworki. Jednak kontroler powinien zintegrować ze sobą warstwę modeli i widoków co wcale nie przekreśla idei filtrowania w tym miejscu. Co więcej, uważam że to kontroler powinien zdecydować jak przefiltrować dane w zależności od oczekiwanego rezultatu (HTML, PDF, SQL) zamiast zrzucać tę odpowiedzialność na warstwę widoku. Zwłaszcza, jeżeli mamy do czynienia z widokami dostarczanymi od zewnętrznych osób.
Według mnie składnia, tak jak pisałem w podsumowaniu, jest rzeczą osobistą. Chociaż tu może dopuściłem się małego nadużycia przedstawiając jedynie styl znany z Smarty w poście o tak ogólnym tytule.
A benchmark… cóż, benchmarki z góry można uznać za wskazujące bardzo niewiele. W jednym projekcie będzie więcej pętli, w innym filtrowania. W jednym ładowany będzie jeden plik, w innym użyte będzie dziedziczenie i kompozycja. Kiepski wyznacznik „lekkości”. A mimo to dalej uważam Twiga za lekkiego. Robi to co ma robić bardzo dobrze, realizuje absolutne minimum a w razie potrzeby udostępnia minimalistyczne API którym zmodyfikujemy praktycznie wszystko, co może nas interesować.