phpFashion

Rubrika PHP

Špeky a párky v nahrazování řetězců

Jak se nenechat vypéct při nahrazování výskytů jednoho řetězce jiným. Search & Replace tricks.

Základní funkcí pro nahrazování řetězců v PHP je str_replace:

$s = "Lorem ipsum";
echo str_replace('ore', 'ide', $s); // vrací "Lidem ipsum"

Díky chytře vymyšlenému kódování UTF-8 ji lze spolehlivě používat i pro takto kódované řetězce. Navíc jako první dva argumenty lze uvést pole a funkce potom provede vícenásobné nahrazení. A tady narážíme na první špíček, který je třeba mít na zřeteli. Každé nahrazení prochází řetězec znovu, pokud bychom tedy chtěli ve větě pánské dárky prohodit <⇒ a získat tak dánské párky (švédská delikatesa!), žádné pořadí argumentů k cíli nepovede:

// vrací "dánské dárky"
echo str_replace(array('dá', 'pá'), array('pá', 'dá'), "pánské dárky");

// vrací "pánské párky"
echo str_replace(array('pá', 'dá'), array('dá', 'pá'), "pánské dárky");

Hledanou funkcí, která řetězec projde jen jednou a zamezí vzniku kolizí, je strtr:

// vrací "dánské párky", hurá
echo strtr("pánské dárky", array('pá' => 'dá', 'dá' => 'pá'));

Pokud bychom hledali výskyty podle složitějších pravidel, využijeme regulární výrazy a funkci preg_replace. Ta také umožňuje vícenásobné nahrazování a chová se stejně jako str_replace. Teď však mířím jinam. Potřebuji v řetězci nahradit všechny čísla slovem hafo, což je snadné:

$s = "Radek tvrdí, že má IQ 151. Pěkný sběratelský kousek!";
echo preg_replace('#\d+#', 'hafo', $s);

Zobecněme kód, nechť umí čísla nahradit čímkoliv, co mu předáme v proměnné $replacement. Řada programátorů použije:

return preg_replace('#\d+#', $replacement, $s); // spatne!

Což bohužel není dobře. Je třeba si uvědomit, že i v nahrazovaném řetězci mají určité znaky speciální význam (konkrétně lomítko a dolar), proto ho musíme escapovat. Správné obecné řešení je:

return preg_replace('#\d+#', addcslashes($replacement, '$\\'), $s); // ok

Napadají vás ještě nějaké nahrazovací špeky?


Best practices pro jmenné prostory v PHP

Pár dobře míněných tipů, jak navrhnout strukturu jmenných prostorů (namespaces) a názvů tříd.

Jmenné prostory jsou asi nejznámější novinkou PHP verze 5.3. Jejich hlavním smyslem je zabránit jmenným konfliktům a umožnit zkracovat (aliasovat) názvy tříd pro používání v jednom souboru. V praxi se ukazuje, že konfliktům lze předejít i použitím 1–2 písmenného prefixu, stejně tak jsem se nikdy nedopustil názvů tříd jako Zend_Service_DeveloperGarden_Response_ConferenceCall_AddConferenceTemplateParticipantResponseType (97 znaků, zajímalo by mě, jak s tím dodržují své pravidlo o max. délce řádků 80 znaků 🙂 ). Avšak PHP kráčí ve šlépějích Javy a tak tu máme namespaces. Jak s nimi naložit?

Přínos jmenných prostorů

Asi nejsložitější otázka, na kterou si musíte odpovědět, zní: čemu prospěje přejmenovat třídu:

  • sfFormSymfony\Component\Form\Form

Otázka je osvědčeným závdavkem k nekonečným flame wars. Z hlediska pohodlí programátora, intuitivnosti a zapamatovatelnosti je vhodnější původní stručné a výstižné sfForm. Odpovídá totiž tomu, jak třídu programátoři označí hovorově, tj. „formulář v Symfony“. Nový a delší název je zase správný z jiných hledisek, u nichž si ovšem nejsem jist, jestli je běžný uživatel ocení.

Jak jmenné prostory rozvrhnout?

Syntaktickou stránku použití popisuje dokumentace, objevit správné vzory však chce praxi, na kterou zatím nebylo dost času. Prostory v PHP mají svá specifika daná celou řadu faktorů, proto není ideální 100% kopírovat konvence používané třeba v Javě nebo .NET. Dá se z nich však dobře vycházet.

Více si řekneme u jednotlivých pravidel pro pojmenování.

1) třída má mít výstižný název i bez uvedení NS

Název každé třídy i bez jmenného prostoru musí vystihovat její podstatu. Nevhodné by bylo přejmenovat třídu ArrayIteratorSpl\Iterators\Array, protože pod názvem Array by člověk neočekával iterátor (pomiňme, že třídu nelze nazvat klíčovým slovem). A pozor, ani z názvu Spl\Iterators\Array nemusí být zřejmé, že jde o iterátor, protože nelze sázet na to, že namespace Spl\Iterators obsahuje pouze iterátory. Mohou tam být třeba i nějaké pomocné třídy.

Pár příkladů:

  • nevhodné: Nette\Application\Responses\Download – nelze dovodit, že Download je odpověď
  • nevhodné: Zend\Validator\Date – předpokládali byste, že Date je validátor, nikoliv datum?
  • nevhodné: Zend\Controller\Request\Http – předpokládali byste, že Http je požadavek?

Proto krom specializace tříd je vhodné v názvu ponechat i obecnost:

  • lepší: Nette\Application\Responses\DownloadResponse
  • lepší: Zend\Validator\DateValidator
  • lepší: Zend\Controller\Request\HttpRequest

Ideální je, pokud existuje jednoslovný a přitom výstižný název. Ten lze vymyslet hlavně u tříd, které reprezentují něco z reálného světa:

  • nejlepší: Nette\Forms\Controls\Button – dvouslovné ButtonControl netřeba (avšak HiddenControl na Hidden zkrátit nelze)

2) jmenný prostor má mít výstižný název

Pochopitelně i samotný název jmenného prostoru musí být výstižný a výhodou je název kratší, bez zbytečností. Takovou zbytečností se mi zdá např. Component v Symfony\Component\Routing, protože bez něj by název nijak neutrpěl.

V některých situacích je třeba se rozhodnout mezi jednotným a množným číslem (Zend\Validator vs Zend\Validators), což je podobně nerozhodný problém, jako při volbě jednotných a množných čísel u databázových tabulek.

3) rozlište jmenné prostory a třídy

Pojmenovat třídu stejně jako jmenný prostor (tj. mít třídy Nette\Application a Nette\Application\Request) je technicky možné, mohlo by to však programátory mást a lépe se bude tomu vyhnout. Myslete i na to, jak dobře se bude výsledný kód číst nebo jak budete API někomu vysvětlovat.

4) omezte zbytečné duplicity (+ parciální namespace)

Ideální je, pokud název třídy a název prostoru neobsahují duplicitně stejnou informaci.

  • místo Nette\Http\HttpRequest raději Nette\Http\Request
  • místo Symfony\Component\Security\Authentication\AuthenticationTrustResolver raději třídu TrustResolver

Třída Nette\Http\Request neodporuje pravidlu č. 1 o výstižném názvu třídy i bez uvedení jmenného prostoru, naopak nám dovoluje elegantně využít parciální namespace:

use Nette\Http; // alias pro namespace

// dostupné jsou všechny třídy via Http:
$request = new Http\Request;
$response = new Http\Response;
// a navíc Http\Response je srozumitelnější než samotné Response

Pokud jmenné prostory chápeme jako balíčky, což je obvyklé, vede to k nešťastnému duplikování posledního slova:

  • Zend\Form\Form
  • Symfony\Component\Finder\Finder
  • Nette\Application\Application

Jmenné prostory také doslova svádí sdružovat třídy (např. různé implementace téhož rozhraní apod.) do vlastních prostorů, což opět vytváří duplicity:

  • Nette\Caching\Storages\FileStorage – tj. všechny úložiště v samostatném prostoru Storages
  • Zend\Form\Exception\BadMethodCallException – všechny výjimky do Exception
  • Symfony\Component\Validator\Exception\BadMethodCallException – opět všechny výjimky do Exception

Sdružující namespaces prodlužují název a vytváří v něm duplicity, protože obecnost často není možné z názvu tříd odstranit (1. pravidlo). Jejich výhodou může být lepší orientace ve vygenerované API dokumentaci (téhož by však šlo dosáhnout i jinak) a snazší dostupnost při použití plnohodnotných IDE s napovídáním. Každopádně je doporučuji používat jen s rozvahou. Například pro vyčlenění výjimek se moc nehodí.

5) nezaměnitelnost tříd z více prostorů

Dle bodu 1) má mít třída výstižný název, což ale neznamená, že má být i jednoznačný v rámci celé aplikace. Obvykle stačí, aby byl jednoznačný v rámci jmenného prostoru. Pokud se však v kódu často používají vedle sebe dvě třídy z různých prostorů, nebo pokud mají mezi sebou jinou podstatnou souvislost, neměly by mít stejný název. Jinými slovy, nemělo by být nutné používat AS v klauzuli USE.

6) jednosměrné závislosti

Zvažte, jaké závislosti mají mezi sebou mít třídy z jednotlivých jmenných prostorů. Snažím se dodržovat:

  • pokud má třída ze jmenného prostoru A\B závislost na třídě ze jmenného prostoru A\C, neměla by žádná třída z A\C mít závislost na A\B
  • třídy ze jmenného prostoru A\B by neměly mít závislost na třídě z prostoru A\B\C (tohle berte s rezervou)

p.s.: neberte prosím tento článek jako dogma, jde jen o zachycení aktuálních myšlenek


Recenze knihy 1001 tipů a triků pro PHP

Když jsem se dozvěděl, že Jakub Vrána píše knížku 1001 tipů a triků o PHP, samozřejmě mě napadlo pár trapných fórků („1001 vtipů o PHP“, „to jich bude devět?“ nebo „č. 1: znáte Ruby?“) ale pak jsem si uvědomil, jak je to vlastně šíleně velké číslo. Schválně, zkuste vymyslet a napsat padesátku. Dáte to? Já leda s odřenýma játrama. No a pak další. A další. A další! Uff. Když pak Jakub hlásil, že se blíží do třetiny, vzpomněl jsem si na Marvinovo „Nejhorší bylo těch prvních deset miliónů.“

Humor mi došel ve chvíli, kdy mě Jakub požádal, jestli bych mu udělal korekturu. Co si budeme povídat, když váš kamarád napíše knihu, tak ji od něj dostanete, někam ji důmyslně založíte a až se setkáte, tak mu řeknete, že jste ji samozřejmě četli a jako že to fakt bylo dobrý a poplácáte ho po rameni nebo tam někde. Alespoň tak velí bonton. Ovšem když máte dělat korekturu, ocitáte se vstříc hrozbě, že ji doopravdy budete muset přečíst! Že bych mu ji po týdnu vrátil netknutou a bez jediného škrtu („čéče, sem to fakt čet a žádnou vyloženě jako chybu sem tam nenašel…“), o tom jsem vůbec neuvažoval, by mi to nezbaštil. Pustil jsem se do čtení a zjistil, že to je hodně dobrý materiál.

Byť školy nemám a z odborné literatury jsem přečetl jen Pixyho, Kruga a Koska, cítím se v PHP poměrně zdatný. Co mi může kniha od Jakuba dát? Ejhle, může. Jakub totiž na vydavatele ušil pěknou kulišárnu, ta kniha je o PHP spíš mimochodem. Je to prostě hromada zajímavých tipů a nápadů pro všechny, kdo se zabývají webovými aplikacemi. Navíc originálních tipů, nejde totiž o tištěnou verzi Jakubova blogu. V žádném případě nečekejte referenční příručku PHP nebo nudnou přehlídku funkcí, kdepak. Najdete tu tipy počínaje návodem, jak skrýt emailovou adresu před roboty, přes generování sitemap až po pádné argumenty, zda mít doménu s www. nebo bez.

U toho bych se zastavil. Ač s Jakubem máme na mnohé věci odlišné názory, s ním je radost nesouhlasit. On má totiž svůj postoj vždycky podpořený jasnou argumentací. U něj neexistuje „to se dělá tak, protože to tak dělá každý“ nebo „protože tak to dělá Nielsen|Zeldman|Lerdorf|Torvalds|(doplň si svou kapacitu)“, ale protože nad tím přemýšlel a došel k nějakému závěru. A to se jako červená niť táhne celou knihou. Takže ačkoliv bych mohl s něčím nesouhlasit, stěží bych v ní hledal chybu. Takovou preciznost jsem zažil snad jen u knih Douglase Crockforda.

Ale ať jen nechválím – kniha je sbírkou nápadů, které jsou sice tematicky uspořádané, ale chybí mi tam průvodní slovo, co by z toho dělalo souvislé čtivo. Chybí mi tam Miloš Frýba. A na můj vkus obsahuje příliš málo humoru a nadsázky 🙂

Každopádně knihu vřele doporučuji každému PHPčkaři, své si v ní najdou jak začátečníci, tak matadoři. Za cenu jednoho bifteku není nad čím váhat. A neříkám to proto, že je v ní zmíněné Nette 🙂 V knihkupectvích by měla být od pondělí, nicméně dokud Jakub nabízí možnost nechat si ji za stejnou cenu poslat s jeho podpisem, tak knihkupectví nemají šanci.


Jak se píše generátor API dokumentace?

Dlouhá léta mi ze zdrojáků generoval API dokumentace phpDocumentor. Vývoj tohoto nástroje ustrnul a PHP 5.3 je mu cizí, zejména chybí podpora jmenných prostorů. Začal jsem hledat alternativu. Bohužel žádná sláva.

On ani samotný phpDocumentor nebyl žádný zázrak. Dodáván se sadou šablon, jedna hnusnější než druhá, vedle toho i taková Lupa.cz vypadá jako fešanda. Chápu, „programátoři sobě“, oželel bych nějaké eye-candy, ale šablony jsou tak zoufale nepřehledné a odpudivé, že si neumím představit takovou dokumentaci používat dobrovolně. Užitná hodnota nulová. A nabídka alternativních šablon není.

Vyzkoušel jsem phpDoctor, doxygen a PHP_UML, u kterého jsem nakonec zůstal. Jeho standardně vygenerovaná dokumentace asi nejvíc odpovídala tomu, co jsem hledal (ukázka). Jenže aplikace je taková PEARovská, hned v úvodu člověka přivítá Notice: Undefined variable: errorLevel, musel jsem se hrabat ve zdrojácích a opravit několik bugů, strávil moře času úpravou XSL šablon. Ještě štěstí, že PHP_UML generuje dokumentaci neskutečně rychleji než phpDocumentor, takže jsem viděl výsledek každého zásahu takřka ihned a ne až po dlouhých minutách. Stále tomu ale chyběly dost podstatné věci, a tehdy mě napadlo…

…že si prostě napíšu vlastní generátor. Za čas, co jsem investoval do úprav, jsem ho mohl mít hotový několikrát. Ach ta narušená schopnost používat cizí knihovny.

Pokusím se vysvětlit postup, jak jsem Apigen psal. Dopředu prozradím, že se vešel do pouhých 150 řádků (!) kódu + šablony.

Jak na to

Nejprve: zdrojový kód lze analyzovat buď vlastním parserem, nebo využít reflection. Jedním z hlavních požadavků bylo, že generátor bude umět plnohodnotně pracovat s interními třídami PHP (třeba takto). Což mi jinde citelně chybělo. Jelikož interní třídy žádný zdrojový kód nemají, zvolil jsem reflexi. To znamená, že všechny třídy, pro které chci generovat dokumentaci, musím načíst. Projít adresářovou strukturu třeba Finderem a postupně volat require pro každý soubor nejde – mezi třídami jsou závislosti a je nutné je načítat v pořadí: rozhraní, rodiče, potomci. Nelehký úkol, ale vyřešil jej RobotLoader:

$robot = new Nette\Loaders\RobotLoader;
$robot->addDirectory($dir); // adresář, který chceme dokumentovat
$robot->register(); // zapne autoloading

// getIndexedClasses vrací seznam nalezených tříd a souborů
foreach ($robot->getIndexedClasses() as $file) {
	require_once $file;
}
$robot->unregister();

Když nyní načtu třídu, která má vazbu na dosud nenačtenou třídu nebo rozhraní, přijde ke slovu RobotLoader a situaci vyřeší. Takže to bychom měli. Paráda.

Když operuješ sám sebe

Avšak objevil se tu oříšek. Jak generovat API dokumentaci pro knihovnu, kterou pro generování API dokumentace používám? Slepice a vejce hadr. Řešení vidím dvě: buď použít jako vstup přímo tu kopii Nette, kterou používá generátor, nebo vytvořit alternativní vesmír, kde se Nette nejmenuje Nette. Druhý způsob se mi zdá rozumnější, nakonec ho používám pro generování distribučních balíčků Nette. Jde o to, že generátor používá knihovnu NetteX, která se liší jen v tom, že sídlí v „oiksovaném“ jmenném prostoru.

Ano, anotace

Aby byl model plnohodnotný, potřeboval jsem přihlížet k anotacím. Například vynechat všechny elementy s anotací @internal, určovat vrácené hodnoty metod dle anotace @return atd. Tady jsem opět využil skutečnosti, že Nette Framework podporuje anotace a pro reflektování použil Nette\Reflection.

$class = new Nette\Reflection\ClassReflection($name);
if ($class->hasAnnotation('internal')) {
	...
}

Třídy z Nette\Reflection podporují tzv. properties, tudíž se příjemněji používají v šablonách, kde místo

$tmp = $method->getAnnotations();
foreach ($tmp['params'] as $value) ...

mohu psát stručnější

foreach ($method->annotations['params'] as $value) ...

Nakonec jsem pro reflexi tříd použil vlastního potomka Nette\Reflection\ClassReflection s přidanou funkcionalitou. Přičemž Nette automaticky zajišťuje, aby metody jako getDeclaredClass() opět vracely instanci mé třídy. Svěží 🙂

Ša-la-la-blo-ny

Při generování HTML souborů jsem naplno využil sílu šablonovacího jazyka Latte. Ten lze používat nejen ve spojitosti s MVC aplikacemi, ale naprosto kdekoliv. Využil jsem oddělený layout, dědičnost bloků i tzv. n:attributy. Výsledkem jsou skutečně dobře čitelné šablony. Příklad:

{* $implementers je pole objektů ClassReflection *}
<div n:if="$implementers">
	<h4>Direct Known Implementers</h4>
	{foreach $implementers as $item}
		<a href="{$item|classLink}">{$item->name}</a>{sep}, {/sep}
	{/foreach}
</div>

Uvedený <div> se vykreslí pouze v případě, že pole $implementers je neprázdné. Jednotlivé položky vykreslí jako odkazy oddělené čárkou. A makro {sep}...{/sep} zajistí, aby se čárka (tj. separátor) neobjevila za poslední položkou. Živé to můžete vidět třeba tady.

Jiným příkladem je šablona, která vygeneruje v JavaScriptu seznam všech tříd pro potřeby našeptávače (výsledek):

// $classes je opět pole objektů ClassReflection

{contentType javascript}

var classes = {$classes|values|map:'return $value->name;'};

Pro generování obarvených zdrojových kódů jsem použil skvělou knihovnu FSHL, kde stačilo doplnit seznam klíčových slov PHP 5.3. Obsah doc-bloků jsem zkusil formátovat pomocí Texy ve spojení s FSHL a výsledek se mi zdá dostačující.

Světlo světla spatřil Apigen

Naprogramovat celý generátor trvalo pár hodin, což je řádově méně, než bych strávil úpravou existujících knihoven. Výsledkem totiž bylo, jak jsem zmínil, pouhých 150 řádků kódu. Ač jsem ho nijak rychlostně neoptimalizoval, dokumentaci k Nette vygeneruje za cca 11s, zatímco phpDocumentor se s tím trápí přes dvě minuty. Ani jsem nečekal, že se Nette Framework tak výborně hodí na tak netypický úkol.

Mnohem víc času jsem pak strávil vylaďováním šablon, připojil jsem jQuery a hrál si s tříděním metod a dalšíma opičkama. Generátor jsem opatřil rozhraním pro ovládání z příkazové řádky, přidal další fíčůrky a dopsal komentáře, takže v tuto chvíli je těch řádků určitě alespoň jednou tolik 🙂

Jestli chcete, tak si Apigen můžete stáhnout


Programátoři chyby neignorují

Tedy alespoň by neměli. PHP je jazyk s poměrně laxním přístupem k chybám a tudíž vyžaduje od programátora vyvinout větší úsilí při jejich ošetřování. Nenechte si namluvit opak. Článek je reakcí na dobře míněnou radu Jakuba Vrány.

Existují dva tradiční způsoby, jak chyby oznamovat:

  • vyhozením výjimky
  • návratovou hodnotou

Takřka všechny knihovny dodávané s PHP používají druhý způsob, protože výjimky byly do jazyka zavedeny až v páté verzi. Což je v mnoha případech na škodu. Programátor totiž musí neustále otrocky kontrolovat návratové hodnoty a ošetřovat chybové stavy. A zároveň je tu riziko, že na to zapomene. Přesněji řečeno, ani ne tak „zapomene“, jako spíš se na to „vy…kašle“, protože to ve světě PHP platí za normu. Nakonec, podívejte se třeba do dokumentace fread a spočítejte, kolikrát v příkladech ošetřili návratovou hodnotu jakékoliv funkce.

Při takovém stylu práce pak není divu, že programátoři považují výjimky za něco otravného, co se (cituju Jakuba) „nedá ignorovat“, zatímco „chyby indikované návratovou hodnotou ignorovat lze a program nejspíš nějak pracovat bude.“ Notykrávo.

Co přesně znamená ono nějak pracovat? Třeba:

// přesouváme z disku na disk
copy('c:/oldfile', 'd:/newfile');
unlink('c:/oldfile');
// pokud první operace selže, soubor se nenávratně smaže
$link = new mysqli('localhost', $user, $pass, 'eshop');
...
$link->query('USE testdata'); // přepneme se z ostré do testovací databáze
$link->query('DELETE FROM orders');
// pokud předchozí operace selže, vymažou se objednávky z ostré databáze
// BTW věřili byste, že výchozí nastavení query() při chybě nevyhodí ani noticku?
// smažeme soubor z adresáře 'test'
ftp_chdir($connection, 'test');
ftp_delete($connection, 'database.sdb');
// pokud předchozí operace selže, vymaže se kupříkladu ostrá databáze

A pak jsou případy, kdy nějak pracovat znamená zařvat s neošetřitelnou fatální chybou:

// načtení konfigurace
$defaults = array('host' => 'localhost', 'charset' => 'utf8');
$config = parse_ini_file('config.ini');
$config = $config + $defaults;
// pokud předchozí operace selže a vrátí false, skončí sčítání fatální chybou
function saveConfig(array $config) {   ...  }

$config = parse_ini_file('config.ini');
saveConfig($config);
// pokud předchozí operace selže a vrátí false, skončí opět fatální chybou
mysqli_connect('localhost', $user, $pass)
	->query("SET NAMES 'utf8'");
// pokud první operace selže, skončí opět fatální chybou

Bastlení zdar!


Zrádné regulární výrazy v PHP

V PHP jsou k dispozici tři knihovny pro regulární výrazy: PCRE, Oniguruma a POSIX Regex. Druhá jmenovaná nemusí být vždy k dispozici a třetí je zavržená, proto byste měli používat výhradně šikovnější a rychlejší knihovnu PCRE. Bohužel implementace trpí docela nepříjemnými nedostatky, a to ve všech verzích PHP.

Činnost jednotlivých funkcí preg_* lze rozdělit do dvou kroků:

  1. kompilace regulárního výrazu
  2. exekuce (hledání, záměna, filtrování, …)

Sympatické je, že PHP zkompilovanou podobu regulárních výrazů udržuje v cache a tudíž se kompilují vždy jen jednou. Proto je vhodné používat statické regulární výrazy, tj. negenerovat je parametricky.

Teď k těm nepříjemným záležitostem. Pokud se během kompilace odhalí chyba, PHP na ni upozorní chybou úrovně E_WARNING, avšak návratová hodnota funkce je nejednotná:

  • preg_filter, preg_replace_callback, preg_replace vrací null
  • preg_grep, preg_match_all, preg_match, preg_split vrací false

Dobré je vědět, že funkce vracející skrze referenci pole $matches (tj. preg_match_all a preg_match) při kompilační chybě argument nevynulují, tudíž testovat návratovou hodnotu má opodstatnění.

PHP od verze 5.2.0 disponuje funkcí preg_last_error vracející kód poslední chyby. Avšak pozor, týká se to pouze chyb vzniklých během exekuce! Pokud dojde k chybě během kompilace, hodnota preg_last_error se nevynuluje a vrací předchozí hodnotu. Pokud tedy návratová hodnota preg_* funkce není null resp. false (viz výše), rozhodně nepřihlížejte k tomu, co preg_last_error vrací.

K jakým chybám může dojít během exekuce? Nejčastějším případem je překročení pcre.backtrack_limit nebo nevalidní UTF-8 vstup při použití modifikátoru u. (Poznámka: neplatné UTF-8 v samotném regulárním výrazu se odhalí již při kompilaci.) Nicméně způsob, jak PHP s takovou chybou naloží, je naprosto neadekvátní:

  • nevygeneruje žádnou zprávu (silent error)
  • návratová hodnota funkce může naznačovat, že je vše v pořádku
  • chybu lze zjistit až následným zavoláním preg_last_error

Zastavím se u té návratové hodnoty, což je asi největší zrada. Proces se totiž vykonává do chvíle, než se chyba objeví a poté se vrátí částečně zpracovaný výsledek. A to v naprosté tichosti. Jenže ani tohle neplatí vždy, třeba trojice funkcí preg_filter, preg_replace_callback, preg_replace umí i při exekutivních chybách vracet null.

Zda došlo během exekuce k chybě lze zjistit jedině voláním preg_last_error. Ale jak už víte, tato funkce vrací nesmyslný výsledek v případě, že došlo naopak k chybě kompilace, musíme tedy obě situace rozlišit přihlédnutím k návratové hodnotě funkce, zda-li je null resp. false. A jelikož funkce vracející null při chybě kompilace umí vracet null i při chybě exekuce, lze konstatovat asi jen tolik, že PHP je nadevší pochybnost zkurvený jazyk.

Jak by vypadalo bezpečné použití PCRE funkcí? Například takto:

function safeReplaceCallback($pattern, $callback, $subject)
{
	// callback musíme ověřit sami
	if (!is_callable($callback)) {
		throw new Exception('Neplatny callback.');
	}

	// testujeme výraz nad prázdným řetězcem
	if (preg_match($pattern, '') === false) { // chyba kompilace?
		$error = error_get_last();
		throw new Exception($error['message']);
	}

	// zavoláme PCRE
	$result = preg_replace_callback($pattern, $callback, $subject);

	// chyba exekuce?
	if ($result === null && preg_last_error()) {
		throw new Exception('Chyba zpracovani regularniho vyrazu.', preg_last_error());
	}

	return $result;
}

Uvedený kód transformuje chyby do výjimek, nesnaží se však potlačit výpis varování.

Bezpečné zpracování regulárních výrazů je implementováno ve třídě Nette\Utils\Strings.


Apel na hostéry: chceme PHP 5.3!

PHP

Drazí provozovatelé hostingů, už je to tady. Před bezmála půl rokem vyšlo PHP 5.3.0. Přirozený nástupce řady 5.2 bez zpětně nekompatibilních změn. Víme, že jste s nasazením čekali, než se objeví následující setinková verze. Dnes se tak stalo, PHP 5.3.1 je venku.

Trojková řada přináší velké množství vylepšení, na které my, krásní a bohatí programátoři, toužebně čekáme. Neotálejte proto více a novou verzi PHP nám dopřejte. Když budete chtít, stihnete ji nasadit ještě dnes v noci! Stejně v televizi nedávají nic zajímavého. Děkujeme!

(tipy na hostingy, které už novou verzi mají, můžete posílat do komentářů)


Jak zazálohovat všechny své twíty

Pokud máte dojem, že ty 140 znakové kravinky, co píšete na Twitter, je nutné zálohovat pro příští generace, ať už z důvodu, že Twitter má občas výpadky doprovázené ztrátou dat, nebo vám někdo může účet ukrást a smazat, nebo se blížíte k limitu 3200 štěbetnutí, po kterém se (prý) nejstarší kusy odmazávají, nebo prostě chcete mít vše na disku kvůli lepšímu vyhledávání, je tento článek pro vás.

Protože jsme na blogu o PHP, nebudu zde popisovat online služby určené k zálohování, ale rovnou vypustím z klávesnice kus kvalitního objektového kódu ;)

Nejprve si stáhněte knihovničku Twitter for PHP (verzi 2.0) od stejnojmenného autora s autorem blogu. A pak si vytvořte zálohovač twitter-backup.php:

<?php
set_time_limit(0);

require 'twitter.class.php';

// zde dejte své přihlašovací údaje
$twitter = new Twitter($consumerKey, $consumerSecret, $accessToken, $accessTokenSecret);

// naráz lze načíst maximálně 200 twittů, tož budeme stránkovat
$page = 1;
$retry = 0;
do {
	try {
		$channel = $twitter->load(Twitter::ME, 200, $page);
		if (empty($channel->status)) { // prázdný výstup? narazili jsme na konec
			break;
		}
		file_put_contents("twitter-backup.$page.xml", $channel->asXml());
		echo "Ulozena stranka c. $page\n";
		$page++;
		$retry = 0;
		sleep(1);

	} catch (TwitterException $e) {
		echo "Error: {$e->getMessage()}\n";
		if ($retry > 3) break; // chyby s občas stávají, dáme 3 pokusy
		$retry++;
	}
} while (true);

Po spuštění se vytvoří soubory twitter-backup.1.xml, twitter-backup.2.xml atd., podle toho, jak jste aktivní štěbetal. XML obsahuje skutečně vše, včetně informací, na koho zpráva reaguje, z jakého zařízení byla poslána nebo jaké máte barvičky v profilu.


Vyplatí se jít na školení Jakuba Vrány?

Prapůvodně měl být na tomto místě ohlas na školení Jakuba Vrány Konfigurace a výkonnost MySQL. Chtěl jsem psát o tom, že ačkoliv mě Jakub dopředu varoval, že téma konfigurace MySQL není gór moc kůl, obavy se ukázaly jako liché, neboť prakticky každou vlastnost demonstroval na živých příkladech a tím držel posluchače ve střehu. Jako skvělý prezentační nástroj se přitom ukázal Adminer. Až jsem měl pocit, že si ho vyrobil speciálně k tomu účelu, protože s phpMyAdminem by tak ladně demonstrovat některé rysy MySQL nešlo. No a pak tu mám několik drobností, které bych školení vytkl, nicméně…

…Nicméně hlubší rozbor tohoto konkrétního školení vynechám. Jednak proto, že ho Jakub aktuálně nenabízí. (Přesněji řečeno nabízí řadu jiných školení a máte poslední šanci se na ně přihlásit, první z nich je totiž už zítra). A také proto, že více čtenářů bude zajímat odpověď na úvodní otázku.

Začnu obecně: aby pro vás školení mělo přínos, musí dojít k určité konjunkci hvězd:

  • přednášející musí tématu výborně rozumět
  • přednášející musí umět školit
  • přednášející musí mít školení dobře připravené
  • a vy si musíte vybrat správný kurz (a ptát se)

Že neříkám nic objevného? Kéž by. Divili byste se, kolik firem posílá lidi na školení k pánům nikdy-jste-o-mně-neslyšeli, jsem-o-kapitolu-před-vámi nebo říkají-mi-uspavač-hadů. Není lepší jít na doopravdické školení? Obzvlášť pikantní je, když se na takovém doopravdickém školení objeví osoba ze známé-školící-firmy, aby se to naučila, bo za týden to sama školí 🙂

Jak je na tom Jakub Vrána, jsou jeho školení doopravická? Nepopiratelně je významná osobnost PHP scény, jeho jméno najdete dokonce v dokumentaci. Navíc disponuje schopností přednášet, jeho výklad je srozumitelný a působí přirozeně. A když jsem z něj ve svých školitelských začátcích mámil know-how, shodli jsme se na tom, že jednomu kurzu věnujeme téměř rok příprav. Splňuje tedy všechny tři body. Čtvrtý už je na vás. Když ho splníte, tak vězte, že se školení rozhodně vyplatí.


Při psaní tohoto článku zemřelo jen zanedbatelné množství zvířat a žádný Davídek nebyl podplacen.


Konečně je tu PHP 5.3

Přichází trošku jako opozdilec, pro kterého včera večer byla přichystána velkolepá oslava, ale dnes už se hosté rozešli domů. Před rokem a půl byl internet plný článků o klíčových novinkách v PHP 5.3, po neustálém odkládání termínů a méně příjemných změnách nadšení vychladlo.

5 – 4 – 3 – 2 – 1 DOWNLOAD!

Jaké jsou novinky? Podstatné. Vezmu to podle důležitosti:

To klíčové přijde v následujících týdnech: jak rychle se PHP 5.3 rozšíří? Vzhledem k tomu, že bylo vyvíjeno jako přirozený nástupce řady 5.2 bez zpětně nekompatibilních změn, šance na brzké rozšíření rozhodně má. Nicméně si myslím, že hostéři budou čekat, až vyjde následující setinková verze. Jako když se u Windows čekává na první service pack.

Historie jim přitom dává za pravdu:

  • PHP 5.0.0, které vyšlo s rozdílem 14 dní právě před pěti lety, bylo použitelné cca od verze 5.0.3.
  • PHP 5.1.0 existovalo pouhé 4 dny a bylo nahrazeno 5.1.1, za dva týdny 5.1.2.
  • PHP 5.2.0 bylo opět případem zabugované verze, kde smutnou roli sehrál Debian.

(Totiž Debian Etch má tuto verzi předinstalovanou a nepochopitelný konzervatismus správců vede k tomu, že odmítají PHP updatovat, bo „jen to, co je v Debianu, je stabilní.“)

Přeji PHP 5.3 rychlý a úspěšný nástup do světa!


phpFashion © 2004, 2025 David Grudl | o blogu

Ukázky zdrojových kódů smíte používat s uvedením autora a URL tohoto webu bez dalších omezení.