Kniha návštěv s PHP a metatable
 x   TIP: Přetáhni ikonu na hlavní panel pro připnutí webu
Reklama
Reklama

Kniha návštěv s PHP a metatableKniha návštěv s PHP a metatable

 

Kniha návštěv s PHP a metatable

Google       Google       5. 8. 2009       25 790×

V tomto článku si ukážeme, jak vytvořit jednoduchou knihu návštěv. Různé články se různí v pohledu na to, kdo by si na takovou knihu návštěv měl troufnout. Ti, kteří znají PHP, i ti, kteří ho neznají, uvidí, že až tak složité to není; rozhodně ne s těmi správnými nástroji.

Reklama
Reklama

Jestliže vás odpudily hrůzné kódy, které se válí různě po internetu, ať jsou pro jiné platformy (skriptovací či další), nebo pro PHP, anebo chcete pochytit pár triků, díky kterým v PHP můžete docílit zajímavých výsledků, pak je tento článek určen pro vás.

Databáze

„Databáze“, nad kterou bude tato kniha návštěv běžet, bude opravdu jednoduchá. Budou se v ní pouze ukládat příspěvky uživatelů, nic víc. U každého příspěvku by mělo stačit jméno přispěvatele, jeho e-mailová adresa, adresa jeho stránek a samotný text příspěvku.

Jako „databáze“, resp. úložiště dat, bude sloužit metatable. Bylo by zbytečné tu popisovat práci s metatable, když je to již napsáno jinde. Buď na již odkazované stránce, nebo ve zdejším článku.

Méně zdatnějším čtenářům tu radši napíšu, jak se vlastně ta „databáze“ vytvoří a jak se k ní připojíme:

${'@table'} = metatable::open(dirname(__FILE__) . '/db/guestbook');

Ano, to je celé.

Jak budou data uložena? Vzhledem k tomu, že bude potřeba příspěvky stránkovat podle data přidání, každý příspěvek v databázi vytvoří záznamy, které budou mít jako klíč (aka řádek, row) datum přidání a v jednotlivých sloupcích budou informace o příspěvku. Pro rychlejší vybírání se bude navíc ještě udržovat řádek, řekněme =, kde název každého sloupce bude klíč k příspěvku:

+---------------------+-------+-------------------+-------------+-------------------------------+
|                     | name  | email             | www         | text                          |
+---------------------+-------+-------------------+-------------+-------------------------------+
| 2009-07-30 00:00:00 | Jan   | jan@example.com   | http://.../ | Super guestbook!              |
+---------------------+-------+-------------------+-------------+-------------------------------+
                                            .
                                            .
                                            .
+---------------------+-------+-------------------+-------------+-------------------------------+
| 2009-07-30 06:06:06 | Jakub | jakub@example.com | http://.../ | Tak tohle se mi opravdu líbí. |
+---------------------+-------+-------------------+-------------+-------------------------------+

+---+---------------------+---------------------+
|   | 2009-07-30 00:00:00 | 2009-07-30 06:06:06 | . . .
+---+---------------------+---------------------+
| = | TRUE                | TRUE                | . . .
+---+---------------------+---------------------+

Zobrazování příspěvků

K zobrazování dat na stránce použijeme at. Co to ten at je? Je to jazyk, který si můžete upravit podle sebe. Nejlepší bude asi odkázat na krátkou sérii článků, které at popisují.

Kód v at pro vypisování příspvěků bude takovýto:

@each {
    @{text | texy}
    <p class="right">
        @{date | date | escape} —
        @on  www   {<a href="@{www | escape}">@{name | escape}</a>}
        @on! www   {@{name | escape}}
        @on  email {&lt;<a href="@{email | mailto}">@{email | mailize}</a>&gt;}
    </p>
}

Stručně se jedná o to, že at prochází předaný text a makra nahrazuje za jejich obsah (který je výsledkem volání některé PHP funkce). Makra začínají symbolem zavináče, poté následuje nějaký text s názvem makra a jeho parametry a nakonec je tu blok ohraničený složenými závorkami.

Makra musíme spojit s funkcemi v PHP. Vytvoříme si tedy instanci at:

${'@at'} = new at;

A teď již nějaké to makro. Třeba to nejjednodušší – makro s prázdným názvem, které vypíše obsah proměnné (to je například to volání @{text | texy}). Aby to nebylo zas tak jednoduché, budeme chtít, abychom mohli proměnné prohnat přes nějaké filtry. Ve zmíněném příkladu je to např. filtr texy, který zavolá formátovač Texy!.

Filtry se budou přidávat jednoduše – vytvoří se proměnná s názvem filtru prefixovaným řetězcem @filter::

${'@filter:texy'} = fn(array(new Texy, 'process'), array(fn::ph()));

Co je to fn() a fn::ph()? Způsob, jak v PHP provádět currying – viz článek PHP curry. V tomto případě by tu ani fn() být nemuselo, ale je lepší to vysvětlit předem.

${'@at'}->fn('', fn('_echo', array(fn::ph())));

function _echo($block)
{
    $filters = preg_split('~\s*\|\s*~', $block[0]);
    $var = $GLOBALS['@' . array_shift($filters)];

    foreach ($filters as $filter)
        $var = call_user_func($GLOBALS['@filter:' . $filter], $var);

    return $var;
}

„Výroba“ makra spočívá v zaregistrování si nějakého callbacku u in­stance at. To dělá metoda at::fn() (pozor, není to to samé jako funkce fn() použitá dříve!), která příjímá jako první argument název makra a jako druhý callback na PHP funkci (v tomto případě opět obalenou pomocí fn(), teď to je ta fn() použitá dříve).

Kód je jednoduchý – rozdělí text mezi složenými závorkami ($block[0]) podle rour pomocí funkce preg_split() a odebere první „filtr“, protože to je vlastně název proměnné. (Pozorování: proměnné nemohou mít v názvu znak |.) Navíc, aby se daly proměnné pro at a ostatní proměnné od sebe odlišit, ty pro at jsou prefixované zavináčem. Poté se na obsah proměnné aplikují požadované filtry. A nakonec výsledek vrátíme (což ho vlastně vypíše na výstup).

Ještě ukážu, jak jsou udělané dva filtry – a to date a escape. Ani pro jeden z nich nemusíme vytvářet vlastní funkci, protože pomocí curry fn() můžeme na základní funkce v PHP navázat výchozí parametry:

${'@filter:escape'} = fn('htmlspecialchars', array(fn::ph(), ENT_QUOTES));

${'@filter:date'} = fn('date', array('j. n. Y, H:i:s', fn::ph()));

V příkladu výše jsou použity další dva filtry – mailto a mailize. Jejich implementace v PHP nejsou podstatné; pouze zařídí trochu obfuskace e-mailové adresy proti snadnému získání hloupými roboty. Jejich kód je v archivu, který je k nalezení na konci článku.

Další makra jsou on a on!. Obě jsou implementována jedním callbackem s nějakými dosazenými argumenty:

${'@at'}->fn('on', fn('_on', array(FALSE, fn::ph(), fn::ph())));

${'@at'}->fn('on!', fn('_on', array(TRUE, fn::ph(), fn::ph())));

function _on($not, $cond, $block)
{
    if ($not === empty($GLOBALS['@' . trim($cond)]))
        return $GLOBALS['@at']->run($block);
}

on je něco jako konstrukt if v PHP, jen mnohem omezenější – zkontroluje, jestli je proměnná „prázdná“ funkcí empty() (jaké hodnoty jsou považované za „prázdné“ si můžete přečíst v dokumentaci) a není-li prázdná, vrátí výsledek bloku. První argument _on() – $not – udává, jestli se má výsledek empty() negovat.

Abychom mohli předat, jestli se má, nebo nemá negovat, je pro vytvoření callbacku na _on() použito fn(). Šlo by to udělat též pomocí názvu makra, pod kterým byl callback vyvolán (o tomto způsobu se můžete dočíst v odkazované sérii článků o at).

Poslední makro je each, které pro každý příspěvek spustí daný blok:

${'@all'} = array_reverse(
    array_keys(array_shift(${'@table'}->get('=', '*'))
);

${'@limit'} = 10;

${'@page'} = 1;
if (!empty($_SERVER['QUERY_STRING']))
    ${'@page'} = intval($_SERVER['QUERY_STRING']);

${'@at'}->fn('each', fn('_each', array(fn::ph())));

function _each($block)
{
    $ret = '';

    foreach (array_slice($GLOBALS['@all'], ($GLOBALS['@page'] - 1) * 
        $GLOBALS['@limit'], $GLOBALS['@limit']) as $i)
    {
        $values = $GLOBALS['@table']->get($i, '*');
        $values[$i]['date'] = strtotime($i);

        foreach ($values[$i] as $k => $v) $GLOBALS['@' . $k] = $v;

        $ret .= $GLOBALS['@at']->run($block);
    }

    return $ret;
}

Aby bylo vůbec možné nějaké příspěvky vypisovat, musíme je nejprve načíst. O to se starají první tři řádky. Získáme klíče (názvy řádků) všech příspěvků array_keys(array_shift(${'@table'}->get('=', '*')). Jelikož jsou data seřazena vzestupně (taková už metatable prostě je), obrátíme je. Aby bylo možné příspěvky stránkovat, nastavíme omezení v proměnné ${'@limit'}. Proměnná ${'@page'} obsahuje momentální stránku získanou z query stringu.

Makro each jako každé jiné zaregistrujeme. Opět je použita curry funkce fn(), i když by tu opět nemusela být. Kód _each() je jasný – vybereme potřebné klíče k příspěvkům, pro každý klíč získáme příspěvek podle klíče, nastavíme proměnné příspěvku a necháme instanci at, aby proběhla blok a rozvinula v něm makra.

on nám dalo možnost do kódu vnést podmínky, each zase iterovat nad sadou dat (v tomto případě předem danou, ale nemusí to tak být vždy). Podobně se v at dají implementovat další jazykové konstrukty.

Stránkování

Na stránkování je tu opět (jak jinak) makro:

@pages {
    @{<a href="?@{i}">@{i}</a> }
    @{<strong>@{i}</strong> }
}

pages má v bloku „podbloky“ (vlastně se jedná o makra; akorát že nejsou zavolána přímo, ale jsou z bloku vyfiltrována a at::run() je spouštěno až na jejich blocích), kde první z nich je vykonán, jedná-li se o nějakou neurčenou stránku, a druhý, jde-li o momentální stránku. Kód makra v PHP vypadá následovně:

${'@at'}->fn('pages', fn('_pages', array(fn::ph())));

function _pages($block)
{
    $ret = '';
    list($any, $current) = array_merge(array_filter($block, 'is_array'));

    for ($i = 1, $stop = ceil(count($GLOBALS['@all']) / 
        $GLOBALS['@limit']); $i <= $stop; ++$i)
    {
        $GLOBALS['@i'] = $i;
        if ($i === $GLOBALS['@page']) {
            $ret .= $GLOBALS['@at']->run($current[1]);
        } else {
            $ret .= $GLOBALS['@at']->run($any[1]);
        }
    }

    return $ret;
}

Je to už trochu magie (moudří si prohlédnou výstup var_dump(at::parse('@pages { ... }')); a hned uvidí, proč to tak je), vězte tedy, že list($any, $current) = array_merge(array_filter($block, 'is_array')) vybere dva podbloky a přiřadí je do proměnných. Poté poiterujeme nad jednotlivými stránkami a podle toho, o jakou stránku se jedná, vykonáme potřebný blok. V blocích je nastavena proměnná ${'@i'} na číslo stránky, na které se uživatel nachází.

Přidávací formulář

Programátor je člověk líný a měl by tedy využívat co nejvíce již napsaného kódu (stojí-li ten kód za to). A proto pro práci s formuláři zneužijeme část framworku Nette , a to konkrétně Nette\Forms:

// vytvoříme
${'@form'} = new Form;

// přidáme potřebné prvky a jejich validační pravidla
${'@form'}->addText('name', 'Jméno:')
    ->addRule(Form::FILLED, 'Anonymy tu nechceme.');

${'@form'}->addtext('email', 'E-mail:')
    ->setEmptyValue('@')
    ->addCondition(Form::FILLED)
        ->addRule(Form::EMAIL, 'Podivný e-mail.');

${'@form'}->addText('www', 'WWW:')
    ->setEmptyValue('http://');

${'@form'}->addTextarea('text', 'Text:')
    ->addRule(Form::FILLED, 'Žádný text?');
${'@form'}['text']->getControlPrototype()->rows(2);

// a zpracujeme
${'@form'}->addSubmit('ok', 'Přidat')
    ->onClick[] = '_add';

function _add()
{
    $now = date('Y-m-d H:i:s');

    foreach ($GLOBALS['@form']->getValues() as $k => $v)
        $GLOBALS['@table']->set($now, $k, $v);

    $GLOBALS['@table']->set('=', $now, TRUE);

    $GLOBALS['@table']->close();

    header('HTTP/1.1 303 See Other');
    $request = new HttpRequest;
    header('Location: ' . $request->getOriginalUri()->getAbsoluteUri());
    exit();
}

${'@form'}->isSubmitted();

O Nette\Forms se můžete dočíst více v odkazované dokumentaci. Zaměřme se na zpracování – po odeslání formuláře bude zavolána funkce _add(). Ta vytvoří klíč pro příspěvek z nynějšího času (času přidání) a zapíše jednotlivé hodnoty získané z formuláře do tabulky. Pak přidá ještě záznam do řádku s klíči. Nakonec uloží tabulku a přesměruje – aby uživatel obnovením stránky neodeslal příspěvek znovu.

Zobrazení formuláře na stránce je velmi velmi prosté:

@{form}

Na závěr

Celý zdrojový kód:

@{form}

@each {
    @{text | texy}
    <p class="right">
        @{date | date | escape} —
        @on  www   {<a href="@{www | escape}">@{name | escape}</a>}
        @on! www   {@{name | escape}}
        @on  email {&lt;<a href="@{email | mailto}">@{email | mailize}</a>&gt;}
    </p>
}

@pages {
    @{<a href="?@{i}">@{i}</a> }
    @{<strong>@{i}</strong> }
}
Asi mě nařknete, že tohle přeci není celý zdrojový kód. Ale kdyby existoval framework postavený na at (jsou jich stovky postavených na XML, tak proč by nemohl být nějaký na at!), takhle by výsledný kód vypadat mohl (dobře, ještě je potřeba do toho připočíst ten formulář).

Kompletní (a teď to myslím vážně) zdrojové kódy této knihy návštěv jsou dostupné v GitHubím repozitáři. Pro naklonování:

$ git clone git://github.com/jakubkulhan/guestbook.git

Nemáte-li Git či ho nechcete, pak je tu dostupný archiv s posledním commitem – buďto ve formátu TAR.GZ, nebo ZIP.

Demo je dostupné na http://bukaj.netuje.cz/play/guestbook/.

×Odeslání článku na tvůj Kindle

Zadej svůj Kindle e-mail a my ti pošleme článek na tvůj Kindle.
Musíš mít povolený příjem obsahu do svého Kindle z naší e-mailové adresy kindle@programujte.com.

E-mailová adresa (např. novak@kindle.com):

TIP: Pokud chceš dostávat naše články každé ráno do svého Kindle, koukni do sekce Články do Kindle.

Hlasování bylo ukončeno    
0 hlasů
Google
(fotka) Jakub KulhanAutor momentálně studuje na osmiletém gymnáziu v Kralupech nad Vltavou. Programování se věnuje od 11 let, kdy ho poprvé uchvátila možnost "mít vlastní stránky". Nakrátko poté objevil PHP a už se to s ním "vezlo". Webové aplikace zůstaly jeho hlavní doménou, ale ve svém volném čase probádává nejrůznější zákoutí světa programování, programovacích jazyků a všeho kolem nich.
Web    

Nové články

Obrázek ke článku Delphi 10.1.2 (Berlin Update 2) – na co se můžeme těšit

Delphi 10.1.2 (Berlin Update 2) – na co se můžeme těšit

Touto roční dobou, kdy je zem pokrytá barevným listím a prsty křehnou v mrazivých ránech, se obvykle těšíme na zbrusu novou verzi RAD Studia. Letos si však ale budeme muset počkat na Godzillu a Linux až do jara. Vezměme tedy za vděk alespoň updatem 2 a jelikož dle vyjádření pánů z Embarcadero se budou nové věci objevovat průběžně, pojďme se na to tedy podívat.

Reklama
Reklama
Obrázek ke článku Konference: Moderní datová centra pro byznys dneška se koná už 24. 11.

Konference: Moderní datová centra pro byznys dneška se koná už 24. 11.

Stále rostoucí zájem o cloudové služby i maximální důraz na pružnost, spolehlivost a bezpečnost IT vedou k výrazným inovacím v datových centrech. V infrastruktuře datových center hraje stále významnější roli software a stále častěji se lze setkat s hybridními přístupy k jejich budování i provozu.

Obrázek ke článku Konference: Mobilní technologie mají velký potenciál pro byznys

Konference: Mobilní technologie mají velký potenciál pro byznys

Firmy by se podle analytiků společnosti Gartner měly  rychle přizpůsobit skutečnosti, že mobilní technologie už zdaleka nejsou horkou novinkou, ale standardní součástí byznysu. I přesto - nebo možná právě proto - tu nabízejí velký potenciál. Kde tedy jsou ty největší příležitosti? I tomu se bude věnovat již čtvrtý ročník úspěšné konference Mobilní řešení pro business.

Obrázek ke článku Hackerský kongres přiveze v září do Prahy špičky světové kryptoanarchie

Hackerský kongres přiveze v září do Prahy špičky světové kryptoanarchie

Hackerský kongres HCPP16 pořádá od 30. září do 2. října nezisková organizace Paralelní Polis již potřetí, a to ve stejnojmenném bitcoinovém prostoru v pražských Holešovicích. Letos přiveze na třídenní konferenci přes 40 většinou zahraničních speakerů – lídrů z oblastí technologií, decentralizované ekonomiky, politických umění a aktivismu. Náměty jejich přednášek budou také hacking, kryptoměny, věda, svoboda nebo kryptoanarchie.

loadingtransparent (function() { var po = document.createElement('script'); po.type = 'text/javascript'; po.async = true; po.src = 'https://apis.google.com/js/plusone.js'; var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(po, s); })();
Hostujeme u Českého hostingu       ISSN 1801-1586       ⇡ Nahoru Webtea.cz logo © 20032016 Programujte.com
Zasadilo a pěstuje Webtea.cz, šéfredaktor Lukáš Churý