Flush concept - strumieniowy system szablonów, wprowadzenie
21 marca, 2008
Nowoczesne Internetowe systemy generowania treści - popularnie CMS, projektuje się na bazie wzorca Model-View-Controller. Pokrótce mówiąc aplikacje dzieli się na trzy główne komponenty; bazę danych, interfejs użytkownika oraz logikę aplikacji. O zaletach takiego rozwiązania nie mam zamiaru tutaj wspominać, bo napisano o tym już wiele.
Smarty engine
Warto natomiast przyjrzeć się metodzie generowania interfejsu poprzez wspomnianą logikę aplikacji. Smarty - projekt developerów zza oceanu, chyba uplasował się w tym miejscu jako synonim – chodź istnieją inne podobne projekty (praktycznie o tej samej mechanice a innej implementacji).
Webdesigner przygotowuje szablon w którym umieszcza specjalne tagi, które dzięki kompilatorowi zostają podmienione na dane przygotowane poprzez logikę aplikacji. Mechanika tego procesu sprowadza się do prostego założenia, tagi zostają podmienione na kod który po załadowaniu przez parser PHP zostanie wykonany. Dla zobrazowania z poniższego fragmentu szablonu, Smarty
<p>{$foo}</p>
zapisze do pliku cache poniższy ciąg, a następnie wczyta i wykona jako skrypt PHP.
<p><?php echo $this->_tpl_vars['foo']; ?></p>
Jak nie trudno się domyślić, jeżeli wcześniej uzupełniliśmy tablice _tpl_vars o element "foo" (metodą assign()) jej zawartość tutaj zostanie wydrukowana.
Wady i zalety
Zalet jest sporo. Raz sparsowany szablon nie trzeba ponownie przetwarzać - wystarczy wczytać plik cache i wykonać. Poniekąd mamy zysk wydajności (dlaczego poniekąd to za chwile), bo ostatecznie "wykonuje" szablon parser PHP (binarka, nie skrypt). Ale...
Takie rozwiązanie pociąga za sobą kilka konsekwencji. Wystarczy, aby część algorytmu przed wywołaniem metody display wykonywała się "trochę dłużej", aby docelowy klient musiał odczekać ów czas do otrzymania pierwszego bajtu generowanego dokumentu.
Trzeba także wspomnieć, o zarządzaniu pamięcią - czyli o czymś czego w ogóle nie ma. Wartości zmiennych (albo wartości elementów tablicy _tpl_vars) są trzymane aż do końca wykonywania skryptu - po czym parser PHP je opróżnia. Ale czy trzymanie kilkaset bajtów przez cały proces kompilacji ma sens, jeżeli będą one użyte tylko raz w szablonie? Zwłaszcza, że serwery obsługują w jednym momencie kilka - nawet do kilkaset, klientów.
W ten sposób spora ilość pamięci jest zarezerwowana bez uzasadnienia. Co skutkuje zmniejszoną jej ilością dla innych procesów, oraz z kolei mniejszą wydajnością (wyżej wspomniane; poniekąd). Tak dla jasności, powyższa teza nie jest potwierdzona żadnymi testami lecz wynika z dedukcji autora.
O pętlach...
Stosowanie systemu szablonów w takiej formie (albo inaczej; mechanice) jak Smarty, pociąga za sobą pewien – nie przez wszystkich, świadomy fakt. Może tym razem najpierw, posłużę się przykładem z popularnego systemu for jakim jest phpBB w wersji 3.0.
Chodź implementacja systemu szablonów jest inna w porównaniu ze Smarty, to mechanika jest taka sama (już o tym wspominałem, wiem). Tak też zamiast $this->_tpl_vars['foo'] mamy $_postrow_val['foo'], za $smarty->assign() mamy $template->assign_var() itd.
// Plik: viewtopic.php, v 1.513// [56-59]// Skrypt wykonuje zapytanie do bazy danych w celu pobraniu// postów z określonego topiku. Zwraca handler wyniku zapytania.$sql = 'SELECT forum_id FROM ' . TOPICS_TABLE. "WHERE topic_id = $topic_id";$result = $db->sql_query($sql);// [922-1114]// Pierwsza obróbka danych. Skrypt przekopiowuje zwrócone// rekordy do tablicy o określonej strukturze.while ($row = $db->sql_fetchrow($result)) {$rowset[$row['post_id']] = array('post_id' => $row['post_id'],'post_time' => $row['post_time'],'user_id' => $row['user_id'],'username' => $row['username'],'post_text' => $row['post_text'],)}// [1231-1486]// Obróbka danych numer dwa. Dodajemy do systemu szablonów// tablice 'postrow' z postami.for ($i = 0, $end = sizeof($post_list); $i < $end; ++$i) {$postrow = array('POST_AUTHOR' => get_username_string('username',$poster_id,[...]),'MESSAGE' => $message,'POST_ID' => $row['post_id'],);// Metoda działa mniej więcej jak funkcja array_push()// albo po prostu $template->vars['postrow'][] = $postrow.$template->assign_block_vars('postrow', $postrow);}
Po wywołaniu metody odpowiedzialnej za proceder wyświetlenia szablonu, zaczyna się proces kompilacji do kodu PHP (o ile wcześniej tego nie dokonano) oraz wczytanie i wykonanie pliku cache (znów się powtarzam).
// Plik: tpl_prosilver_viewtopic_body.html.php// [98-161]// Pętla numer trzy, drukowanie na wyjście postów.<?php for ($_postrow_i = 0; $_postrow_i < $_postrow_count; ++$_postrow_i) { ?><div class="postbody"><div class="content"><?php echo $_postrow_val['MESSAGE']; ?></div></div><?php } ?>
Wniosek jest jeden; aby wydrukować posty skrypt musiał wykonać aż trzy pętle. A co na to użytkownik, który czeka na pierwszy bajt dokumentu?
Aby na siłę nie podciągać argumentów, jestem zobowiązany dodać, że można by było powyższy algorytm przerobić do użycia maksymalnie dwóch pętli. Ale dalej stosowanie systemu szablonów w takiej formie jak Smarty, dodaje skryptowi więcej do przetwarzania – na dodatek tego samego.
Komentarze do wpisu "Flush concept - strumieniowy system szablonów, wprowadzenie":
1.
b4rtaz napisał(a):
21 marca 2008, 16:36:33
W tym wpisie celowo pominąłem jakiekolwiek informacje na temat Flush’a. W następnym wpisie przedstawię moją idee oraz kod roboczy.
Tymczasem oczekiwałbym komentarzy na temat racji bądź jej braku w powyższym wpisie. Czy są to wyimaginowane problemy bądź wady czy też coś racji w tym jest.
Oczywiście mam małą prośbę, proszę nie gdybać czym ów projekt może być. Nie chce zaprzeczać czy bawić się w „ciepło/zimno”, bo już niedługo o tym sam napisze. Z góry dzięki.
2.
Khorne napisał(a):
21 marca 2008, 16:50:54
Leniwe wywołania. Dobry pomysł! :)
3.
b4rtaz napisał(a):
21 marca 2008, 17:03:21
Khorne: Szczerze mówiąc nie mam najmniejszego pojęcia, co miałeś na myśli. ;)
4.
Khorne napisał(a):
21 marca 2008, 17:04:19
No jak to, przecież właśnie to chcesz zrobić :]
http://en.wikipedia.org/wiki/Lazy_evaluation
5.
b4rtaz napisał(a):
21 marca 2008, 17:08:11
Khorne: Aż, co za wtopa! Muszę się przyznać, że nie wiedziałem o istnieniu takiego pojęcia.
Tak dokładnie na tym ma polegać Flush. No ale bez szczegółów. [:
6.
Khorne napisał(a):
21 marca 2008, 17:12:10
Ciekawi mnie tylko jak w PHP zrobisz leniwe pętle. W takim Lispie czy Scheme to się robi samo, bo tam pętla jest równoważna rekurencji (ogonowej, ale zawsze).
Czekam z niecierpliwością
7.
stormfly devblog napisał(a):
23 marca 2008, 08:06:24
Piszesz o zajętości pamięci przez zmienne, które nie zwalniamy, a teraz chcesz napisać o buforze, który zajmuje przecież pewnie kilkanaście razy więcej pamięci :P
Co do wykonywania 3 pętli, pomijając fakt, że w większości przypadków można to skrócić do 1 lub 2 (patrz PDO) to zdecydowanie bardziej chodzi o pewien bardzo przejrzysty schemat niż robienie bajzlu i pseudo oszczędzaniu czasu.
8.
Khorne napisał(a):
23 marca 2008, 11:44:34
To nie jest oszczędzanie czasu, tylko zwiększanie responsywności aplikacji. I/O zajmuje sporo czasu, a w międzyczasie można już zacząć coś wysyłać, np. CSS i layout, żeby sobie klient zrenderował ramkę.
9.
stormfly napisał(a):
23 marca 2008, 11:58:06
Klient tego nie zauważy bo to są dziesiątki części sekund. Zresztą, który znany framework wysyła od razu dane? Większość ma metodę display(), która zawsze uruchamiana jest na samym końcu jak już wszystkie operacje zostaną zakończone.
Zauważ też, że np. skrypt :
for($i = 0; $i < 1000; ++$i) { echo $i . ‘ ‘;
}
sleep(5);
echo ‘po 5 sekundach..’;
Wyświetli się cały dopiero po 5 sekundach. Chyba, że ja mam coś z serwerem, albo przeglądarką ;) Nic mi się wcześniej nie renderuje.
Dodaj komentarz: