Temat: Wady vs Zalety OPT
Jakub L. -> wiem, że w PHP istnieje pętla, ale niektóre problemy są ze swojej natury rekurencyjne i nic na to nie poradzę. Pytasz o przykład takich algorytmów:
- przechodzenie po drzewach z zachowaniem hierarchii.
- algorytm BFS/DFS.
Choćbyś nie wiem, co robił, nie zaimplementujesz ich za pomocą samej pętli, gdyż są one ze swej natury rekurencyjne. Rekurencję można emulować przy pomocy pętli, ale w ogólnym przypadku zawsze będzie Ci potrzebna do niej jakaś dodatkowa struktura danych (najczęściej stos, choć czasem da się użyć kolejkę), bo
takie są prawa natury i tyle.
Ponadto - pisałem już o tym wcześniej -
OPT nie używa jawnej rekurencji, ale ją emuluje właśnie przy pomocy pętli +
SplQueue/
SplStack, zatem przepełnienie stosu PHP może zajść tylko wtedy, gdy to Twój skrypt najpierw zajął cały, a później wywołał OPT :P.
Jedyne miejsce, gdzie jest jawna rekurencja, to instrukcja
opt:root include, która była dodawana jako rozwiązanie tymczasowe w końcowej fazie rozwoju i nie chciałem robić już rozpierduchy w głównym algorytmie kompilatora, narażając się na błędy. Jednak zostało to naprawione w wersji OPT 2.1.
Jeśli chodzi o Twój przykład, to jest to dla mnie normalna rekurencja, tyle że emulowana... dokładnie tak, jak to robi OPT i tak, jak opisałem powyżej. Chyba że się umówimy, że takie coś nie jest rekurencją, ale wtedy w OPT także nie ma rekurencyjnych algorytmów poza tym jednym przypadkiem opisanym powyżej.
OPT nie może działać poprzez zwykłe zgłaszanie początku i końca znacznika z prostego powodu: instrukcje OPT nie wykonują jedynie prymitywnego podpinania kawałków kodu PHP pod znaczniki, ale często potrzebują też pomanipulować trochę strukturą drzewa.
Artur Świerc -> dokładnie, właśnie o to chodzi, że
Open Power Template działa inaczej. 90% kodu kompilatora powstało właśnie po to, byś mógł po prostu powiedzieć w szablonie
co chcesz uzyskać, a nie
jak to ma działać oraz by Twój szablon był maksymalnie przenośny między projektami, a nawet językami programowania. Owszem, w szablonie są dostępne instrukcje
opt:if,
opt:foreach czy
opt:for, ale może poza pierwszą się praktycznie do niczego nie przydają. Mój zamysł opiszę na przykładzie:
Przypuśćmy, że chcesz wyświetlić dwie zagnieżdżone listy. W PHP i Smartym zrobisz po prostu zagnieżdżone pętle
foreach. Jednak jest pewien problem, bo musisz samodzielnie poinformować PHP/Smarty, że lista zagnieżdżona ma brać dane z elementu listy nadrzędnej, a co więcej - musisz dokładnie i samodzielnie zakodować takie połączenie. Nie jest to może trudne, ale jak takich list masz do napisania 100, albo musisz później szablon przerobić/skopiować to w inne miejsce, będziesz się ciął, zwłaszcza gdy się okaże, że w tym innym miejscu programista zapodaje szablonowi dane o nieco innej strukturze.
W Open Power Template masz instrukcję
opt:section, która jest rodzajem inteligentnej pętli. I jedyne co musisz zrobić, to umieścić jedną taką instrukcję w drugiej:
<opt:section name="nadrzedna">
{$nadrzedna.info}
<opt:section name="podrzedna">
{$podrzedna.info}
</opt:section>
</opt:section>
OPT sam się "domyśli", że te sekcje mają być połączone, a co więcej - wie, jak je połączyć za Ciebie, i to na więcej niż jeden sposób. Te "sposoby" nazywają się formatami danych, które wybierasz po stronie skryptu, po prostu podając, którego z nich chcesz użyć. Jeśli będziesz robić kiedyś refaktoring i stwierdzisz, że lista elementów zamiast obiektem, ma być tablicą i to połączoną na inny sposób, wtedy zmieniasz format danych i rekompilujesz szablon bez zmiany ani jednej linijki kodu. Jeśli nie chcesz łączyć sekcji, albo chcesz ją połączyć z inną niż domyślna, masz atrybut
parent, który pozwala Ci to zrobić, w dalszym ciągu ukrywając szczegóły implementacyjne.
Co więcej, ponieważ nie zajmujesz się szczegółami implementacyjnymi w szablonie, OPT może Ci je dodatkowo zoptymalizować tak, jak nie zrobiłbyś tego w czystym PHP ze względu na konieczność zachowania czytelności kodu. I to robi -
opt:section iterujący po tablicy jest szybszy, niż
foreach w PHP.
Inny przykład: rendering mapy serwisu. W czystym PHP albo musisz sam bawić się w rekurencyjny algorytm, emulować rekurencję lub korzystać z ciężkich obiektowych helperów. W OPT robisz:
<opt:tree name="map">
<opt:list><ul> <opt:content /> </ul></opt:list>
<opt:node><li><a href="parse:$map.url">{$map.title}</a> <opt:content /> </opt:node>
</opt:tree>
Parę linijek i pełna kontrola nad wyglądem listy w naturalny sposób, a OPT już sam zadba nie tylko o to, by z tego faktycznie renderowało się drzewo, ale żeby renderowało się w sposób optymalny.
Takich rzeczy jest dużo więcej (np. wbudowany mechanizm renderingu formularzy - komponenty), a w wersji 2.1 zrobiłem rewolucję nawet w instrukcjach warunkowych, które potrafią teraz dużo więcej, niż zwykły
if oraz
switch z PHP. Prosty przykład:
<opt:if>
<opt:condition test="$foo eq 1"> condition 1 </opt:condition>
<opt:condition test="$foo is either 2 or 6"> condition 2</opt:if>
<p>Wasz PHP tego nie potrafi</p>
<opt:condition test="$foo is between 3 and 5"> condition 3</opt:condition>
</opt:if>
Inny przykład: mamy szablon wyświetlający informacje o profilu, ale problem jest taki, że w serwisie jest 5 typów profili i niektóre informacje się różnią. Aby było śmieszniej, ostatni z wyświetlanych elementów musi mieć doklejoną klasę CSS "last". Można skopiować to samo 5 razy i dostosować, ale później musisz pamiętać, by coś poprawiać w kilku miejscach naraz. Można też obudować wszystko
ifami, co nie jest czytelne. A można też użyć
opt:switch. Takie coś będzie możliwe już niebawem w OPT 2.1 (w trakcie implementacji):
<opt:switch test="$user.type">
<!-- drogi OPT, masz dokleić ten atrybut do ostatniego elementu, jaki faktycznie bedzie wyswietlany -->
<opt:prepend to="last"><opt:attribute name="str:class" value="str:last" /></opt:prepend>
<li>Name: {$user.name}</li>
<li opt:condition="@value eq 1">Age: {$user.age}</li>
<li opt:condition="@value is either 2 or 3">Points: {$user.points}</li>
<li>Something else: {$user.something}</li>
<li opt:condition="@value is 2">Stuff: {$user.stuff}</li>
</opt:switch>
Dodatkowo, ponieważ szablony są dokumentami XML, mamy na dzień dobry sprawdzenie poprawności kodu HTML pod względem składniowym.
To jest zamysł stojący za tworzeniem OPT. Również uważam, że tworzenie nowego języka szablonów tylko po to, by zamieniać if na if i foreach na foreach nie ma absolutnie żadnego sensu. Dlatego w języku OPT staram się zaprezentować zupełnie inną filozofię projektowania szablonów i wykorzystać maksymalnie fakt, że tworzę nowy język i jestem ograniczony jedynie wyobraźnią i własnymi założeniami projektowymi. A czy ta filozofia jest lepsza czy gorsza - to już pozostawiam do oceny każdemu z osobna. Jestem pewien co do jednego - wciąż jest sporo miejsc, które można ulepszyć.