jakubův notes – programování a vejšplechty

Píšu o:

PHP curry

U funkcionálních jazyků je velice častý tzv. „currying“, neboli možnost vytvořit novou funkci dosazením nějakých výchozích argumentů do již existující funkce. Jak na to v PHP?

Příklad z Haskellu:

add a b = a + b
add3 = add 3

[ pokračovat ve čtení… ]

vydáno 20. 7. 2009, 10:00:00

žádný komentář

Zařazeno mezi:

at – rozšiřujeme syntaxi

Posledním příkladem praktického využití at je rozšíření syntaxe jazyka PHP. Nemohu si pomoci, ale velice se mi líbí sinatra.rb. Jen se podívejte na tu jednoduchost a eleganci (která je převážně zajištěna jazykem Ruby), s jakou lze psát aplikace. S čistým PHP tohoto nikdy nedocílíte. at může pomoci – rozšíříme si jím syntax do nějaké takovéhle podoby:

@get / {
    // ... show something ...
    return "hello, world!\n";
}

@post / {
    // ... create something ...
    return "ok, created\n";
}

@put / {
    // ... update something ...
    return "ok, updated\n";
}

@delete / {
    // ... annihilate something ...
    return "ok, it's gone\n";
}

Hezké, není-liž pravda? Teď zase jak na to. Prolog:

<?php
require 'at.php';

// zpracování HTTP požadavků
$methods = array(
    'GET'       => array(),
    'POST'      => array(),
    'PUT'       => array(),
    'DELETE'    => array()
);

1:N

Samozřejmě budeme potřebovat at. Pak si připravíme pole, ve kterém budou callbacky na zpracování jednotlivých HTTP metod. Ty musíme nějak naplnit. Budou k tomu sloužit makra @get ... { ... } apod. Teď k nim ještě nějaký hezký callback. Jelikož má makro vždy název typu HTTP požadavku, můžeme využít toho, že volané funkce dostávají jako poslední argument název makra. Proto si připravíme pouze jednu funkci na přidávání:

$add = function ($regexp, $block, $method /* a.k.a. $name */) use (&$methods) {
    $code = '';
    foreach ($block as $_) $code .= $_;
    $methods[strtoupper($method)]['%' . $regexp . '%'] = create_function('', $code);
};

A naň vlastně uděláme „alias“ několika maker:

$_ = at()
    ->fn('get',     $add)
    ->fn('post',    $add)
    ->fn('put',     $add)
    ->fn('delete',  $add);

Když se vrátíme zpět ke callbacku, je snad jasné, co dělá. První argument se namapuje na první parametr makra (používáme výchozí fnname callback, takže bacha na čárky!), dále dostaneme blok a název makra, což je vlastně název HTTP metody. create_function() vytvoří funkci, která se při zpracovávání HTTP požadavku bude volat. Celé by to šlo také udělat přes notfound, podobně jako konfigurace minule, ale chtěl jsem ukázat využití posledního argumentu callbacku.

Pak si naplníme pole s callbacky spuštěním at:

$_->run(substr(file_get_contents(__FILE__), __COMPILER_HALT_OFFSET__));

Tentokráte to však není všechno, protože požadavek ještě chceme nějak zpracovat:

foreach ($methods[$_SERVER['REQUEST_METHOD']] as $regexp => $code) {
    if (preg_match($regexp, $_SERVER['REQUEST_URI'])) {
        echo call_user_func($code);
        return ;
    }
}

Velice jednoduché. Vyzkoušíme, jestli regulární výraz odpovídá požadavku. A pokud ano, tak callback spustíme.

Rozšířená syntaxe

Nakonec ještě zbytek ze souboru (zase je tu totiž ten __halt_compiler()):

__halt_compiler();

@get / {
    // ... show something ...
    return "hello, world!\n";
}

@post / {
    // ... create something ...
    return "ok, created\n";
}

@put / {
    // ... update something ...
    return "ok, updated\n";
}

@delete / {
    // ... annihilate something ...
    return "ok, it's gone\n";
}

Pokud váš editor nezná __halt_compiler() a nezastaví na něm zvýrazňování syntaxe, máte zvýrazněnou syntaxi v makrech grátis.

Závěr

Není to sinatra.rb, ještě tomu hodně chybí. Ale jak je vidět, rozšířit PHP syntaxi jde :-)

Tímto posledním příkladem končí představování at. Všechny příklady naleznete v repozitáři. Je to open-source, takže jakékoli připomínky, hlášení chyb, používání a jiné věci jsou vítány. Když už nic, tak doufám, že se vám alespoň líbí __halt_compiler() a/nebo closures. Snad se PHP 5.3 stabilizuje brzy a rozšíří se na hostinzích, aby se daly closures využívat. __halt_compiler() je ale přítomný ve všech verzích PHP 5, takže se dá krásně používat již dnes.

vydáno 19. 7. 2009, 10:00:00

žádný komentář

Zařazeno mezi:

Patří do skupiny:

Představení at

at – konfigurujeme

Pokračování představování at, tentokrát praktická ukázka toho, jak se dá takový metajazyk použít ke konfiguraci. Jak může vypadat konfigurace pomocí at?

Konfigurace

@localhost {
    @debug          { true      }
    @loglevel       { 50        }
    @db {
        @host       { localhost }
        @username   { jakub     }
        @password   { xxx       }
        @database   { test      }
    }
}

Nám jde o to z toho získat nějaké hezké pole. Opět na začátek takový menší prolog:

<?php
require 'at.php';

// zde bude nakonec načtená konfigurace
$configuration = array();

// vytvoří se nová instance
$at = at();

A teď už již k tomu zábavnějšímu. Jako první si trochu upravíme parser názvu makra a parametrů:

$at->fnname(function ($tokens) {
    return array(trim($tokens[0]['content']), array());
});

fnname()

Metoda fnname() je getter/setter callbacku na naformátování názvu makra a jeho parametrů. Callback je opět realizován pomocí closure. Teď již k obsahu. Jako jediný argument dostane callback pole tokenů. Každý token je pole, které má na indexu type typ tokenu (který nabývá hodnot konstant: at::TEXT – textový token –, at::AT – znak @ –, at::LBRACE – znak { – a at::RBRACE – znak }), přičemž v názvu makra, resp. v textu mezi zavináčem a levou složenou závorkou, mohou být pouze textové tokeny a další zavináče.

Na indexu content každého tokenu se skrývá jeho textový obsah vyparsovaný ze vstupu. Jelikož naše konfigurace nebude nijak chytrá, vezmou se jednoduše všechny znaky mezi zavináčem a levou složenou závorkou (tj. všechny textové a zavinářové tokeny) a jejich obsah se spojí do jednoho názvu. Při pohledu na ukázku, jak bude konfigurace vypadat, je jasné, že callback dostane vždy pole s jedním textovým tokenem. Ale kdyby bylo např. makro @foo @bar @baz { ... }, dostaneme pětiprvkové pole.

fnname callback musí vracet dvouprvkové pole, kdy na prvním indexu je výsledný název makra a na druhém pole argumentů.

notfound()

Jelikož u konfigurace nemusíme vědět, jaké všechny názvy maker, resp. názvy konfiguračních hodnot, se tam mohou objevit, je nejjednodušší si to obstarat pomocí notfound callbacku:

$at->notfound(function ($block, $name) use ($at, &$configuration) {
    if (count($block) === 1) {
        $value = trim($block[0]);
        if (strcasecmp($value, 'true') === 0) $value = TRUE;
        else if (strcasecmp($value, 'false') === 0) $value = FALSE;
        else if (((string) $_ = intval($value)) === $value) $value = $_;
        $configuration[$name] = $value;
        return ;
    }

    $up = $configuration;
    $configuration = array();

    $at->run($block);

    $up[$name] = $configuration;
    $configuration = $up;
});

notfound callback je volán v případě, pokud není název makra registrován pomocí fn(). Argumenty dostává stejné, které by dostala funkce, kdyby byla nalezena. My jsme si pomocí předefinování fnname zajistili, že seznam parametrů makra bude vždy prázdný, takže jako první argument bude notfound callback přijímat blok a jako druhý (zároveň poslední) jméno makra. Pokud tápete, co tam dělá to use ( ... ), pak vězte, že takhle se PHP říká, že tahle closure bude používat dané proměnné z nadřazeného kontextu. My potřebujeme mít k dispozici instanci at a odkaz na konfiguraci.

Nejdříve ve funkci zkusíme, jestli se jedná o skalární hodnotu, nebo o seznam hodnot. Pokud o hodnotu, tak ji prostě přidáme do současné konfigurace. Je tam ještě převod na nativní booleovskou hodnotu apod., ale to není zas tak důležité. Podstatné je, že argumentem bloku je předáván podstrom AST bloku daného makra. Tento strom je neinterpretovaný (říká se tomu lazy evaluation) a je to docela důležité, protože to umožňuje implementovat různé věci. Třebas tady zanořování.

Pokud se tedy jedná o seznam hodnot, zanoříme konfiguraci, projedeme předaný blok (tady je využito, že run() přijímá i naparsovaný strom), čímž se hodnoty přidávají do zanořené konfigurace atd. atp. Prostě rekurze :-)

Poslední drobek

Nakonec už jen pustíme at na konfiguraci (zase je využito __halt_compiler()):

// a jede se
$at->run(substr(file_get_contents(__FILE__), __COMPILER_HALT_OFFSET__));

// kouknout na configuraci
//var_dump($configuration);

__halt_compiler();

@localhost {
    @debug          { true      }
    @loglevel       { 50        }
    @db {
        @host       { localhost }
        @username   { jakub     }
        @password   { xxx       }
        @database   { test      }
    }
}

Když si jednotlivé drobečky spojíte dohromady, máte ukázku dalších možností at a praktický příklad, jak se dá vyřešit např. konfigurace. Příště o tom, jak může at rozšiřovat syntaxi jazyka.

vydáno 18. 7. 2009, 10:00:00

žádný komentář

Zařazeno mezi:

Patří do skupiny:

Představení at

Jak jsem skončil s Nette

Když jsem se rozhodoval, na čem postavit Shopaholic, moc jsem neváhal a říkal si, že konečně na něčem vyzkouším to slavné Nette. Nabuzen články ze seriálu Začínáme s Nette Framework jsem se odvážně vrhl na věc. První fáze byla taková, že jsem postupně vytvářel model databáze a v mezičase četl různé věci z dokumentace. Když byl model, vrhl jsem se již k samotnému programování „těch příjemnějších věcí“.

Nejdříve byla cesta dosti strastiplná – člověku, než se v tom všem zorientuje, to chvilku trvá. Dokumentace tomu moc nepomohla, protože přeci jen se mi zdá o hodně chudší než např. u Zendu. Ke konci práce na frontendu jsem už měl povědomí slušné a v administraci to jen sypalo. Nakonec jsem dodělal celý e-shop a říkal si: Paráda, mám to.

Ale v ten moment začalo to nejhorší, deployment z localhostu na produkční server. Nahraju a bílá obrazovka. Nette\Debug v produkčním módu tohle dělá, tak dám logování do souboru a odesílání chyb na e-mail. Načtu, opět bílá stránka, v errorlogu nic a schránka taky prázdná. Pohled na HTTP hlavičky odpovědi je zajímavý: HTTP/0.9 200 OK, to je všechno. Jo, tak tohle nebude dělat Nette\Debug.

Hrál jsem si s tím. A hrál a hrál, začal sahat na Application, Presenter a další a vysledoval, že provádění skončí někde v metodách formatTemplateFiles() a formatLayoutTemplateFiles(). Zkoušel jsem porůznu upravovat atp. Divné bylo, že asi tak jeden z 50 požadavků se náhodně povedl. Nakonec jsem boj vzdal.

Řekl jsem si, že by to mohlo být použitím poslední verze zdrojových kódů z repozitáře, a proto jsem provedl downgrade na verzi 0.8, která je označena za stabilní. Hurá, už to jakžtakž fungovalo. Projedu, jestli všechno jde. A hle, nejde – jeden presenter hlásí: metoda Environment::getVariables() neexistuje. Začnu bádat, na localhostu všechno jde, podívám se tedy do API dokumentace. A tam metoda Environment::getVariables() je. Kouknu do zdrojových kódů a tam opravdu není. Zkontroluji tedy na SVNko a hle, v 0.8 tahle metoda chybí.

Některé Nettí nástroje – Nette\Debug, profiler – jsou opravdu hezké, usnadňují vývoj. Ale se zbytkem je problém. Ne v tom, že by se nejednalo o kvalitní kód, či podobně, to jsem nezkoumal (tedy až na pár řádků při této deployovací slavnosti). Ale dokumentace, dokonce i API dokumentace – kámen úrazu. Framework prostě bez kvalitní dokumentace být nemůže. Může to být mistrovský kousek kódu, ale bez stejně dobré dokumentace je to jen brak, který nestojí za to používat. Snad se to trochu zlepší po srpnu.

Další věcí je zpětná kompatibilita. Jen za těch pár dnů, co jsem Shopaholic psal, se stihlo změnit hned několik věcí: vylepšená továrnička a úprava životního cyklu presenteru. Opravdu hrozný pocit, když se vám něco pod rukama mění. Může to být dobrý ukazatel stavu projektu – je krásně živý, vyvíjí se. Ale čeho je moc, toho je přespříliš. Takovýhle vývoj už neprospívá věci; je cestou do záhuby. Kult osobnosti Davida Grudla mě nezasáhl, ale byla by škoda špatným marketingem a příliš překotným vývojem poslat do kytek takový podle mě docela slibný český framework.

Možná, že se spolu s Nette ještě někdy potkáme, ale v nejbližší době to asi nebude.

vydáno 17. 7. 2009, 20:48:25

2 komentáře (2 nové)

Zařazeno mezi:

at – text-processing metalanguage

Jak nadpis praví, at je metajazyk na zpracování textu. Co to vlastně znamená? at přijme na vstupu nějaký text, v něm najde volání maker, makra se rozvinou a na výstupu je vrácen text s rozvinutými makry. Jednoduché. A teď praktické využití.

Nejdříve je potřeba získat zdrojový kód. at je knihovna (dobře, jedna třída, ale já jsem prostě minimalista) pro PHP a má repozitář na githubu. Buď si můžete naklonovat Gití repozitář:

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

Nebo si stáhnout zabalené zdrojáky – buď jako tarball, či v ZIPu.

Šablonovací systém

Docela intuitivní způsob využití, když se jedná o knihovnu na zpracování textu. Příklad mluví za vše:

<?php
require 'at.php'; // potřebujeme knihovničku

// nadefinujeme si nějaké proměnné
${'@foo'} = "hello, world!";
${'@bar'} = "that's at :-)";

// a už jedem
echo at()
    ->fn('', function ($block) {
        $var = trim($block[0]);
        if (!isset($GLOBALS['@' . $var])) return  '';
        return (string) $GLOBALS['@' . $var];
    })
    ->run(substr(file_get_contents(__FILE__), __COMPILER_HALT_OFFSET__));

// šablona
__halt_compiler();

@{foo}
@{bar}

Postupně. Nejdříve se vloží knihovna s at, to je jasné. Poté si nadefinujeme nějaké proměnné (v příkladě jsou to globální proměnné (i když s prefixem, který zabraňuje, abychom si je jen tak s něčím spletli), ale můžeme si klidně celé šablonování zapouzdřit do třídy). A teď už přichází na řadu at. Funkce at() je zkratka pro new at(), abychom nemuseli psát:

$_ = new at;
echo $_->...

fn() a definování callbacků maker

Metoda fn() je setter/getter maker. V tomto příkladě konkrétně ten setter, který spojuje název makra (první argument) s callbackem (druhý). Tedy je využito closures z PHP 5.3 (pro nižší verze můžete zkusit použit create_function()). Funkce se podívá, jestli existuje nějaká globální proměnná s požadovaným názvem, a když ano, tak její obsah vrátí.

run() aneb zpracování

run() má jeden argument, který může být:

  1. řetězcem; v tom případě je argument naparsován (pomocí at::parse()) a vytvořen z něj AST

  2. polem; a tudíž je považován za již naparsovaný AST

V příkladu je využito __halt_compiler(), kterýžto jazykový konstrukt zastaví zpracovávání souboru, takže můžeme mít kód i šablonu v jednom souboru, a run() je předána právě ta šablona, která se nachází za __halt_compiler().

Makra

Je vidět, že šablona je docela jednoduchá, a tak lze lépe vysvětlit, co je vlastně co. Je patrné, že ona makra budou @{foo} a @{bar}. Takové volání makra se skládá z několika částí. Každé volání uvozuje znak @ (česky zavináč, anglicky at (odtud pochází i název knihovny)). Po něm následuje název makra se seznamem parametrů – řetězec mezi zavináčem a levou složenou závorkou ({). U těchto maker je prázdný, a proto i při volání metody fn() byl jako název makra předáván prázdný řetězec. Nakonec je tu obsah složených závorek ({ ... }), který je v terminologii at nazýván blok a v tomto příkladě je obsahem bloku název proměnné, kterou chceme vypsat.

Když se vrátíme ke spojování prázdnojmenného makra s funkcí (fn('', ...)) a podíváme na dotyčnou funkci, je vidět, že blok přebírá jako první argument. To nemusí být vždycky pravda. Jde o to, že tyto makra nemají žádné parametry. Obecně je totiž callback volán s těmito argumenty: param0, param1, ..., paramN, block, name. block je AST bloku a name název, s jakým bylo makro zavoláno (můžete tedy přiřadit jeden callback pod víc názvů maker a podle názvu se rozhodnout, co se má udělat).

Odvozování názvu makra

O rozhodování, co je název makra a co jednotlivé parametry, se stará callback nastavovaný metodou fnname(), jeho defaultní implementace je at::_fnname_(). Ta jako název makra bere všechny znaky až do prvního bílého (mezery, tabulátoru atp.) a argumenty jsou oddělovány čárkou – tzn. že makro @foo bar, baz { ... } by bylo nahrazeno voláním callbacku, který je zaregistrován pod názvem foo s argumenty: "bar", "baz", array( ... ), "foo".

To be continued…

Tohle byla jen taková lehká ukázka, jak využít at pro šablonovací systém. Ještě by bylo samozřejmě potřeba doplnit možnosti větvení a iterování. Ale to již můžete udělat sami. A nejlépe se o to poté podělte.

Další způsoby praktického využití a větší ponoření se do magie at přijde v následujícím článku.

vydáno 17. 7. 2009, 10:00:00

žádný komentář

Zařazeno mezi:

Patří do skupiny:

Představení at