Zpět

Začínáme s PHP - díl 2.

Dynamické skládání stránek

Ve druhém dílu seriálu o tvorbě dynamických doplňků na webové stránky si povíme něco o tom, jak číst soubory a vkládat jeden do druhého.

K čemu nám to bude?

Většina internetových stránek obsahuje kromě hlavní části s "užitečným nákladem" i různé pomocné navigační lišty, menu, loga, hlavičky, patičky a podobně. Zatímco obsah je téměř vždy na každé stránce jiný, navigační menu bývá obvykle všude stejné. Ruční kopírování stejného kusu kódu do spousty HTML souborů je otrava, navíc jestli se v něm někdy něco změní, museli bychom to dělat znovu. Proto je výhodné napsat tento kód jenom jednou, odděleně od ostatních stránek, a pak ho s nimi jenom nějak propojit.

Jak by to šlo udělat?

U čistě statických stránek máme jedinou možnost: rámy (tagy frameset nebo iframe), kdy v jednom rámu je zobrazena stránka s navigační lištou a ve druhém stránka s obsahem (rámů může být samozřejmě i víc). Pro ukázku nemusíme chodit daleko, právě se na jednu takovou stránku díváte. Výhodou je celková jednoduchost tvorby, vzájemná nezávislost stránek v jednotlivých rámech a možnost zobrazit nebo vytisknout si kteroukoli stránku bez rušivých lišt a hlaviček. Nevýhodou je horší přehlednost při přístupu přes vyhledávače (pomocné odkazy pro zobrazení chybějících rámů to sice řeší, ale dají skoro stejně práce jako kopírování celého navigačního menu) a taky právě ta zmíněná nezávislost stránek, kvůli které musí být v každé znovu a znovu obsažen všechen HTML balast jako jsou hlavičky, definice stylů a tak.

Mezistupněm mezi statickou a dynamickou stránkou je

menu vkládané Javascriptem

(nebo obecně jakýmkoli skriptem na straně prohlížeče). Všechny stránky i navigační lišty jsou sice statické a neměnné, ale skript umožní sestavit je dohromady bez únavného ručního kopírování. Pokud byste si to chtěli vyzkoušet, následuje malý příklad. Pokud vás Javascript nezajímá, můžete rovnou přeskočit na další odstavec ↓.

<html>
<body>
<script type="text/javascript" src="menu.js"></script>

...vlastní obsah stránky...

</body>
</html>

Soubor menu.js by mohl vypadat třeba takhle:

document.write('<img src="logo.jpg"><br>');
document.write('<a href="1.htm">První stránka</a> | ');
document.write('<a href="2.htm">Druhá stránka</a> | ');
...atd...
document.write('<a href="p.htm">Poslední stránka</a>');
document.write('<hr>');

Tím jsme si u horního okraje stránky vytvořili jednoduchou lištu s obrázkem a řadou odkazů, naspodu ohraničenou vodorovnou čárou (v praxi bychom to nejspíš ještě obalili patřičně ostylovaným odstavcem). Šlo by to samozřejmě i jinak, tohle je jenom ten nejjednodušší způsob, jaký mě zrovna napadl.

Výhodou je rychlost zobrazování, protože statické stránky server nemusí nijak předžvýkávat. Nevýhodou je, že návštěvník bez podpory Javascriptu nemá šanci (na drtivé většině prohlížečů to sice nehrozí, ale někdo ho třeba může mít z nějakého důvodu zakázaný).

Konec odbočky do světa Javascriptu, zpět k PHP. Tam máme možností mnohem víc, hlavně ve větším výběru možných způsobů vkládání textů a kódu. Než se pustíme do technických záležitostí, položme si nejdříve jednu otázku spíše filosofického rázu: chceme do stránek vkládat menu, nebo obsah?

Vkládání menu

vypadá podobně jako výše uvedený javascriptový příklad: máme php soubor se vším všudy včetně užitečného obsahu, ale v místě, kde chceme mít navigační lištu (případně i všechny hlavičky, patičky, styly a jiná metadata), bude jenom nějaký krátký příkaz, který ji tam vloží.

Tento systém je vhodný pro ručně psané stránky a sólové projekty, kde si chceme jenom ušetřit práci s vkládáním opakujícího se kódu. Výhoda je také v možné variabilitě: pokud skriptem vkládáme pouze navigaci, můžeme si klidně udělat každou stránku v úplně jiném stylu (i když se to v praxi většinou nedělá).

Vkládání obsahu

na to jde opačně. Užitečný obsah stránky máme uložený zvlášť, bez jakýchkoli metadat. O ta se postará hlavní stránka, která si do sebe soubor s obsahem natáhne.

Tento způsob je vhodný pro redakční systémy a týmové projekty, kde mají všechny stránky jednotný vzhled a obsah se má vytvářet třeba i jinými cestami než jenom nahráváním jednotlivých stránek na server - to je jeho největší výhoda. Poněkud omezující může být nutnost používat jednotný styl, se kterým se obvykle z jednotlivých obsahových stránek nedá hnout.

Zásadní rozdíl mezi těmito dvěma přístupy je v tom, že zatímco při vkládání menu máme pro každou stránku samostatný, přímo adresovatelný php soubor, při vkládání obsahu máme takový soubor pouze jeden a stránky si vybíráme pomocí parametrů, obvykle předávaných metodou GET (tj. přes adresní řádek).

Dost teorie! Jak se dělá to vkládání?

První a nejjednodušší možnost jsou příkazy include a require, které přímo do stránky vloží celý soubor (stejně jako u echa můžeme parametr uzavřít do závorky, ale není to povinné):

include 'soubor.txt';
require 'adresar/skript.php';

Rozdíl mezi nimi je v tom, co se stane, když daný soubor nenajdou: include místo vložení souboru vypíše chybovou hlášku (případně neudělá nic, pokud jsou hlášky zakázané - viz minulý díl) a zbytek skriptu se normálně provede. Require v takovém případě způsobí, že se skript vůbec nespustí, podobně jako při syntaktické chybě.

Je třeba počítat s tím, že vložený soubor bude v režimu HTML, de facto jako kdyby se include po vyhodnocení změnil na tohle (ono to tak nějak nejspíš opravdu je):

?> ...obsah souboru... <?php

To znamená, že pokud je ve vkládaném souboru nějaký PHPčkový kód, musí být uzavřen do <?php ... ?>.

Vkládat obsah z jiných serverů se dá jenom když je v konfiguraci PHP nastaveno allow_url_include=1, což obvykle z bezpečnostních důvodů nebývá.

Samozřejmě pozor na nekonečné smyčky. Pokud se dva soubory includují navzájem, server se zacyklí a nakonec to vzdá, protože mu dojde paměť.

Tolik k přímému vkládání celých souborů. Co když ale potřebujeme soubor před vypsáním nějak upravit, nebo prostě jenom natáhnout do proměnné a něco z něj přečíst?

Nejjednodušší je použít funkci file_get_contents, která načte celý soubor do jednoho dlouhého stringu:

$obsah = file_get_contents('soubor.txt');

Potom s ním můžete pracovat stejným způsobem jako např. s textem zadaným z textarea. Další užitečná funkce je file():

$radky = file('soubor.txt');

Funkce načte daný soubor a uloží ho do pole stringů, kde každý prvek představuje jeden řádek a indexy jsou čísla od 0. Načtou se i prázdné řádky a navíc na konci každého řádku zůstane připojen zalamovací znak (pro textové soubory z prostředí DOSu a Windows je to \r\n (CR+LF), pro Unix pouze \n). To se ovšem dá změnit, pokud přidáme i nepovinný druhý parametr. Do něj můžeme zadat několik předdefinovaných konstant, např. FILE_SKIP_EMPTY_LINES (prázdné řádky nebudou načteny) nebo FILE_IGNORE_NEW_LINES (na koncích řádků nebudou zalamovací znaky), kompletní seznam najdete v manuálu. Doufám, že už nemusím připomínat, že ta písmena opravdu musí být všechna takhle velká ;-). Hodnoty pro druhý parametr jsou aditivní, tj. dají se sčítat nebo orovat (operátorem "bitové nebo", čili |) dohromady a pak působí všechny. Příklad:

$radky=file('seznam.txt',FILE_IGNORE_NEW_LINES+FILE_SKIP_EMPTY_LINES);

U FILE_IGNORE_NEW_LINES ovšem musíme dávat velký pozor na formát konců řádků. Funkce totiž normálně předpokládá unixovské jednoznakové \n, ale jestli jste daný soubor psali třeba ve windowsím Notepadu, bude mít formát \r\n. Konce řádků by se při přenosu přes FTP v režimu ASCII měly automaticky zkonvertovat, ale kdybyste z nějakého důvodu museli nahrávat binárně nebo prostě chcete mít jistotu, použijte před prvním použitím funkce file tento příkaz:

$auto_detect_line_endings=1;

Tím se zapne automatická detekce formátu konců řádků a zalamováky pak budou odstraněny bez problémů. Další možná hodnota je 0, která detekci vypne.

Soubor jsme si tedy načetli do pole a teď co s ním? Zřejmě ho budeme chtít projít řádek po řádku a něco s nimi udělat. Na procházení polí je nejvýhodnější použít cykly for a foreach.

Cyklus for se píše úplně stejně jako v C:

for (inicializační příkaz; ukončovací podmínka; posouvací příkaz) příkaz;

Jestli se má vykonat víc příkazů než jeden, musíme je zavřít do složených závorek:

for (...)
 {
 příkaz;
 příkaz;
 };

nebo alternativně mezi dvojtečku a ukončovací slovo:

for (...):
 příkaz;
 příkaz;
endfor;

Inicializační příkaz slouží k nastavení počáteční hodnoty řídicí proměnné. Protože chceme procházet pole od nultého prvku, nastavíme ji na nulu.

Ukončovací podmínka říká, jak dlouho má cyklus běžet (má význam "dokud tohle platí", stejně jako např. v cyklu while, který si popíšeme jindy). Obvykle se v ní kontroluje hodnota řídicí proměnné, ale teoreticky jde použít i něco jiného. My si sem dáme podmínku, že řídicí proměnná (neboli index v poli) musí být menší než počet prvků v poli. Vzhledem k počítání od nuly bude index posledního řádku o 1 menší než počet řádků, takže to sedí.

Posouvací příkaz se provede na konci každé iterace cyklu a obvykle mění hodnotu řídicí proměnné. V našem případě k ní budeme přičítat jedničku.

Každý z těch tří výrazů může být i prázdný; v případě ukončovací podmínky to znamená, že cyklus poběží donekonečna.

Náš cyklus tedy bude vypadat takhle (dejme tomu, že si řídicí proměnnou pojmenujeme třeba $t):

$pocet=count($radky);
for ($t=0; $t<$pocet; $t++) ...zpracování $t-tého řádku ($radky[$t])... ;

Funkce count() dává počet prvků v poli. Proměnnou $pocet jsme použili proto, aby se funkce zbytečně nevolala v každé iteraci cyklu.

Operátory "menší než" (<), "větší než" (>), "menší nebo rovno" (<=) a "větší nebo rovno" (>=) fungují stejně jako ve všech ostatních jazycích (snad kromě Cobolu :-) ).

Unární operátor ++ znamená "zvětši tuhle proměnnou o 1". Stejně tak existuje --, který ji naopak o 1 zmenší.
Při použití ve výrazech záleží na tom, jestli je operátor k proměnné přilepený zleva nebo zprava: $a=$b++; znamená "do $a vlož $b a potom $b zvyš o 1", naopak $a=++$b; znamená "$b zvyš o 1 a potom ho ulož do $a". Použít se dá i na levé straně přiřazení. Dají se pomocí něj psát krátké, ale velmi "vydatné" příkazy. Ovšem je to trochu na úkor přehlednosti, takže opatrně.

Stejně tak jsme v tom cyklu mohli použít univerzální přičítací operátor += a napsat $t+=1 ("k $t přičti 1"). Na odčítání existuje analogicky -=.

A nebo jsme mohli použít úplně nejjednodušší, ale zato nejdelší zápis: $t=$t+1. Možností je spousta a je úplně jedno, kterou si vyberete.

Procházení pole se ovšem dá vyřešit i pohodlněji - příkazem foreach (doslova "pro každý"), který je přímo k tomuto účelu určený. V případě indexů, které netvoří číselnou řadu, dokonce ani jinou možnost nemáme. Jeho syntaxe je:

foreach ($pole as $prvek) příkaz;

nebo

foreach ($pole as $index => $prvek) příkaz;

V prvním případě se daný příkaz vykonává v cyklu pro každý prvek daného pole a v každé iteraci bude mít k dispozici proměnnou $prvek, ve které bude uložena kopie hodnoty právě procházeného prvku toho pole.

Ve druhém případě příkaz dostane kromě hodnoty prvku i jeho index, takže vždycky víme, kde zrovna jsme. Operátor => se v PHP obecně používá k indexování. Kromě foreach ho můžete potkat např. při výpisu nějakého pole funkcí print_r() nebo při tvorbě pole funkcí Array(). Zatím ho necháme být.

Zápis pro více příkazů:

foreach (...)
 {
 příkaz;
 příkaz;
 };

nebo alternativně:

foreach (...):
 příkaz;
 příkaz;
endforeach;

Další možností, jak číst ze souborů, jsou funkce známé z Céčka: fopen, fgetc, fgets, fread, fscanf atd.. Ale vzhledem k tomu, že je v celém tomhle seriálu nebudeme ani jednou potřebovat, nemá cenu o nich psát.

Jen pro úplnost ještě uvedu, že se PHP dá výhodně kombinovat i s rámovým designem a statickými stránkami: stačí echem vložit do atributu src v tagu frameset nebo iframe příslušnou adresu a je to. Jestli chcete příklad, prohlédněte si odkaz "zobrazit navigační lištu" na konci téhle nebo libovolné jiné stránky tohoto webu. Soubor index.php, na který ukazuje, obsahuje frameset a parametr "stranka" se přímo použije pro src pravého rámu.

A co nějaký reálný příklad, třeba na to vkládané menu?

Dejme tomu, že chceme vložit hlavičku HTML, navigační lištu a patičku s podpisem. Vezmeme to od konce - soubor s patičkou by mohl vypadat třeba takhle:

<p class="paticka">Stránku spravuje <a href="mailto:mrakoplas@nu.am">MgDr. Mrakoplaš</a>.</p>
<p class="reklama">Sponzoruje Kolík ASP, uzeniny všeho druhu!</p>
<p><a href="index.php">Zpět na hlavní stránku</a></p>
</body>
</html>

Protože patička bude nejspíš pro všechny stránky stejná, postačí pro její vložení jednoduchý include:

Require radši ne, protože kdyby se náhodou soubor s patičkou ztratil, je lepší potkat chybovou hlášku na konci stránky než místo celé stránky.

S hlavičkou bychom to mohli udělat podobně, ale je tu jeden háček: titulek stránky v tagu <title>, který pravděpodobně chceme mít na každé stránce jiný. Tím pádem statický soubor nestačí, musíme ho sestavit dynamicky podle požadavků v okamžiku vložení. Protože si chceme práci ušetřit a ne přidělat, napíšeme si na to funkci, která dostane titulek v parametru a pomocí echa vytvoří kompletní hlavičku:

<?php
function pis_hlavicku($titulek)
{
//hlavička:
echo '<html><head>'
echo '<meta http-equiv="content-type" content="text/html;charset=windows-1250">';
echo '<title>'.$titulek.'</title>';
echo '<link rel="stylesheet" type="text/css" href="styly.css">';
echo '</head><body>';
//navigace:
echo '<p><a href="motyli.php">sbírka motýlů</a>, <a href="zaby.php">sbírka žab</a></p><hr>';
}
?>

Řádek s titulkem by šel zapsat i jinak, s využitím rozbalování proměnných v řetězcích ohraničených uvozovkami:

echo "<title>$titulek</title>";

To je věc osobního vkusu každého programátora. Já radši pro veškeré HTML používám apostrofy, abych nemusel dělat tohle:

echo "<link rel=\"stylesheet\" type=\"text/css\" href=\"styly.css\">";

Echa nezalamují řádky, takže jestli chcete, aby se na výsledný HTML kód dalo koukat, připište si na konec každého řádku zalamovací znak \n. Ale z hlediska prohlížečů je to jedno.

Jo, abych to nezamluvil: objevila se nám tu definice funkce. Ta vždycky začíná klíčovým slovem function. Za ním je povinně závorka, která je buď prázdná nebo obsahuje jména formálních parametrů (pokud jich je víc než jeden, oddělují se čárkami). Parametry začínají na $ jako každá jiná proměnná a stejně tak se u nich neuvádějí žádné datové typy. Volají se hodnotou, ne odkazem (to sice taky jde, ale zatím to nepotřebujeme). Tělo funkce je uzavřeno ve složených závorkách (žádná alternativní syntaxe na to není). Případná návratová hodnota se předá příkazem return, např.:

function cislo_slovy($cislo)
{
if ($cislo==1) return 'Jedna';
          else return 'Něco jiného';
}

echo cislo_slovy(20); //vypíše "Něco jiného"

Zpátky k původnímu příkladu. Funkci na vypsání hlavičky máme a uložíme si ji do samostatného souboru, třeba hlavicka.php. Před jejím zavoláním ovšem musíme soubor do stránky vložit, aby interpret věděl, jak funkce vypadá (definice funkcí sice můžou být až za místem jejich prvního použití, ale to platí jen když jsou v tom samém skriptu). Na to můžeme použít starý známý require (nebo include, v případě problémů by skript spadnul tak jako tak) a celá stránka motyli.php potom může vypadat třeba takhle:

<?php
require 'hlavicka.php';
pis_hlavicku('Sbírka motýlů');
?>

<h1>Moje sbírka motýlů</h1>
<p>Babočky, otakárci, bělásci, moli...</p>
<p>(fotky budou, až najdu foťák)</p>

<?php include 'paticka.htm'; ?>

V praxi by byl obsah stránky samozřejmě mnohem delší a hlavička s patičkou by tedy představovaly jenom mizivé procento. Jakákoli změna v nich by se samozřejmě okamžitě promítla do všech stránek, které je používají.

A jak by vypadalo to vkládání obsahu?

Tady si nejdřív musíme vymyslet způsob výběru stránky. Dejme tomu, že použijeme metodu GET a přes adresní řádek budeme předávat přímo jména souborů i titulek stránky, třeba:

http://mrakoplas.nu.am/index.php?stranka=motyli.htm&titulek=Sbírka motýlů

Kód hlavní stránky index.php by pak mohl vypadat takto:

<html><head>
<meta http-equiv="content-type" content="text/html;charset=windows-1250">
<title>
<?php echo $_GET['titulek'] ?>
</title>
<link rel="stylesheet" type="text/css" href="styly.css">
</head><body>
<p><a href="index.php?stranka=motyli.htm&titulek=Sbírka motýlů">sbírka motýlů</a>,
   <a href="index.php?stranka=zaby.htm&titulek=Sbírka žab">sbírka žab</a></p><hr>

<?php include $_GET['stranka'] ?>

<p class="paticka">Stránku spravuje <a href="mailto:mrakoplas@nu.am">MgDr. Mrakoplaš</a>.</p>
<p class="reklama">Sponzoruje Kolík ASP, uzeniny všeho druhu!</p>
<p><a href="index.php">Zpět na hlavní stránku</a></p>
</body>
</html>

A soubor motyli.htm by bylo holé HTML bez hlaviček:

<h1>Moje sbírka motýlů</h1>
<p>Babočky, otakárci, bělásci, moli...</p>
<p>(fotky budou, až najdu foťák)</p>

No jo, ale má to nejméně dva háčky. První jsou česká písmena s diakritikou a mezery v titulku, které by v adresním řádku dělaly neplechu. To by se dalo vyřešit buď ručním nahrazením problematických znaků ASCII kódem v šestnáctkovém tvaru (např. mezera je %20) nebo funkcí urlencode(), která to udělá za nás. Navigační lišta by se tedy mohla změnit třeba na tohle:

<p><a href="index.php?stranka=motyli.htm&titulek=
<?php echo urlencode('Sbírka motýlů') ?>
">sbírka motýlů</a>, <a href="index.php?stranka=zaby.htm&titulek=
<?php echo urlencode('Sbírka žab') ?>
">sbírka žab</a></p><hr>

Nic jiného než odkazy se nezmění. Pole $_GET se rozkódovává automaticky, takže funkci urldecode() vůbec nebudete potřebovat.

Samozřejmě je vhodné používat nějakou přehlednou úpravu (odsazování a tak), abychom se v napsaném kódu později vyznali - výše uvedený příklad berte spíš jako odstrašující :-).

Druhý háček je v tom, že si do adresního řádku může kdokoli cokoli ručně napsat. Možná si říkáte: no a co? Stránku mi nepředělá a je jeho věc, co se mu v prohlížeči zobrazí, ne? Ovšem jednou se může třeba na vašem oblíbeném fóru objevit vzkaz:

Hele, mrkněte se na Mrakoplašův blog - to je hustý, co?
[url]http://mrakoplas.nu.am/index.php?stranka=http://www.pecko.com/hardcore.html&titulek=Co%20jsem%20delal%20o%20prazdninach[/url]

Zrovna tohle je celkem neškodný vtípek, ale kdyby tak vypadala internetová banka a někdo by si tímhle způsobem vytáhl ze zákazníků přihlašovací údaje, to by byl prů...duch. Také by se dal zobrazit soubor s hesly nebo něco podobného. Této technice útoku na stránky se říká php injection a obrana proti ní je základ, bez kterého se neobejdeme.

Takže co s tím?

Je potřeba nějak zařídit, aby šly zobrazovat jenom ty stránky, u kterých to dovolíme. Způsobů, jak to konkrétně naprogramovat, je nepřeberně, ukážeme si jenom dva nejjednodušší.

1) Natvrdo nakódovaný seznam stránek

Vtip je v tom, že do adresního řádku nepíšeme přímo jména souborů, ale jenom nějaké zástupné identifikátory (teoreticky sice můžou být stejné, ale není to nutné). Ve skriptu potom pomocí příkazu switch určíme, kterému souboru zadaný parametr odpovídá:

switch($_GET['stranka'])
 {
 case 'motylci': $soubor='motyli.htm';
                 $titulek='Sbírka motýlů';
                 break;
 case 'zabky': $soubor='zaby.htm';
               $titulek='Sbírka žab';
               break;
 default: $soubor='zakazano.htm';
 };

A máme tu nový příkaz: switch. Slouží k rozvětvení běhu skriptu do několika možných cest v závislosti na hodnotě nějaké proměnné, kterou uvedeme v závorce za slovem switch (v našem případě $_GET['stranka']). Zbytek příkazu je uzavřen do složených závorek, nebo alternativně mezi dvojtečku a slovo endswitch. Jednotlivé možnosti začínají slovem case, následuje požadovaná hodnota proměnné, dvojtečka a za ní příkazy, které se mají v tomto případě vykonat. Za poslední z nich je potřeba vždycky dát break, který ze switche vyskočí, jinak by se plynule pokračovalo příkazy z následující možnosti, až do konce (což se někdy může hodit, proto to tak je). V našem příkladě jsme si kromě jména souboru nastavili i titulek, abychom ho nemuseli pracně psát do odkazů. Na konec můžete dát slovo default a příkazy, které se mají provést v případě, že hodnota proměnné neodpovídá žádné z uvedených možností. Když tam default není, neprovede se v takovém případě nic. V našem případě jsme si připravili soubor zakazano.htm, ve kterém bude nejspíš nějaké upozornění na chybu v adrese.

Následné využití proměnných $soubor a $titulek už je doufám jasné.

2) Seznam stránek v nějakém samostatném souboru

Switch je jednoduchý a univerzální, ale nutí nás při každém přidání nové stránky přepisovat skripty a tím se pracnost dostává zpátky na úroveň ruční administrace, což nechceme. Výhodnější je mít seznam dovolených jmen uložený v samostatném souboru (nebo v databázi), který si skript přečte a prohledá. Vytvoření takového seznamu je velice jednoduchá záležitost, protože se dá zautomatizovat - například v prostředí DOSu nebo Windows příkazem:

DIR /B /ON *.htm > seznam.txt

Dir je výpis souborů v adresáři. /B říká, že chceme holý (bare) formát - pouze jména bez jakýchkoli doplňujících údajů. /O (order) zapne třídění, N jako name říká, že se má řadit podle jména (seznam ovšem třídit nemusíme, uvádím to jenom pro úplnost). *.htm říká, že chceme vypsat všechny soubory s koncovkou htm (samozřejmě můžete použít i jinou). Operátor > říká, že se seznam nemá vypsat na obrazovku, ale do souboru seznam.txt.

Seznam nahrajeme na server (teoreticky to nemusí dělat jenom administrátor, ale pozor na kolize, kdyby dva lidé těsně po sobě nahráli každý svoji verzi souboru) a do hlavního skriptu napíšeme něco ve smyslu:

$auto_detect_line_endings=1;
$jmena=file('seznam.txt',FILE_IGNORE_NEW_LINES|FILE_SKIP_EMPTY_LINES);
if($jmena and in_array($_GET['stranka'],$jmena)):
 ...v pořádku, stránku můžeme zobrazit...
else:
 ...chyba!...
endif;

Příkaz na druhém řádku načte seznam do pole $jmena, kde v každém prvku bude jedno jméno. Funkci file a její parametry už jsme si vysvětlovali o pár obrazovek výš, stejně jako význam toho prvního řádku.

Třetí řádek rozhoduje, jestli stránku zobrazíme nebo ne. První podmínka je, že proměnná $jmena nesmí být prázdná (prázdná by byla, kdyby se seznam z nějakého důvodu nepovedlo načíst) - v PHP stejně jako v C znamená jakákoli nenulová hodnota ekvivalent logického "true", takže pokud v poli aspoň něco je, je to OK. Druhá podmínka je, že zadané jméno stránky musí být v seznamu. Na to použijeme funkci in_array(), která nám to řekne. Její první parametr je hodnota, kterou v poli hledáme, druhý je to pole a výstupem je true (je tam) nebo false (není tam). Když jsou obě podmínky splněny, stránku vložíme.

Ještě trochu teorie k logickým výrazům. Interpret je vyhodnocuje postupně podle závorek a priority operátorů, v případě stejné priority potom zleva doprava. Jakmile je mu jasný celkový výsledek výrazu, vyhodnocování ukončí (stejně jako např. v Turbo Pascalu v režimu {$B-}). Ve výše uvedeném příkladu tedy nejdříve vyhodnotí prázdnost/neprázdnost pole $jmena a kdyby mu vyšlo false, nebude už ani volat in_array, protože ví, že by na výsledku toho andu nic nezměnila.

Teoreticky bychom místo in_array mohli pole klasicky (tj. for nebo foreach) projít prvek po prvku a postupně je porovnávat se zadaným jménem. Ale bylo by to pomalejší, protože by PHP muselo pracně interpretovat cyklus, zatímco předdefinované funkce jsou v něm zakompilované a pracují s daty přímo.

Ještě by to možná chtělo vysvětlení, proč jsme se při načítání seznamu tak moc chtěli zbavit konců řádků. Je to jednoduché - "stranka.htm\n" je úplně jiný řetězec než "stranka.htm", takže by to in_array nevyhodnotila jako shodu.

V praxi se obvykle seznamy stránek neukládají do souboru, ale do databáze, kde je kromě jména souboru rovnou i titulek, jméno autora a celá řada dalších doplňujících metadat. Často tam bývá uložen i samotný obsah stránky. Výhodou databází je, že bezpečně řeší současné přístupy od několika uživatelů, což je pro větší redakční systémy nezbytné. Databáze si popíšeme v dalších dílech tohoto seriálu. Začneme hned v tom příštím, ve kterém si vytvoříme jednoduché počítadlo návštěv.

Zpět

Reklamy: