Invenzzia »

Pages: [1]   Go Down
  Print  
Author Topic: OPTv2: Walidacja szablonów  (Read 846 times)
0 Members and 2 Guests are viewing this topic.
Kozack
User

Offline Offline

Posts: 5


View Profile
« on: September 29, 2010, 18:48:06 »

Chcę, aby szablon był automatycznie weryfikowany przed kompilacją. Mam na myśli m.i.n sprawdzanie czy wymagane atrybuty HTML-a zostały określone (np. ALT w IMG). Oprócz walidacje chcę, aby kod był automatycznie poprawiany np. kod:

Code:
<a href="news,index.html">Aktualności</a>

zostałby automatycznie zamieniony na (dodana domena oraz atrybut TITLE):

Code:
<a href="http://moja_domena.pl/news,index.html" title="Aktualności">Aktualności</a>

To pozwoli na zaoszczędzenie czasu podczas pisania szablonów oraz zmniejszy liczbę błędów.


Udało mi się uzyskać taki efekt poprzez zarejestrowanie nowej instrukcji (opt:validate) oraz umieszczanie całego kodu szablonu (lub wybranych części) właśnie w takim znaczniku:

Code:
class Template_Validator extends Opt_Compiler_Processor {

protected $_name = 'validator';

public function configure() {
$this->_addInstructions(array('opt:validate'));
}

public function processNode(Opt_Xml_Node $node) {

// tutaj następuje walidacja oraz poprawianie kodu

$this->_process($node);
}

}

$OPT->register(Opt_Class::OPT_INSTRUCTION, 'Validator', 'Template_Validator');

<opt:root>
<opt:validate>

   tutaj szablon

</opt:validate>
</opt:root>

Jest to o tyle problematyczne, że trzeba dodawać dodatkowe tagi do każdego szablonu o czym nie każdy może pamiętać, co będzie skutkowało brakiem walidacji. Próbowałem przekierować instrukcję opt:root do mojej klasy, ale wtedy przestają działać snippety :)

Czy sposób w jaki realizuję takie funkcjonalności jest poprawny? Czy istnieje inny, lepszy sposób na uzyskanie takiego efektu?
Logged
Zyx
Your programmer
Administrator
User
*****
Offline Offline

Posts: 291



View Profile WWW
« Reply #1 on: September 30, 2010, 07:04:45 »

Jeśli chodzi o poprawianie odnośników, preferuję zarejestrowanie jawnej instrukcji url() odwołującej się do routera, która generuje takowe zgodnie z aktualnymi ustawieniami. Ostatecznie nie zawsze jest potrzeba dodawania pełnej nazwy domeny do adresu.

W przypadku samego kompilatora trzeba zarejestrować jakąś instrukcję i sprawić, by w jakiś sposób była wywoływana. Jeśli chcesz podmienić opt:root, to najlepiej jest po prostu wziąć kod tej instrukcji i go rozszerzyć, a nie próbować pisać ją od zera. Tylko tutaj pamiętaj o małej rzeczy, mianowicie w OPT 2.1 implementacja tego znacznika jest nieco zmieniona.
Logged

PozDrX, Zyx
---Invenzzia group---
Kozack
User

Offline Offline

Posts: 5


View Profile
« Reply #2 on: October 06, 2010, 20:59:13 »

1. Na to nie wpadłem :) Zrobiłem teraz tak i wszystko ładnie działa:

Code:
class Template_Validator extends Opt_Instruction_Root {

protected $_name = 'validator';

public function configure() {
$this->_addInstructions(array('opt:root'));
}

public function processNode(Opt_Xml_Node $node) {

parent::processNode($node);

// tutaj następuje walidacja szablonu
}

}

Mógłbym zarejestrować funkcję lub nową instrukcję, która generowałaby URL-e, ale nie o to mi chodzi. Celem tej modyfikacji jest skrócenie czasu pracy poprzez zmniejszenie ilości kodu, który musimy wpisać, aby wygenerować poprawy szablon (a w efekcie kod HTML). Np.:

a) w 90% przypadków zawartość atrybutu TITLE jest taka sama jak zawartość ALT, więc jeżeli nie podam TITLE, to zostanie on automatycznie skopiowany z ALT-a,

b) 90% obrazków jest trzymana zawsze w tym samym katalogu; zamiast za każdym razem pisać pełną ścieżkę, możemy wpisać po prostu nazwę obrazka; jeżeli nie zostanie podana pełna ścieżka, to zostanie dodany domyślny katalog.

Może powyższe przykłady może są jeszcze, aż tak uciążliwe, żeby pisać dodatkowy kod od ich obsługi, ale dobrze pokazuje co chcę osiągnąć.


2. Udało mi się skopiować zawartość odsyłacza do atrybutu TITLE, jeżeli nie jest on wypełniony. Przykład:

Zamieniamy <a href="register,index.html">Rejestracja</a>
na <a parse:href="$Domain~'register,index.html'" title="Rejestracja">Rejestracja</a>

Niestety pobieranie zawartości tagu A jest to trochę skomplikowane (w tym przypadku pobieranie tekstu "Rejestracja"). Parsery XML posiadają często metody np. GetValue, które zwracają takie dane. W OPT czegoś takie nie znalazłem (podejrzewam, że nie jest to po prostu potrzebne). Udało mi się osiągnąć taki efekt poprzez iterowanie po elementach podrzędnych (Opt_Xml_Text, Opt_Xml_Cdata oraz Opt_Xml_Expression) i pobieranie ich zawartości (metoda __toString). Istnieje szybszy / lepszy sposób?
Logged
Zyx
Your programmer
Administrator
User
*****
Offline Offline

Posts: 291



View Profile WWW
« Reply #3 on: October 07, 2010, 06:25:09 »

Jest to skomplikowane, ponieważ możesz mieć następującą sytuację:

Code:
<a href="...">Rejestracja {$foo} Tekst</a>

I wtedy masz już tutaj trzy węzły: Opt_Xml_Cdata, Opt_Xml_Expression i znowu Opt_Xml_Cdata zgrupowane wewnątrz Opt_Xml_Text. Niestety takie rozwiązanie było w tym przypadku absolutnym minimum. Napisz sobie jakąś metodę pomocniczą, która dla podanego węzła Opt_Xml_Text sprawdzi czy ma on dokładnie jeden węzeł i czy jest to Opt_Xml_Cdata, a jak tak, to zwróci jego wartość.

Nawiasem mówiąc fajnie, że wspominasz o takich rzeczach, ponieważ faktycznie wykorzystywanie OPT do wykonywania takich rzeczy jest kolejnym z ciekawych zastosowań tego parsera. Jak będę robić wersję 3.0, spróbuję dodać tam jakieś wsparcie do tworzenia tego typu narzędzi.
Logged

PozDrX, Zyx
---Invenzzia group---
hamczu
User

Offline Offline

Posts: 10


View Profile WWW
« Reply #4 on: October 22, 2010, 12:18:52 »

mógłbyś umieścić tutaj kod tej instrukcji którą stworzyłeś?
używam OPT do tego samego celu - poprawianie url'i w znacznikach A i IMG,
dopisywanie alt="" gdy tego atrybutu nie ma itd.
Logged
Kozack
User

Offline Offline

Posts: 5


View Profile
« Reply #5 on: October 22, 2010, 21:31:57 »

Klasa rzuca wyjątek, jeżeli nie podamy atrybutu ALT, kopiuje zawartość ALT od TITLE, jeżeli TITLE jest pusty, kopiuje zawartość A do TITLE, jeżeli TITLE jest pusty itd.. Uprzedzam, że jest to dopiero początek, więc mogą się pojawić błędy (wyciąłem też część kodu, który nie jest istotny np. pobieranie konfiguracji z bazy).

Code:
class Template_Validator extends Opt_Instruction_Root {

        protected $_name = 'validator';

        public function configure() {
                $this->_addInstructions(array('opt:root'));
        }

        public function processNode(Opt_Xml_Node $node) {

                $aConfig = Core_Config::getInstance()->opt['validator'];

                parent::processNode($node);



// Element a.
$a = $node->getElementsByTagName('a');

foreach ($a as $v) {

// Atrybut HREF.
$sHref = (string)$v->getAttribute('href');

if (empty($sHref) === false) {

if (strpos($sHref, 'ftp://') === false && strpos($sHref, 'http://') === false && strpos($sHref, 'https://') === false && strpos($sHref, 'mailto:') === false) {
$v->removeAttribute('href');
$v->addAttribute(new Opt_Xml_Attribute('parse:href', '$opt.const.WEB_HOST~\'' . $sHref . '\''));
}
}

// Atrybut TITLE.
$sTitle = (string)$v->getAttribute('title');
$sParseTitle = (string)$v->getAttribute('parse:title');

// Jeżeli atrybut TITLE jest pusty.
if (empty($sTitle) && empty($sParseTitle)) {

$aChildren = $v->getChildren();

// Jeżeli odsyłacz zawiera tylko jeden element i jest nim Opt_Xml_Text.
if (count($aChildren) === 1 && $aChildren[0] instanceof Opt_Xml_Text) {

$aTitleParts = array();

foreach ($aChildren[0]->getChildren() as $v1) {

if ($v1 instanceof Opt_Xml_Cdata) {
$aTitleParts[] = '\'' . $v1->__toString() . '\'';
}
elseif ($v1 instanceof Opt_Xml_Expression) {
$aTitleParts[] = $v1->__toString();
}
}

if (isset($aTitleParts[0])) {
$v->addAttribute(new Opt_Xml_Attribute('parse:title', implode('~', $aTitleParts)));
}
}
}
}


                // Element img.
                $img = $node->getElementsByTagName('img');

                foreach ($img as $v) {
                      


// Atrybuty ALT i TITLE.
$sAlt = (string)$v->getAttribute('alt');
$sParseAlt = (string)$v->getAttribute('parse:alt');

if (empty($sAlt) && empty($sParseAlt))
throw new Exception('IMG: Atrybut ALT jest wymagany.');

                      


$sTitle = (string)$v->getAttribute('title');
$sParseTitle = (string)$v->getAttribute('parse:title');

if (empty($sTitle) && empty($sParseTitle)) {

if (empty($sAlt) === false)
$v->addAttribute(new Opt_Xml_Attribute('title', $sAlt));
elseif (empty($sParseAlt) === false)
$v->addAttribute(new Opt_Xml_Attribute('parse:title', $sParseAlt));
}

// Atrybut SRC.
$sSrc = (string)$v->getAttribute('src');
$sParseSrc = (string)$v->getAttribute('parse:src');

if (empty($sSrc) && empty($sParseSrc))
throw new Exception('IMG: Atrybut SRC jest wymagany.');

if (empty($sSrc) === false) {

if (strpos($sSrc, 'http') === false) {
$v->removeAttribute('src');
$v->addAttribute(new Opt_Xml_Attribute('parse:src', '$opt.const.WEB_IMG~\'' . $sSrc . '\''));
}
}

                }
              


// Element input[type="image"].
$input = $node->getElementsByTagName('input');

foreach ($input as $v) {

// Atrybut SRC.
$sSrc = (string)$v->getAttribute('src');
$sParseSrc = (string)$v->getAttribute('parse:src');

if (empty($sSrc) === false && empty($sParseSrc)) {

if (strpos($sSrc, 'http://') === false && strpos($sSrc, 'https://') === false) {
$v->removeAttribute('src');
$v->addAttribute(new Opt_Xml_Attribute('parse:src', '$opt.const.WEB_IMG~\'' . $sSrc . '\''));
}
}
}

        }

}


Postaram się to szerzej opisać i zamieścić cały kod, jak znajdę trochę czasu ;)
« Last Edit: October 22, 2010, 21:41:29 by Kozack » Logged
Zyx
Your programmer
Administrator
User
*****
Offline Offline

Posts: 291



View Profile WWW
« Reply #6 on: October 23, 2010, 10:31:52 »

Wrzuć go na wiki :)
Logged

PozDrX, Zyx
---Invenzzia group---
hamczu
User

Offline Offline

Posts: 10


View Profile WWW
« Reply #7 on: November 24, 2010, 17:09:49 »

czy nie jest problemem że ta walidacja w znaczniku root odbywa się przy każdym wywołaniu szablonu?
patrzyłem na scache'owane pliki i nie było w nich tego, stąd wniosek że jest to dynamiczne.
przy wiekszej ilości szablonów i znaczników to chyba może być problem wydajnościowy?
chyba żeby cachować gotowego htmla ?
Logged
Kozack
User

Offline Offline

Posts: 5


View Profile
« Reply #8 on: November 24, 2010, 19:40:31 »

Jestem pewien, że powyższy kod uruchamia się tylko podczas kompilacji szablonów. Np. w szablonie mam:

Code:
<p><a href="#oblicz-cene-pobytu">Oblicz cenę pobytu</a></p>

A w cache'u:

Code:
<p><a href="#oblicz-cene-pobytu" title="<?php  echo 'Oblicz cenę pobytu';   ?>">Oblicz cenę pobytu</a></p>
Logged
Zyx
Your programmer
Administrator
User
*****
Offline Offline

Posts: 291



View Profile WWW
« Reply #9 on: November 24, 2010, 20:20:32 »

Taka mała uwaga: jak atrybuty mają wartości statyczne, OPT umożliwia wyłączenie generowania dla nich kodu PHP :). Po pierwsze, w przypadku atrybutów można w ogóle ustawić zwykłą wartość poprzez setValue(), a jeśli coś mamy w buforach kodu, możemy dorzucić atrybut:

Code:
$node->set('nophp', true);

I wtedy zawartość buforów zostanie potraktowana jako treść statyczna, tj. nie zostaną wygenerowane otaczające "<?php" "?>" - to samo działa też dla węzłów atrybutów.
Logged

PozDrX, Zyx
---Invenzzia group---
Pages: [1]   Go Up
  Print  
 
Jump to:  

Subject Started by Replies Views Last post
OPF: Czy ktoś to robi? hash 5 2135 Last post June 11, 2008, 15:51:21
by hash
OPTv2: Widoczny prolog w trybie debug i blad wyswietlania strony bez niego. gorky_park 1 599 Last post October 30, 2009, 11:39:13
by Zyx
OPTv2: opt:extends i zmienne metaxy 1 950 Last post April 07, 2009, 13:47:11
by Zyx
OPL for Zend Framework ersonic 1 274 Last post March 09, 2011, 21:27:24
by Zyx
OPTv2: opt:snippet a może coś innego metaxy 13 2301 Last post April 17, 2009, 13:56:55
by Zyx