V prvním dílu jsem ukázal jednoduchý návod na statické stránky bez použití databáze. V dnešním pokračování náš mikrosystém v jazyce PHP vylepšíme ještě o další úroveň - jak v jednoduchosti nasazení, tak v efektivitě. Nebudeme již řešit zbytečné mod_rewrity a podmínky pro načítání šablon druhé úrovně.  Taktéž si systém připravíme na multijazyčnost. K tomu všemu postačí jediný index.php jakožto jádro aplikace a zároveň kontroler, .htaccess a jedna povinná šablona.

Hned ze začátku se do toho pustíme chytře. V prvé řadě explicitně povolíme výpis chybových hlášení, a to i chyby typu E_STRICT - zapovězené funkce. Například taková ereg_replace již v PHP 6 nemá být, tak proč ji používat, když máme mnohem rychlejší preg_replace. Dále deklarujeme globální pole $tpl. Do něj budeme ukládat vše, co se někde někam ukládat bude. Důvod je jednoduchý: kdekoli v šabloně si ho můžeme vypsat funkcí print_r() a hned dostaneme seznam všech proměnných, které jsme vytvořili. Nezapomeneme ani na referenci k aktuálnímu adresáři, pro správné includování.

/**
 * povolíme chybová hlašení
 * zajímají nás i použití zapovězených funkcí, proto přidáme E_STRICT 
 **/
ini_set('display_errors',1);
error_reporting(E_ALL | E_STRICT);

global $tpl;

$tpl['include_path'] = dirname(__FILE__).'/';

Nyní ale zpět k aplikaci. Jak jsem již zmiňoval, budeme potřebovat soubor .htaccess. V něm spustíme rewrite engine (hezké url) a zakážeme přímé volání šablonových souborů. Sám jsem si již zvykl na příponu .phtml; mohu tak efektivně zakázat spouštění skriptů, které ke spouštění nejsou určeny.

Soubor .htaccess

<FilesMatch "\.phtml$">
  Order Deny,Allow
  Deny From All
</FilesMatch>

RewriteEngine On 
RewriteBase /

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php?path=$1 [L,QSA]

Pravidlo na přepis url je zde jediné: vše budeme směřovat na index.php. Každý řetězec, například i "kategorie/podstrana/" se převede do proměnné $_GET['path'].

Rozparsování GET proměnné

Ani v tom nehledejte nic těžkého. Není nutné vytvářet a složitě přepisovat několik GET proměnných, když můžeme jen jedinou uložit do pole. Odmažeme koncové lomítko (aby se nevytvářel prázdný index) a funkcí explode rozřežeme string podle lomítka. Při té příležitosti si do jiné proměnné uložíme i ošetřený string v původním znění.

/**
 * rozparsovaná get proměnná
 * [path] je pole, v [get_path] je ponechán string, čiže REQUEST_URI relativně od indexu 
 **/
if (!empty($_GET['path'])) {
	$tpl['path'] = explode('/', trim($_GET['path'], '/'));
	$tpl['get_path'] = htmlspecialchars($_GET['path']);
} else {
	$tpl['path'] = array();
	$tpl['get_path'] = '';
}

Šablony: princip načítání

Princip zůstane stejný jako v předchozím případě. Hodnota proměnné se rovná jméno souboru + přípona. My si ale tuto možnost rozšíříme o další úroveň - přesněji o neomezené množství zanoření.

Pokud budeme mít v url například "www.treba.cz/kategorie/", načte se šablona "kategorie.phtml".
Pokud budeme mít v url například "www.treba.cz/kategorie/detail/", načte se šablona "kategorie_detail.phtml".

Lomítko akorát nahradíme za podtržítko a jméno souboru je na světě:

/**
 * zinicializujeme jméno šablonového souboru
 * bez přípony, zatím bez testování existence 
 **/
if (empty($tpl['path'])) {
	$tpl['template'] = 'index';
} else {
	if (count($tpl['path']) == 1) {
		$tpl['template'] = $tpl['path'][0];
	} else {
		$tpl['template'] = implode('_', $tpl['path']);
	}
}

/**
 * seskládáme cestu k souboru
 * path + adresář + soubor + postfix 
 **/
$tpl['template_file'] = $tpl['include_path'].'templates/'.$tpl['template'].'.phtml';

/**
 * pokud soubor existuje, nainkludujeme ho
 * otherwise hledáme šablonu 404.phtml 
 **/
if (file_exists($tpl['template_file'])) {
	// zde budou řádky, o kterých se zmíním záhy      
	include_once $tpl['template_file'];
} else {
	header('HTTP/1.1 404 Not Found');
	include_once $tpl['include_path'].'templates/404.phtml';
}

Jak je vidět ve výše uvedené ukázce kódu, následně jen seskládáme celou cestu k souboru i s adresáři a postfixem, otestujeme ho na existenci a případně načteme. Pokud soubor nenalezneme (v url adrese je zadán nesmysl), pak zašleme 404 hlavičku a k ní patřičný pohled.

Přidaná hodnota: meta description

Než začnu hovořit o ukázkových šablonách, rád bych popsal další funkci, o kterou si systém rozšíříme. Jak je jistě známo, takzvaný meta description je poměrně důležitý prvek na stránce, na který se ale často zapomíná, nebo se naopak zbytečně složitě vymýšlí.

Mě se osvědčil jednoduchý postup: Předpokládáme stánku s jistou textovou částí. Jednoduše vezmeme obsah souboru před tím, než ho zavoláme funkcí include, vytáhneme text z prvního odstavce, který najdeme a uložíme do proměnné $tpl['description']. Ta lze samozřejmě v daném souboru redefinovat, ale proč na to myslet, že ano. Velice jednoduchou cestou máme meta description pro každou z našich podstran. Stačí pár řádků kódu:

$file = file_get_contents($tpl['template_file']);
$pos = mb_strpos($file, '<p', 0, 'utf-8');
if ($pos !== false) {
	$offset_begin = mb_strpos($file, '>', $pos, 'utf-8');
	$offset_end = mb_strpos($file, '</p>', 0, 'utf-8');
	$tpl['description'] = mb_substr($file, $offset_begin+1, $offset_end-$offset_begin, 'utf-8');
	$tpl['description'] = preg_replace('~[[:space:]]+~', ' ', strip_tags($tpl['description']));
} else {
	$tpl['description'] = null;
}

Základní struktura šablon

Nyní ale zpět k šablonám. Ty budou všechny uložené v adresáři templates/. Jelikož se index nestará o žádný HTML výstup a stránka se nevloží nikam mezi hlavičku a patičku, je nutné na ně myslet právě v šabloně. Je to sice jisté odbočení, ovšem vyřešíme tím i unikátní titulek, který si v každé šabloně deklarujeme.

Ukázková šablona index.phtml může tedy vypadat nějak takto:

<?php

/**
 * Šablona
 * podle této šablony mohou vypadat všechny ostatní 
 * proměnnou title inicializovat vždy před inkludem hlavičky, aby tam už proměnná byla vidět
 * v _headeru.phtml je hlavička, ve _footeru je pravý resp. levý sloupec a pata 
 **/

$tpl['title'] = 'Hlavní stránka';
include_once '_header.phtml';

?>

	<h1>Nadpis</h1>
	<p>Text stránky. Tuna může být cokoli.</p>
	<p>A ještě jeden odstavec navíc, ať víme, že se ty meta description generují správně.</p>

<?php include_once '_footer.phtml'; ?>

Tipy a triky na konec

Na začátku článku jsem naznačoval, že zmíněná struktura aplikace nám ulehčí i případnou multijazyčnost. Zase tak jednoduché to nebude: na překlady společných textů nejspíš bude nutné použít nějakou knihovnu. Pokud si ale vystačíme s více soubory pro každou jazykovou mutaci, pak už máme problém vlastně vyřešený.

Pod url "www.treba.cz/en/" se totiž zobrazí šablona "en.phtml", která do sebe může naincludovat anglickou hlavičku i patičku, tudíž nebude problém s anglickým obsahem uvnitř souboru.

Odkazy na styly, JavaScripty a obrázky

Jedná se o častý problém. Mod_rewrite simuluje adresářovou strukturu, ale simuluje ji tak věrně, že i obrázky a CSS styly si myslí, že se mají načítat z jiného adresáře. Problém řeší buď tedy absolutní odkazy, nebo definice HTML tagu <base> v hlavičce stránky. V ukázkovém souboru je zakomentovaná proměnná, kterou si můžete definovat například v indexu a vložit do ní adresu Vašeho webu. Pokud ji tam tedy nevložíte takříkajíc natvrdo. Také si v souboru .htaccess nezapomeňte změnit RewriteBase, pokud web nespouštíte z kořenového adresáře.