PHP/Open Power Template

Z Wikibooks, biblioteki wolnych podręczników.

< PHP
Poprzedni rozdział: Smarty Spis treści Następny rozdział: Sztuczki

Spis treści

[edytuj] Open Power Template

Zapoznamy się teraz z polskim systemem szablonów Open Power Template reprezentującym nieco inne podejście do pracy z szablonami, niż Smarty oraz napisanym specjalnie dla PHP5.

[edytuj] Instalacja

Najnowszą wersję można ściągnąć ze strony projektu: [1] w postaci spakowanego archiwum. Zawiera ono kilka katalogów:

  • /configurator - narzędzie do konfigurowania OPT (tylko w wersji 1.0.x)
  • /docs - angielska dokumentacja w formacie PDF (tylko w wersji 1.0.x)
  • /examples - przykładowe skrypty demonstrujące różne możliwości OPT
  • /lib - źródła biblioteki. Ten katalog interesuje nas najbardziej i to jego zawartość należy dołączać do naszych projektów korzystających z OPT.
  • /toolset - pakiet narzędziowy do OPT od wersji 1.1.x
  • /unitTest - zestaw testów sprawdzających poprawność działania kompilatora oraz całej biblioteki. Aby go uruchomić, wymagany jest skrypt PHPUnit 1.3

Instalacja OPT jest bardzo prosta. Kopiujemy zawartość katalogu /lib do drzewa katalogowego naszego projektu. W kodzie naszych skryptów musimy ustawić stałą OPT_DIR pokazującą położenie plików biblioteki, a następnie dołączyć plik opt.class.php. Ścieżka podana w stałej powinna być zakończona ukośnikiem.

<?php
   define('OPT_DIR', '../includes/opt/');
   require(OPT_DIR.'opt.class.php');
?>
Uwaga! Uwaga!
Nie zaleca się ustawiania bezwzględnej ścieżki do katalogu w stałej OPT_DIR z powodów wydajnościowych! PHP przeszukuje wtedy niepotrzebnie kilka własnych folderów zdefiniowanych w dyrektywie include_path, co niepotrzebnie marnuje czas skryptu. Ścieżka względna rozpoczyna się zawsze od ../ (katalog nadrzędny) lub ./ (katalog obecny).

Musimy także stworzyć katalogi, w których będziemy przechowywać szablony:

  • /templates - pliki źródłowe
  • /templates_c - katalog na użytek OPT, w którym będzie on umieszczać sobie skompilowane wersje. PHP musi posiadać prawa zapisu w tym katalogu.

Jeżeli zamierzamy w obrębie folderu /templates tworzyć podkatalogi, musimy także stworzyć je w /templates_c.

[edytuj] Pierwszy skrypt

Zanim rozpoczniemy, konieczne będzie pewne wyjaśnienie. OPT używa nieco innej terminologii, niż Smarty. To, co w poprzednim systemie zwaliśmy zmienną szablonową, tutaj będzie blokiem (zmienne też istnieją, lecz rozpoczynają się znakiem małpy i są tworzone przez szablon, a nie przez programistę). Podobnych różnic jest nieco więcej i będziemy je stopniowo wyjaśniać.

Napiszemy teraz pierwszy skrypt korzystający z OPT do przetwarzania szablonów:

<?php 
  define('OPT_DIR', '../lib/');     // 1
  require(OPT_DIR.'opt.class.php'); // 2
  try
  {  
    $tpl = new optClass; // 3

    $tpl -> root = './templates/'; // 4
    $tpl -> compile = './templates_c/'; 
    $tpl -> gzipCompression = 1; 

    $tpl -> httpHeaders(OPT_HTML); // 5
  
    $tpl -> assign('current_date', date('d.m.Y'));  // 6
    $tpl -> parse('szablon1.tpl');  // 7
  }
  catch(optException $exception) // 8
  {  
    optErrorHandler($exception);  
  }  
?>

Jak widać, skrypt startowy jest znacznie bardziej rozbudowany, aniżeli ten ze Smarty'ego. Wynika to ze specyfiki nowego systemu. Oto opis działania:

  1. Ustawiamy stałą OPT_DIR ze ścieżką do plików biblioteki.
  2. Ładujemy główny plik biblioteki.
  3. Tworzymy obiekt klasy optClass.
  4. Tutaj konfigurujemy OPT. Podobnie, jak w Smarty, może to się odbywać poprzez bezpośrednie ustawianie odpowiednich dyrektyw. root zawiera ścieżkę do katalogu ze źródłami szablonów, natomiast compile - ścieżkę do katalogu dla skompilowanych wersji. gzipCompression włącza automatyczną kompresję generowanego kodu HTML, o ile przeglądarka takową obsługuje. Pozwala to przyspieszyć ładowanie strony, jednak na serwerze musi być zainstalowane rozszerzenie zlib.
  5. OPT posiada także prosty menedżer nagłówków HTTP. W tym przypadku ustawiamy, że generujemy dokument HTML (nagłówek text/html). Do dyspozycji mamy również stałe OPT_XHTML - informacja o wysyłaniu dokumentu XHTML, o ile przeglądarka obsługuje odpowiedni nagłówek oraz o ile użytkownik ustawił sobie odpowiedni priorytet. W przeciwnym razie wysyłany jest nagłówek text/html. OPT_FORCE_XHTML zawsze wymusza wysłanie nagłówków XHTML-a, o ile tylko przeglądarka je rozpoznaje.
  6. Analogicznie, jak w Smarty, przypisujemy dane do bloków metodą assign().
  7. Wysyłamy żądanie parsowania szablonu szablon1.tpl. Wygenerowany kod zostanie automatycznie wysłany do przeglądarki.
  8. W OPT wszystkie błędy obsługiwane są przez mechanizm wyjątków, które musimy przechwycić. Możemy sami napisać sobie ich obsługę, lub skorzystać z gotowej, zawartej w funkcji optErrorHandler().


Nasz szablon (szablon1.tpl) wygląda następująco:

<html>
<head>
<title>Mój pierwszy skrypt w OPT</title>
</head>
<body>
<p>Hello world! Dzisiaj jest {$current_date}!</p>
</body>
</html>

Zauważ, że w OPT bloki osadzamy identycznie, jak w Smarty, domyślnie otaczając je nawiasami klamrowymi oraz poprzedzając nazwę znakiem dolara. Po wykonaniu skryptu ujrzymy w przeglądarce stronę zatytułowaną "Mój pierwszy skrypt w OPT" oraz posiadającą zawartość, np.:

Hello world! Dzisiaj jest 28.12.2006!

Zwykłe osadzanie bloków to nie jedyna operacja, jaką można przeprowadzić wewnątrz nawiasów klamrowych. Ponieważ OPT mimo wszystko z tradycją programistyczną nie zrywa, faktycznie można umieszczać w nich niemal dowolny rodzaj wyrażeń. Poniżej zaprezentujemy skrypt, który będzie wyświetlał statystyki odwiedzin pewnego serwisu. Dla uproszczenia pominiemy na razie wszystkie rzeczy związane z bazą danych i ilości użytkowników wpiszemy do skryptu "na sztywno".

<?php 
  define('OPT_DIR', '../lib/');     // 1
  require(OPT_DIR.'opt.class.php'); // 2

  function getActiveUsers()
  {
    // funkcja zwraca liczbe aktywnych uzytkownikow
    return 367;
  }

  function getInactiveUsers()
  {
    // funkcja zwraca liczbe nieaktywnych uzytkownikow
    return 62;
  }

  try
  {  
    $tpl = new optClass;

    $tpl -> root = './templates/';
    $tpl -> compile = './templates_c/'; 
    $tpl -> gzipCompression = 1; 

    $tpl -> httpHeaders(OPT_HTML);
  
    $tpl -> assign('active_users', getActiveUsers());
    $tpl -> assign('inactive_users', getInactiveUsers());
    $tpl -> parse('statystyka.tpl');
  }
  catch(optException $exception)
  {  
    optErrorHandler($exception);  
  }  
?>

W skrypcie definiujemy dwa bloki: active_users oraz inactive_users, zatem te informacje może wyświetlić szablon. Załóżmy teraz, że szef chce widzieć także łączną liczbę użytkowników, ponieważ ma trudności z (szybkim) sumowaniem liczb większych, niż 10. Niektóre aplikacje mogą być tak złożone, że odnalezienie tych dwóch linijek w setkach kilobajtów kodu może być ciężkie - w końcu najpierw trzeba poprawić szablon, a potem zmodyfikować skrypt, by przesyłał także i trzeci blok. Jeśli zadania są podzielone między webmastera i programistę, trzeba nawet zaangażować cały zespół do poszukiwań. Jednak jak wspomnieliśmy, wykorzystamy tutaj coś więcej, niż tylko zwykłe osadzanie bloków, po prostu wykonując niezbędne sumowanie po stronie szablonu (dodajmy, że podobną rzecz można zrealizować też w Smarty):

<html>
<head>
<title>Statystyki serwisu: użytkownicy</title>
</head>
<body>
<p>Aktywnych użytkowników: {$active_users}</p>
<p>Nieaktywnych użytkowników: {$inactive_users}</p>
<p>Łącznie użytkowników: {$active_users + $inactive_users}</p>
</body>
</html>

Po wykonaniu skryptu otrzymamy następujący rezultat:

Aktywnych użytkowników: 367
Nieaktywnych użytkowników: 62
Łącznie użytkowników: 429

Choć OPT także przenosi do szablonów cały język programowania, należy traktować to jednak z dystansem jako pewien dodatek, kiedy inne możliwości zawodzą. W rzeczywistości przy pracach nad rozmaitymi serwisami wykorzystuje się jedynie część potencjału biblioteki, zostawiając całą logikę i przetwarzanie danych skryptom, a w szablonie umieszczając to, co szablon powinien zawierać, czyli opis, jak osadzić wygenerowane dane w kodzie HTML.

OPT pozwala na proste osadzenie wewnątrz szablonów list, a od wersji 1.1.0 także hierarchicznych struktur (drzew) oraz wstawek systemu dzielenia wyników na strony. Ponadto, jeśli tworzymy aplikację z wielojęzycznym interfejsem, możemy ją łatwo zintegrować z OPT, dzięki czemu nie będzie konieczne ręczne przenoszenie wszystkich komunikatów językowych ze skryptu do szablonów.

Ćwiczenie 1: Napisz skrypt, który najpierw wyświetla formularz, w którym pyta się o imię, nazwisko, stanowisko oraz adres e-mail osoby odwiedzającej, a po jego wysłaniu wyświetla wpisane dane w postaci ładnej wizytówki. Do wyświetlania kodu HTML użyj wyłącznie systemu szablonów Open Power Template.

Ćwiczenie 2: Rozszerz skrypt wizytówki o sprawdzanie poprawności wpisanych danych. Jeżeli są błędne, wyświetl odpowiedni komunikat.

Ćwiczenie 3: Usprawnij system raportowania błędów w skrypcie wizytówek. Jeżeli użytkownik źle wypełnił formularz, powinien zostać on przeładowany wraz z wpisaną przez niego zawartością oraz odpowiednimi komunikatami wyświetlonymi pod błędnie wypełnionymi polami.

Porada Porada
W ćwiczeniach wykorzystaj fakt, że możesz bez żadnych konsekwencji tworzyć w szablonach bloki, którym nie przypisujesz w skrypcie żadnej wartości.

[edytuj] Tworzenie list

OPT również posiada znacznik określany mianem sekcji i również używa się go do tworzenia wszelkiego rodzaju list, lecz w tym systemie szablonów jego użycie wymaga znacznie mniej pracy. Spróbujmy napisać tutaj jeszcze raz skrypt do wyświetlania listy newsów z poprzedniego rozdziału, aby się o tym przekonać. Rozpoczniemy od kodu PHP, który generalnie nie ulega żadnym modyfikacjom - tu także musimy wygenerować identyczną tablicę z danymi i przekazać ją do parsera w identyczny sposób z użyciem metody assign().

<?php 
  define('OPT_DIR', '../lib/');
  require(OPT_DIR.'opt.class.php');
  try
  {  
        $tpl = new optClass;

        $tpl -> root = './templates/';
        $tpl -> compile = './templates_c/'; 
        $tpl -> gzipCompression = 1; 

        $tpl -> httpHeaders(OPT_HTML);

        $newsy = array();

        for($i = 0; $i < 5; $i++)
        {
                $newsy[] = array(
                                'tytul' => 'Tytuł wiadomości',
                                'data' => date('d.m.Y'),
                                'tresc' => 'Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
                                Cras nec diam. In hac habitasse platea dictumst. Donec id leo. Ut
                                feugiat augue at metus. In hac habitasse platea dictumst. Donec
                                pulvinar sollicitudin tellus. Quisque mattis faucibus nulla. Praesent
                                in mauris. Maecenas erat nisi, laoreet in, porta nec, varius ut, turpis.
                                Suspendisse pretium nibh at tellus placerat venenatis. Vestibulum
                                ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia
                                Curae; Etiam felis arcu, ringilla a, commodo quis, blandit id, est.
                                Fusce nec sapien nec libero dignissim volutpat.'        
                        );
        }

        $tpl -> assign('newsy', $newsy);
        $tpl -> parse('szablon3.tpl');
  }
  catch(optException $exception)
  {  
        optErrorHandler($exception);  
  }  
?>

A tak będzie prezentować się szablon HTML pozwalający tę właśnie listę wyświetlić:

<html>
 <head>
  <title>OPT News!</title>
 </head>
 <body>
  {section=newsy}
    <h3>{$newsy.tytul}</h3>
    <p>Dnia {$newsy.data}</p>
    <p>{$newsy.tresc}</p> 
  {/section}
 </body>
</html>

OPT nie wymaga podawania żadnych iteratorów itd. - to, jak dokładnie wszystko jest obsługiwane od strony technicznej, jest już wewnętrzną sprawą biblioteki. Naszym zadaniem jest jedynie określenie nazwy sekcji równoznacznej w tym wypadku z nazwą bloku, w którym mieści się tablica z danymi. Do poszczególnych bloków sekcji odwołujemy się, podając najpierw nazwę samej sekcji, a później po kropce nazwę informacji, którą chcemy w danym miejscu wyświetlić.

Analogicznie sprawa ma się z zagnieżdżaniem sekcji. Tutaj również przepiszemy na OPT skrypt obsługi biblioteki z poprzedniego rozdziału. O dziwo, także i teraz generowanie tablic z danymi odbywa się dokładnie tak samo.

<?php 
  define('OPT_DIR', '../lib/');
  require(OPT_DIR.'opt.class.php');
  try
  {  
        $tpl = new optClass;

        $tpl -> root = './templates/';
        $tpl -> compile = './templates_c/'; 
        $tpl -> gzipCompression = 1; 
        $tpl -> xmlsyntaxMode = 1;

        $tpl -> httpHeaders(OPT_HTML);

        $pdo = new PDO('mysql:host=localhost;dbname=podrecznik_php', 'root', 'root');

        $kategorie = array();
        $ksiazki = array();

        $stmt = $pdo -> query('SELECT id, nazwa, il_ksiazek FROM kategorie ORDER BY nazwa');
        while($row = $stmt -> fetch())
        {
                $kategorie[] = $row;
        }
        $stmt -> closeCursor();
        unset($stmt);

        $stmt = $pdo -> query('SELECT x.id, x.nazwa, k.id AS `kategoria_id` FROM ksiazki x, kategorie k
                WHERE k.id = x.kategoria_id ORDER BY k.nazwa, x.nazwa');
        $i = -1;
        $kid = 0;
        while($row = $stmt -> fetch())
        {
                if($row['kategoria_id'] != $kid)
                {
                        $i++;
                        $kid = $row['kategoria_id'];
                }

                $ksiazki[$i][] = $row;
        }
        $stmt -> closeCursor();

        $tpl -> assign('kategorie', $kategorie);
        $tpl -> assign('ksiazki', $ksiazki);
        $tpl -> parse('szablon4.tpl');
  }
  catch(optException $exception)
  {  
        optErrorHandler($exception);  
  }  
?>

Szablon:

<html>
 <head>
  <title>Biblioteka</title>
 </head>
 <body>
  <ul>
   <opt:section name="kategorie"> {* 1 *}
   <li>{$kategorie.nazwa} <opt:show name="ksiazki"><ul> {* 2 *}
     <opt:section> {* 3 *}
     <li>{$ksiazki.nazwa}</li> {* 4 *}
     </opt:section>
   </ul></opt:show>
   </li>
  </opt:section>
  </ul>
 </body>
</html>

Chwila, a cóż to się porobiło ze składnią? Nic - to jest dalej jeden i ten sam OPT. Rzuć na chwilę okiem na skrypt PHP, gdzie podczas konfigurowania pojawiła się dyrektywa xmlsyntaxMode. Dzięki niej włączamy tzw. tryb kompatybilności z XML, który udostępnia alternatywną składnię znaczników, stojącą w zgodzie z językiem XML. Naturalnie OPT nie sprawdza tak ściśle, czy użycie poszczególnych wzorców pasuje do reguł języka XML i nie ma żadnego problemu, aby rozpocząć sekcję znacznikiem XML-owym, a skończyć tradycyjnym, ograniczonym nawiasami klamrowymi. Dodajmy, że styl parametrów nazwa="wartość" dostępny jest zawsze i można go używać zamiennie z listami parametrów rozpoczynających się od znaku równości.

Przejdźmy teraz do samego szablonu:

  1. Tutaj rozpoczynamy sekcję kategorii.
  2. Instrukcja show jest rozwinięciem sekcji i tu też podajemy jej nazwę. Sprawdza ona, czy do sekcji przypisane są jakieś elementy, co pozwala na niewyświetlenie znaczników <ul> i </ul>, kiedy nie są one z racji braku takowych potrzebne.
  3. Nazwa sekcji została już podana w instrukcji show i tu już nie trzeba jej podawać.
  4. Oto odwołanie się do bloków wchodzących w skład opisu pojedynczej książki. Podajemy nazwę interesującej nas sekcji, a później blok wewnątrz niej. Alternatywnie moglibyśmy napisać $kategorie.ksiazki.nazwa, ale dla OPT nie robi to absolutnie żadnej różnicy.

Zwróćmy uwagę na fakt, że OPT z góry "wie", jakie są zależności pomiędzy danymi, dzięki czemu w tym systemie szablonów nie trzeba martwić się o iteratory. Znacznie poprawia to przenośność kodu. Fragment szablonu wyświetlający listę książek można wyciąć i przenieść w inne miejsce, używając go np. do wyświetlenia wszystkich książek, bez podziału na kategorie, a nie wymagałoby to absolutnie żadnych zmian. Powstaje jednak pytanie, czy w ten sposób nie ograniczamy naszych możliwości? W końcu teoretycznie ręczne określanie wszystkich parametrów pozostawia w naszych rękach większą kontrolę. Odpowiedź brzmi: nie. Dłuższa praktyka pokazuje, że paradoksalnie nasze możliwości w ten sposób rosną. OPT zezwala na przekazywanie danych do sekcji na cztery różne sposoby (część z nich jest dostępna po włączeniu odpowiednich dyrektyw konfiguracyjnych). Dzięki wprowadzeniu takiej separacji, kod tej samej sekcji bez żadnych zmian może w jednym miejscu pracować w oparciu o jeden format danych, a w innym o drugi. Biblioteka sama zajmuje się technicznymi aspektami ich obsługi.

[edytuj] Rendering drzew

[edytuj] Interfejs wielojęzyczny

[edytuj] Zakończenie

Na OPT kończymy poznawanie możliwości oferowanych przez systemy szablonów. W następnym rozdziale zatytułowanym PHP/Sztuczki dowiemy się, jak korzystać z systemów szablonów do osiągania różnych rezultatów. Podamy tam konkretne wskazówki, jak za pomocą szablonów tworzyć dynamiczne formularze oraz jak składać design strony z kilku szablonów. W studium przypadku przerobimy napisany wcześniej system newsów z wykorzystaniem obu poznanych systemów szablonów.