Interfejs biblioteki
Pierwsza zmiana, jaka rzuca się w oczy, to przejście na PHP5. Nowy Smarty nie zadziała na wersjach 4.x. Autorzy na razie nie potrafią określić, jakie będą minimalne wymagania dla PHP5, gdyż "piątka" przez pięć lat też nie próżnowała i PHP 5.3.0 to dużo bardziej rozbudowany produkt od PHP 5.0.0 wydanego w 2004 roku.
Na pierwszy rzut oka wygląda, że zmiana numerka i przejście na piątkę niewiele zmieniło w kwestii API biblioteki. Autorzy starali się zachować maksymalną kompatybilność wsteczną, dlatego w dalszym ciągu można pracować z systemem, posługując się jedynie "klasą do wszystkiego", co z ideą obiektowości ma niewiele wspólnego. Jednak oprócz tego, pojawiły się klasy Smarty_Data oraz Smarty_Template. Pierwsza z nich służy do definiowania zakresów widzialności, czyli zbiorów zmiennych, które używane są w szablonie. Nie jesteśmy już skazani na zmienne globalne, które przy bardziej zaawansowanych układach rodziły dużo problemów, niemniej wciąż można ich używać. Smarty_Template reprezentuje natomiast pojedynczy szablon, który podlega parsowaniu.
Wada projektu API ujawnia się, gdy zajrzymy do źródeł. Plik Smarty.class.php ma tylko 18 KB wielkości? Fajnie, lecz wszystko przez to, że główna klasa to w dużej mierze funkcja __call(), która w locie doładowuje potrzebne funkcje. Jeśli ktoś korzysta z aktywnego podpowiadania składni w edytorze, będzie klnąć na czym świat stoi, bo oczywiście przy takiej budowie edytor nie pokaże mu już ani dokumentacji, ani nawet faktu istnienia większości metod. Podstawowe metody, jak assign() czy assign_global() są ładowane z klasy bazowej Smarty_Internal_Data, tyle że jest ona umieszczona w osobnym pliku i nie ma siły, żeby nie była ona załadowana. Jest to pewna niekonsekwencja. Skoro autorzy przyjęli zasadę, że plik może zawierać więcej niż jedną klasę, przecież można było wszystko podzielić tak, by rzeczywiście system mógł uruchomić całą podstawową funkcjonalność z jednego pliku. Ostatecznie operacje dyskowe trochę kosztują.
Usprawnienia API oceniam ogólnie mimo wielu niekonsekwencji na plus, lecz zastanawia mnie usilne pragnienie trzymania się wstecznej kompatybilności. Ostatecznie jest to wersja "3", bez żadnego jeszcze ważniejszego numerka. Co innego język programowania, a co innego biblioteka. Ktoś, kto napisał aplikację na Smarty 2 przecież i tak nie będzie mieć zbytniej korzyści z przesiadki, jeśli nie będzie wykorzystywać nowej funkcjonalności, dlaczego więc męczyć się z anachronizmami, kiedy taka zmiana to znakomita okazja do oczyszczenia kodu?
Rodzaje szablonów
Począwszy od wersji 3.0 Smarty dopuszcza tworzenie szablonów w czystym PHP (po uprzednim aktywowaniu tej opcji). Takie szablony nie podlegają kompilacji, lecz są z miejsca dołączane do wykonywanego skryptu. Rodzaj szablonów wybiera się dość prosto:
$smarty->display('php:szablon.php');
O zaletach czy wadach takiego podejścia nie będę się rozwodzić, zapamiętując jedynie, że taka opcja jest i jeśli ktoś chce, może z niej zawsze skorzystać. Możliwe jest także wczytanie szablonu ze zmiennej:
$smarty->display('string:Hi universe: {$hiUniverse}');
Prawdę mówiąc na razie nie potrafię sobie wyobrazić praktycznego zastosowania, ale zwiększoną elastyczność doceniam.
Składnia szablonów
Cóż, rewelacji nie ma. Smarty 3 jest wstecznie zgodny składniowo ze Smartym 2, a składnia została jedynie rozszerzona o nowe możliwości. Dalej bawimy się klamerkami, kompilator dalej nie rozumie tego, co jest w statycznym tekście, dalej mamy mnóstwo średnio użytecznych funkcji, które przede wszystkim doskonale replikują funkcjonalność czystego PHP. Z nowości, autorzy chwalą się zabudowaniem w kompilatorze prawdziwego leksera i parsera wyrażeń, dzięki czemu wreszcie możliwe jest pisanie rzeczy w stylu $foo = strlen($text) * 17 - bar($zmienna). Oczywiście nie wspomniano o tym, że parę systemów szablonów, wliczając w to OPT 1.x oraz 2.x, ma takie coś już od lat...
Szkoda, że autorzy w dalszym ciągu pozostają odporni na jakiekolwiek głosy rozsądku ze strony programistów, którzy powtarzają w kółko: po co nam język szablonów, który przede wszystkim naśladuje PHP, i to nieudolnie? Ludzie, którzy fascynują się składnią Smarty'ego i im podobnych systemów po pewnym czasie zrażają się do niej, gdy widzą, że prawie dokładnie tak samo pisze się w czystym PHP, a na dodatek później zamiast wieszać psy na Smarty'm, wieszają je na systemach szablonów jako takich. A za bezmyślność twórców płacą dodatkowo Bogu ducha winne porządne systemy szablonów mające nieco wyższe ambicje...
Nowości w funkcjach
Niestety tutaj, podobnie jak w wielu innych miejscach muszę opierać się na szczątkowych informacjach. W paczce z biblioteką jest jedno prymitywne demo, niedokończony README i tyle. Żadnych testów jednostkowych, żadnej namiastki dokumentacji. Dlatego mogę mieć tylko nadzieję, że nie walnąłem gdzieś poważnej gafy.
Najważniejszy dodatek to pojawienie się dziedziczenia szablonów. Formuła jest nawet zbliżona do tego, co mamy np. w Open Power Template 2, czyli dziedziczenie statyczne:
{extends file='base.tpl'} {block name='list'} <li>{$list[i].var1}, {$list[i].var2}</li> {/block}
plik bazowy:
<?xml version="1.0" ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html> <head> <title>Strona WWW</title> </head> <body> <h1>Strona WWW</h1> <ol> {section name=i loop=$list} {block name='list'}{/block} {/section} </ol> </body> </html>
Bloki, czyli odpowiedniki snippetów z OPT, działają na zasadzie przeklejenia kodu źródłowego w wybraną lokalizację podczas kompilacji, dzięki czemu taki blok od razu integruje się np. z sekcją. Bloki nie muszą koniecznie nadpisywać wcześniej zdefiniowanych bloków o identycznej nazwie, lecz np. doklejać się na koniec już zdefiniowanych, jeśli dodamy do nich atrybut append. Możliwe jest także składanie dynamiczne po stronie skryptu:
$smarty->display('extends:grandchild.tpl|child.tpl|parent.tpl');
Interesująco rysuje się także możliwość definiowania funkcji bezpośrednio w szablonie, które mogą mieć argumenty i które możemy później osadzać w różnych miejscach:
{function name=item element} <li>{$element.var1}, {$element.var2}</li> {/function} <ol> {section name=i loop=$list} {item element=$list[i]} {/section} </ol>
W miejsce sekcji pozostawionych jedynie dla kompatybilności, Smarty 3 wprowadza nowe oraz ulepsza stare pętle: for, foreach oraz while, które teraz już wyjątkowo mocno przypominają swoje odpowiedniki w PHP i rodzą pytanie, jaki jest sens stosowania takiego języka szablonów jako nakładki i alternatywy dla PHP...
Wydajność
Arghhh! To, co zrobił zespół Smarty z wydajnością swej biblioteki, woła o pomstę do nieba. Już Smarty 2 nie grzeszył szybkością, natomiast tutaj mamy istną optymalizacyjną tragedię. Ponieważ przygotowuję aktualizację mojego benchmarku systemów szablonów, postanowiłem włączyć do niego także trzecią odsłonę, aby porównać sobie, jak wypada ona wydajnościowo i powiem krótko: jest gorzej, niż źle. Optymalizacja sekcji leży całkowicie. Uzyskiwane czasy są o 50% gorsze, niż w dwójce. Użycie rekomendowanego foreach jedynie zbliża się do czasów sekcji z "dwójki". W ogóle użycie w szablonie jakiejkolwiek zmiennej działa wolniej, bo postanowiono, że w dostępie pośredniczyć będzie dodatkowa metoda. Praktycznie jedynie w ładowaniu biblioteki oraz w teście na treść pętli zdefiniowaną oddzielnie "trójka" wypadła lepiej, przy czym w tym ostatnim jedynie dzięki pojawieniu się funkcji. W życiu nie podejrzewałem także, że mimo zastosowania takiego samego mechanizmu obsługi dziedziczenia szablonów, Smarty 3 może być tutaj pięciokrotnie wolniejszy niż Open Power Template.
Podsumowanie
Niestety, potwierdziły się moje obawy. Mimo poczynienia wielu pozytywnych kroków, Smarty 3 rewolucją nie jest i najprawdopodobniej nie będzie, co naturalnie najprawdopodobniej nie przełoży się na popularność. Jeśli ktoś nie lubił Smarty'ego, wersja 3.0 wcale nie sprawi, że zmieni swoje nastawienie. Jeśli ktoś nie lubił języków szablonów a'la Smarty, także nagle ich lubić nie zacznie. Wszystko przez to, że ogrom nowości w trójce to rzeczy, które już od dawna można spotkać w wielu innych systemach szablonów, a sam język szablonów nie oferuje wiele ponad to, co można znaleźć w czystym PHP. Do tego dochodzi usilne pragnienie trzymania się wstecznej kompatybilności nawet za cenę przeniesienia złych i kiepskich praktyk programistycznych.
Gwoździem do trumny jest tragiczna wydajność wykonywania szablonów. Oczywiście zawsze można wykręcić się sianem, że to tylko wersja beta i wszystko się może zmienić do czasu finalnego wydania, ale bez przesady. Jeśli problemy z wydajnością zaszyte są w architekturze biblioteki i fundamentalnych założeniach projektowych, to nie da się ich tak od ręki usunąć. Tymczasem patrząc na kody skompilowanych szablonów, mam wrażenie, że mają one właśnie taki charakter... cache może dużo pomóc, ale nie jest to remedium na wszystkie bolączki. A przecież wystarczyło trochę pomyśleć - przykład Open Power Template'a pokazuje, że wydajność wcale nie musi kłócić się z możliwościami.






Napisał matipl w piątek, 4 grudnia 2009 o 21:28
Świetnie, tyle lat mieli i nawet o krok się zespół nie posunął. Ale co się dziwić, jeśli na co dzień możliwe, że pracują z Smarty nie widząc konkurencji...