JustPaste.it

Wprowadzenie do OPT cz. 2

Druga część artykułu wprowadzającego do systemu szablonów Open Power Template. Jest ona w całości poświęcona komponentom.

W poprzedniej części poznaliśmy podstawowe zasady pracy z OPT oraz mechanizm językowy. Część druga niemal w całości skoncentrowana jest na komponentach.

Komponenty

Mechanizm komponentów został wprowadzony na życzenie użytkowników biblioteki. Jest to wysokopoziomowy element składni, co oznacza, że stara się on ukryć jak najwięcej spraw programistycznych przed twórcą szablonu. Komponent to tak naprawdę pewien obiekt implementujący odpowiedni interfejs i osadzony wewnątrz szablonu. Generuje on bezpośrednio kod HTML oraz może reagować na różne zdarzenia, o samodzielnym pobieraniu z naszych skryptów odpowiednich danych nie wspominając. Do czego można komponenty wykorzystać? Jednym z ich zastosowań jest uproszczenie procesu tworzenia formularzy. Zamiast wstawiać kontrolki bezpośrednio i obudowywać każdą z nich dziesiątkami instrukcji warunkowych mających obsługiwać wszystkie możliwe sytuacje (niewłaściwy format danych, automatyczne uzupełnienie pól przy przeładowaniu formularza itd.), możemy cały ten programistyczny bajzel ukryć wewnątrz komponentu, który możemy wykorzystywać wielokrotnie.

Inną ważną cechą komponentów jest ich dynamiczny charakter. Nie jesteśmy wcale ograniczeni do statycznego definiowania miejsc, w których mają się one pojawić. OPT pozwala na utworzenie tzw. komponentu niezdefiniowanego. Jest to swoisty "port", do którego skrypt może samodzielnie podpiąć taki komponent, jaki akurat mu jest w tym miejscu potrzebny. Jeżeli nic nie podepnie, nic się nie stanie. Popatrzmy, jak dziecinnie łatwe staje się teraz generowanie dynamicznych formularzy: pakujemy taki niezdefiniowany komponent w sekcję i łączymy jedno z drugim. Skrypt tworzy tablicę dla sekcji zawierającą obiekty różnych komponentów, które chce sobie wyświetlić. Podpina ją do OPT metodą assign() i voile'a! Mamy w pełni zautomatyzowany oraz dynamiczny formularz w tak prostym kodzie, że prościej się już chyba nie da.

Przejdźmy zatem do praktyki. Najpierw wykorzystamy jeden z przykładowych i dość prostych komponentów dołączonych do OPT, a mianowicie selectComponent. Nasz szablon będzie wyglądać tak:

<html>
<head>
<title>Open Power Template: i18n</title>
</head>
<body>
<form method="get" action="skrypt8.php">
Wybierz kategorię:
{selectComponent name="selected" message="$message" datasource="$list"}
{param name="selected" value="$selected"/}
{onmessage message="msg" position="down"}
<font color="red">{@msg}</font>
{/onmessage}
{/selectComponent}
<input type="submit" value="OK"/>
</form>
</body>
</html>

Oto objaśnienie poszczególnych linijek komponentu:

Linijka Opis
{selectComponent name="selected"
message="$message" datasource="$list"}
Początek deklaracji komponentu. Możemy tu ustawić kilka interesujących rzeczy, m.in. zdefiniować parametry komponentu - te zależą oczywiście od jego autora. I tak: name to nazwa pola formularza, a message to treść opcjonalnego komunikatu błędu. Jedynym nieco istotniejszym czynnikiem jest datasource, który pozwala na zdefiniowanie źródła danych - tutaj jest nim tablica z elementami listy rozwijanej.
{param name="selected"
value="$selected"/}
Jest to alternatywny sposób ustawiania różnych parametrów dla komponentu. Musimy pamiętać o końcowym slashu, podobnie jak w plikach XML. Inaczej znacznik nie zostanie zamknięty i szablon się wykrzaczy przy kompilacji.
{onmessage message="msg"
position="down"}
<font color="red">{@msg}</font>
{/onmessage}
To jest tzw. zdarzenie. Kod HTML wewnątrz tego znacznika zostanie wykonany, jeśli komponent wygeneruje zdarzenie onmessage. Parametr message definiuje nazwę zmiennej, do której ma zostać zapisana treść komunikatu, a opcjonalny position określa, czy kod ma się pojawić przed komponentem ("up"), czy pod nim ("down").
{/selectComponent}
Koniec komponentu.

Zwróć uwagę na dziwny znacznik: {@msg}. Jeśli jakaś nazwa poprzedzona jest małpą zamiast znaku dolara, mamy do czynienia ze zmienną. Zmienna zachowuje się w zasadzie tak samo, jak blok. Jest tylko jedna różnica: bloki tworzy skrypt, a zmienne - szablon oraz OPT. Rozróżnienie nie jest wcale takie bezsensowne. Dzięki wydzieleniu dla OPT osobnej przestrzeni, nie ma żadnej obawy, że jakaś instrukcja chcąca coś udostępnić twórcy szablonu, nadpisze bloki z istotnymi danymi. Tym samym programista w ogóle nie musi się przejmować nazwami bloków - ma pewność, że jakiej nazwy tam nie da (oczywiście za wyjątkiem $opt!), OPT na pewno mu jej sam z siebie nie nadpisze.

Jak na razie wszystko wygląda prosto. OPT, natykając się na taki zestaw linijek, stworzy sobie automatycznie obiekt selectComponent, poprzypisuje mu parametry i spróbuje wywołać. Popatrzmy teraz, jak musimy wygenerować odpowiednie dane dla skryptu:

<?php
define('OPT_DIR', './lib/');
require('./lib/opt.class.php');
 
try
{
$tpl = new optClass;
$tpl -> root = './templates/';
$tpl -> compile = './templates_c/';
$tpl -> gzipCompression = true;
$tpl -> sectionStructure = OPT_SECTION_SINGLE;
$tpl -> httpHeaders(OPT_HTML);

$pdo = new PDO('mysql:host=localhost;port=3305;dbname=opt',
'root', 'root');
$pdo -> setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);
 
$stmt = $pdo -> query('SELECT id, name FROM categories
ORDER BY id'
);
$list = array(0 => array(
'value' => 0,
'desc' => '---Wybierz---',
'selected' => false
));
while($row = $stmt -> fetch())
{
$list[] = array(
'value' => $row['id'],
'desc' => $row['name'],
'selected' => false
);
}
$stmt -> closeCursor();

if(isset($_GET['selected']))
{
$tpl -> assign('selected', $_GET['selected']);
if($_GET['selected'] == 0)
{
$tpl -> assign('message', 'Nieprawidłowy wybór!');
}
}
else
{
$tpl -> assign('selected', 0);
}
 
$tpl -> assign('list', $list);
$tpl -> parse('szablon6.tpl');
}
catch(optException $exception)
{
optErrorHandler($exception);
}
?>

Łohohoh, a miało być tak prosto! Racja, ale to nie wina idei komponentów, tylko przykładów dołączonych do OPT. Są one tak uniwersalne, że mało co ukrywają, zwalając wszystko na programistę, tyle że po stronie skryptu. Jedyną poprawą na lepsze jest ustawianie, który element ma być zaznaczony - robimy to przez osobny parametr: selected, na podstawie którego komponent decyduje, co trzeba zaznaczyć. Jednak to za mało. Pójdźmy zatem dalej i napiszmy swój własny komponent o znacznie lepszych właściwościach!

Projekt naszego komponentu będzie następujący:

  1. 1.Będzie on reprezentować zwykłe pole tekstowe (input type="text").
  2. Komponent ma samodzielnie pobierać odpowiednie dane z systemu szablonów lub ze świata zewnętrznego, przez co nie będzie konieczne przekazywanie ich ręcznie za pom. parametrów.
  3. W przypadku przeładowania formularza do ponownego wypełnienia z powodu błędów, komponent automatycznie załaduje poprzednio wpisaną przez internautę zawartość.
  4. W przypadku błędu komponent automatycznie dobierze odpowiedni komunikat powiadamiający o problemie.

Tworzenie komponentu polega na napisaniu klasy implementującej interfejs ioptComponent, a następnie zarejestrowaniu jej w parserze tak, aby można było ją łatwo wykorzystać. Utwórz sobie jakiś katalog, np. /var, a w nim plik myInputComponent.php. Jego początek jest następujący:

<?php
 
class myInputComponent implements ioptComponent
{
private $name;
private $validForm = true;
private $predefinedValues = array();
private $messageBuffer = array();
private $htmlTags = array();
private $opt;

Definiujemy tutaj wszystkie potrzebne nam pola:

  1. name - nazwa naszego pola tekstowego.
  2. validForm - informacja, czy formularz jest poprawnie wypełniony.
  3. predefinedValues - tablica z predefiniowanymi wartościami dla komponentów.
  4. messageBuffer - tablica asocjacyjna z komunikatami błędów. Indeksami są nazwy pól, których dane komunikaty dotyczą.
  5. htmlTags - wszystkie parametry i ustawienia będziemy zapisywać tutaj. Tablica ta zostanie później zamieniona na listę parametrów dla znacznika "input".
  6. opt - obiekt klasy optClass.

Dane dla pól 1-4 są automatycznie pobierane z systemu szablonów. Pole 5 jest wypełniane przez komponent lub programistę, a szóstka jest otrzymywana od OPT. Napiszmy teraz konstruktor:

		public function __construct($name = '')
{
if($name != '')
{
$this -> htmlTags['name'] = $this -> name = $name;
}
} // end __construct();

Wygodnie jest dać możliwość zdefiniowania w konstruktorze nazwy tworzonej kontrolki. Będzie wtedy znacznie wygodniej tworzyć komponenty od strony skryptu. Jeżeli nazwa jest podana, dopisujemy ją do listy parametrów HTML oraz ustawiamy pole $name.

Kolejna metoda, setOptInstance(), jest już częścią interfejsu ioptComponent:

		public function setOptInstance(optClass $tpl)
{
$this -> tpl = $tpl;

if(isset($this -> tpl -> data['validForm']))
{
$this -> validForm =
(bool)$this -> tpl -> data['validForm'];
}
if(isset($this -> tpl -> data['messageBuffer']))
{
$this -> messageBuffer =
&$this -> tpl -> data['messageBuffer'];
}
if(isset($this -> tpl -> data['predefinedValues']))
{
$this -> predefinedValues =
&$this -> tpl -> data['predefinedValues'];
}
} // end setOptInstance();

Wykorzystujemy ją do pobrania używanego obiektu klasy optClass, a przy tej okazji również automatycznego pobrania stamtąd różnych ciekawych informacji, które mogą nam się przydać. Musimy wiedzieć, że parser trzyma dane bloków w publicznej tabeli $data, skąd też możemy je bezpośrednio odczytać.

		public function set($name, $value)
{
$this -> htmlTags[$name] = $value;
if($name == 'name')
{
$this -> name = $value;
}
} // end set();

Metoda set($name, $value) obsługuje przypisanie do komponentu parametru $name o wartości $value. Dodatkowo sprawdzamy - jeżeli ktoś w ten sposób chce zdefiniować nazwę komponentu, musimy pamiętać o nadpisaniu pola $name w naszej klasie.

		public function push($name, $value, $selected = false)
{
} // end push();
 
public function setDatasource(&$source)
{
$this -> predefinedValues = $source;
} // end setDatasource();

Metoda push() nie jest przez nas wykorzystywana - obsługuje ona znaczniki listItem umieszczone wewnątrz komponentu w szablonie. My z nich nie korzystamy, więc po szczegóły należy zajrzeć do dokumentacji. setDatasource() obsługuje parametr datasource znany nam już z poprzedniego przykładu. Przyjmujemy, że w naszym komponencie można za jego pomocą zdefiniować alternatywne źródło predefiniowanych danych dla komponentów.

		public function begin()
{
if(!$this -> validForm)
{
$this -> htmlTags['value'] = $_POST[$this -> name];
}
else
{
$this -> htmlTags['value'] = '';
if(isset($this -> predefinedValues[$this->name]))
{
$this -> htmlTags['value'] =
$this -> predefinedValues[$this->name];
}
}

echo '<input type="text" '.
generateTagElementList($this->htmlTags).'/>';
} // end begin();
 
public function end()
{
} // end end();

Metody begin() oraz end() wysyłają za pom. komendy echo kod HTML będący wynikiem działania komponentu. Dlaczego jest ich dwie i dlaczego my wykorzystujemy tylko jedną? Musimy wrócić jeszcze raz do naszej wzmianki i obsłudze zdarzeń w komponentach. Otóż opcjonalny parametr position, oprócz "up" i "down", może jeszcze przyjąć wartość "middle", co oznacza odpalenie zdarzenia we wnętrzu komponentu. Patrząc od strony programistycznej, zdarzenie takie odpala się właśnie pomiędzy wywołaniami obu tych metod. Jeśli znajdujesz dla tego jakieś ciekawe zastosowanie, możesz je wykorzystać. W przeciwnym wypadku lepiej zrównać "middle" z "down" i zostawić metodę end() pustą.

Popatrzmy teraz, jak generowany jest sam kod. W zależności od stanu formularza (poprawny lub nie), decydujemy, czy należy zassać dane z tabeli $_POST, czy też pobrać predefiniowane ustawienia. Gdy i tych brakuje, pole value będzie puste. W wygenerowaniu samego znacznika pomaga dostępna w OPT funkcja generateTagElementList(), która dodatkowo przepuszcza wartości wszystkich parametrów przez funkcję htmlspecialchars(), ochraniając formularz przed wygłupami internautów próbujących umieszczać w nich znaki specjalne HTML-a.

Wszystkie pozostałe metody w klasie komponentu reprezentują poszczególne zdarzenia. Każda z nich pobiera jeden parametr: $msg, który opisuje, jak się nazywa zmienna, do której trzeba zapisać komunikat. Dodatkowo, metody te muszą zwrócić true, jeżeli zdarzenie zaszło, a false w przeciwnym razie. Tak będzie wyglądać metoda onMessage() jednocześnie kończąca nasz komponent:

		public function onMessage($msg)
{
if(!$this -> validForm)
{
if(isset($this -> messageBuffer[$this->name]))
{
$this -> tpl -> vars[$msg] =
$this -> messageBuffer[$this->name];
return true;
}
}
return false;
} // end onMessage();
 
}
 
?>

Tu warto dodać, że OPT wartości zmiennych przechowuje w publicznej tablicy optClass::$vars i tam właśnie należy zapisywać komunikaty.

Nasz komponent jest już gotowy. Pora na wykorzystujący go szablon:

<html>
<head>
<title>Open Power Template: komponenty</title>
</head>
<body>
<opt:bindEvent id="defaultMessage" name="onMessage"
message="msg" position="down">

<br/><span style="color: #ff0000;">{@msg}</span>
</opt:bindEvent>
 
<opt:if test="not $validForm">
<p>Formularz nie został prawidłowo wypełniony.</p>
</opt:if>
 
<form method="post" action="skrypt9.php">
<table border="0" width="50%">
<opt:section name="components">
<tr>
<td width="40%"><strong>{$components.title}</strong></td>
<td width="60%"><opt:component id="$components.item">
<opt:load event="defaultMessage"/>
</opt:component></td>
</tr>
</opt:section>
<tr>
<td width="40%">&nbsp;</td>
<td width="60%"><input type="submit" value="Wyślij"/></td>
</tr>
</table>
</form>
</body>
</html>

Stop, zanim powiesz "co do @#$@#$", przeczytaj jeszcze poniższy komentarz. Nietrudno zauważyć, że ten szablon wygląda zgoła odmiennie od tego, co dotąd widzieliśmy. Zamiast klamerek pojawiła się składnia a'la XML i dziesiątki innych rzeczy. To nic takiego, nadal pracujemy na OPT. Zdecydowałem się po prostu przy okazji ostatniego przykładu pokazać pewną ciekawą opcję, a mianowicie "XML Syntax Mode". Jest to specjalny tryb, w którym składnia OPT zaczyna przypominać język XML. Do naszej dyspozycji oddany jest teraz szereg wzorców znaczników XML-owych wykorzystujących przestrzeń nazw "opt". Zamiast {section} napiszemy więc , a zamiast {/section} - . Dalsze zmiany dotyczą sposobu przekazywania parametrów. Domyślnie OPT pozwala na stosowanie aż trzech różnych składni:

  1. {znacznik=parametr1; parametr2; parametr3} - składnia OPT, zawsze aktywna.
  2. {znacznik parametr1="wartosc" parametr2="wartosc" parametr3="wartosc"} - składnia XML, aktywna również w podstawowej składni. Rozróżnienie następuje po tym, czy po nazwie instrukcji występuje znak równości, czy spacja.
  3. {znacznik wartosc} - składnia dowolna, która zależy już od konkretnej instrukcji. Korzysta z niej np. warunek IF.

XML Syntax Mode eliminuje wariant trzeci, a każda pracująca w nim instrukcja musi wtedy przestawić się na składnię predefiniowaną. W instrukcji IF musimy przez to podawać warunek jako parametr test, np. .

Należy pamiętać, że tryb kompatybilności z XML-em to tak naprawdę tylko emulacja i OPT nic sobie nie robi z ewidentnych błędów składniowych (z punktu widzenia XML) w rodzaju . Musimy mieć świadomość, iż parser OPT wprawdzie wykorzystuje mechanizm znaczników zbliżony do XML-a, ale wprowadza do niego sporo własnych dodatków, usprawnień i rozluźnień tam, gdzie jest to możliwe.

Wróćmy jednak do tematu, tj. naszego przykładu. W kodzie szablonu mamy kolejną tajemniczą instrukcję: bindEvent. Wyobraźmy sobie sytuację, gdy na naszej witrynie mamy formularze złożone z wielu statycznych komponentów lub też wiele dynamicznych formularzy. Powtarzanie kodu HTML i całej konfiguracji zdarzenia onMessage dla każdego z nich z osobna jest dość żmudne. Dzięki bindEvent możemy stworzyć je raz na długi czas, a w komponencie tylko je podpinać znacznikiem load, co jest pokazane na przykładzie.

Zwróćmy też uwagę, że nie podajemy tym razem nazwy naszego komponentu w szablonie. Tak, właśnie stworzyliśmy nasz pierwszy komponent niezdefiniowany. Wymaga on podania parametru id, który określa, w jakim bloku/zmiennej/czymkolwiek innym parser ma się spodziewać obecności obiektu komponentu mającego się w tym miejscu znaleźć. Popatrzmy teraz w ten sposób: umieszczamy to wszystko w sekcji i każemy szukać tych obiektów w jednym z pól sekcji. Teraz posiadamy najdynamiczniejszy formularz na świecie - jego pola są definiowane w skrypcie, pakowane w sekcję i wysyłane do parsera. Parser uruchamia sekcję i wstawia kolejne obiekty do komponentu niezdefiniowanego. Komponenty automatycznie pobierają takie dane, jakich potrzebują, generując odpowiedni kod HTML i odpalając zdarzenia, a my otrzymujemy piękny formularz złożony z wielu pól. Czegóż więcej trzeba do szczęścia?

Do uruchomienia naszego przykładu potrzebny nam będzie jeszcze jeden, prościutki szablon, w którym zaprezentujemy wyniki:

<html>
<head>
<title>Open Power Template: komponenty</title>
</head>
<body>
<table border="0" width="50%">
<tr>
<td width="40%">Imię:</td>
<td width="60%">{$name}</td>
</tr>
<tr>
<td width="40%">Nazwisko:</td>
<td width="60%">{$surname}</td>
</tr>
<tr>
<td width="40%">E-mail:</td>
<td width="60%">{$email}</td>
</tr>
</table>
</body>
</html>

A teraz czas na finał:

<?php
define('OPT_DIR', './lib/');
require('./lib/opt.class.php');
require('./var/myInputComponent.php');
 
try
{
$tpl = new optClass;
$tpl -> root = './templates/';
$tpl -> compile = './templates_c/';
$tpl -> gzipCompression = true;
$tpl -> xmlsyntaxMode = true;
$tpl -> httpHeaders(OPT_HTML);
$tpl -> registerComponent('myInputComponent');

Zaraz po zainicjowaniu parsera, musimy z pomocą metody optClass::registerComponent() zarejestrować nasz komponent. Analogiczne metody istnieją także dla innych elementów składni, np. instrukcji, funkcji, filtrów itd. Moglibyśmy wprawdzie wykorzystać pluginy, lecz akurat w przypadku komponentów używanych po stronie skryptu nie jest to wskazane - pluginy załadują się dopiero w momencie przeparsowania pierwszego szablonu, a my potrzebujemy ich wcześniej. Wersje 1.0.x nie posiadają jeszcze mechanizmu wymuszonego ładowania pluginów. Ponadto zauważmy, że dyrektywą xmlsyntaxMode włączamy tryb kompatybilności z XML-em.

		$components = array();
$components[0] = array(
'title' => 'Imię',
'item' => new myInputComponent('name')
);
$components[1] = array(
'title' => 'Nazwisko',
'item' => new myInputComponent('surname')
);
$components[2] = array(
'title' => 'Adres e-mail',
'item' => new myInputComponent('email')
);

Teraz budujemy nasz formularz, pakując nasze komponenty w sekcję. Zwróćmy uwagę, jak bardzo przydała nam się możliwość ustawiania ich nazw w konstruktorze :).

		if($_SERVER['REQUEST_METHOD'] == 'POST')
{
$valid = 1;
$messages = array();
if(strlen($_POST['name']) < 3)
{
$valid = 0;
$messages['name'] = 'Podane imię jest zbyt
krótkie!'
;
}
if(strlen($_POST['surname']) < 3)
{
$valid = 0;
$messages['surname'] = 'Podane nazwisko jest
zbyt krótkie!'
;
}
if(!preg_match('/(.+)\@(.+)\.(.+)/',
$_POST['email']))
{
$valid = 0;
$messages['email'] = 'Podany adres e-mail
ma nieprawidłowy format.'
;
}

Następnie idzie trochę technikaliów, czyli kontrola poprawności danych w przypadku wypełnienia formularza. Jeśli napotkany jest błąd, wprowadzamy odpowiedni komunikat do tablicy $messages i ustawiamy zmienną $valid na 0.

			if(!$valid)
{
$tpl -> assign('validForm', false);
$tpl -> assign('messageBuffer', $messages);
$tpl -> assign('components', $components);
$tpl -> parse('szablon7.tpl');
}
else
{
$tpl -> assign('name', $_POST['name']);
$tpl -> assign('surname', $_POST['surname']);
$tpl -> assign('email', $_POST['email']);
$tpl -> parse('szablon7_wyniki.tpl');
}

Jeżeli formularz nie został wypełniony poprawnie, ładujemy go z powrotem, wysyłając przy okazji informacje o znalezionych błędach. Reszta, tj. załadowanie błędnych danych, to już sprawa dla komponentu. Poprawnie wprowadzone dane spowodują wyświetlenie szablonu szablon7_wyniki.tpl, który zaprezentuje je internaucie.

		}
else
{
$tpl -> assign('validForm', true);
$tpl -> assign('components', $components);
$tpl -> parse('szablon7.tpl');
}
}
catch(optException $exception)
{
optErrorHandler($exception);
}
?>

Na koniec sytuacja, gdy formularz jest wyświetlany po raz pierwszy. Informujemy parser, że tu jest wszystko OK i ma tylko wyświetlić komponenty bez żadnych ponagleń, użalań i innych. To już koniec. Możesz przetestować działanie skryptu.

Komponenty stwarzają ogromne pole do popisu dla programisty. Można je wykorzystać do wielu rozmaitych rzeczy, a każdy z pewnością znajdzie dla nich jakieś nowe zastosowanie. Ich główną zaletą jest możliwość ukrycia całej implementacji i przeniesienie jej w wydzielone miejsce - ni to szablon, ni to skrypt. Dobrze zaprojektowane, automatyzują, organizują projekt i przyspieszają prace. Aktualnie trwa testowanie oficjalnego dodatku do OPT nazwanego "Open Power Forms". Jest to ogromna biblioteka, która przejmuje na siebie wszystkie aspekty komunikacji skryptu z internautą: wysyłanie kodu HTML, przetwarzanie szablonu, pobieranie informacji o internaucie itd. Jednym z jej elementów jest system kontroli danych sprzężony z komponentami OPT. Zestaw specjalnie napisanych do tego celu komponentów automatycznie łączy się z obiektem formularza i pobiera z niego informacje o każdym z pól. Dodatkowo, komponenty te samodzielnie potrafią zająć się kwestią wizualną i inaczej pokolorować wszystkie źle wypełnione kontrolki. OPF był już wykorzystany w rozbudowanym projekcie i sprawdza się w nim znakomicie, udowadniając przy okazji, że dobrze współpracuje z OPT.

Zakończenie

Open Power Template, oprócz wielu możliwości, oferuje także dużą wydajność. Takie zbilansowanie to efekt ponad półtorarocznej ewolucji od małej biblioteczki do poważnego projektu. Artykuł ten zaprezentował tylko fragment możliwości OPT, niemniej mam nadzieję, że wzbudził on w Tobie zainteresowanie i że wkrótce sam sięgniesz po więcej, zaopatrzony w dokumentację i dobry program do analizy kodu źródłowego :). Wszelkie pytania możesz kierować na forum projektu pod adresem http://openpb.net/forum, a ewentualne błędy do publicznego bugtrackera: http://openpb.net/bugs/. Życzę owocnego korzystania z OPT!

Autor: Tomasz "Zyx" Jędrzejewski, www.zyxist.com

 

Źródło: http://artykuly.zyxist.com/czytaj.php/wprowadzenie_do_opt2

Licencja: Creative Commons - użycie niekomercyjne - bez utworów zależnych