Jan 10 2010
Změny vestavěných vyjímek v PHP 5.3
Tento článke je k dispozici pouze v jazyce English.
Komentáře nejsou povoleny
Jan 10 2010
Tento článke je k dispozici pouze v jazyce English.
Komentáře nejsou povoleny
Nov 10 2009
Zase jsem se dnes naučil něco nového o PHP. Věděli jste, že klíčové slovo break může mít číselný argument? Já se přiznám že jsme to netušil a nikdy mě to ani nenapadlo. Kolega v práci to zřejmě běžně používá, takže díky Pauci za objev :) A jak to celé funguje a na co to je? Volitelný celočíselný argument udává, z jaké hloubky zanoření v řídící struktuře má vyskočit, implicitní je 1, podívejte se na příklad:
<?php $i = 0; while (++$i) { switch ($i) { case 5: echo "At 5<br />n"; break 1; /* vyskoci pouze ze switch. */ case 10: echo "At 10; quitting<br />n"; break 2; /* vyskoci ze switch a while. */ default: break; } } ?>
Více přímo v manuálu PHP.
Další zajímavostí je řídící struktura goto. Ta je nově přidána od PHP 5.3, osobně si myslím ze goto je čisté zlo, ale určitě existuje případ, kdy se může hodit. Co mě ale zaujalo nejvíce, přímo v manuálové stránce PHP je naprosto překvapivě umístěn obrázek (s tím jsem se v manuálu PHP nikdy nesetkal), který je navíc pojatý jako komiks a zřejmě vyjadřuje názor autora(ů) manuálové stránky na přidání struktury goto do jazyka :)
Zdroj obrázku: xkcd.
Aug 06 2009
Při vytváření nových verzí/revizí nějaké aplikace (ať již pomocí subversion či jiného nástroje) je často vhodné a někdy i potřebné aktualizovat strukturu databáze (přidání nových tabulek, sloupců, atd.) případně aktualizovat data (číselníky, systémové hodnoty) v databázi tak, aby nová změna v kódu byla kompatibilní se změnou struktury či dat v databázi (pro úplnost pouze doplním, že článek je napsán pro databázi MySQL 5.x).
Otázka je, jak tuto změnu provést tak, aby jsme při aktualizaci projektu na příslušnou revizi měli k dispozici i aktuální verzi databáze. Jedním z jednodušších řešení je, že spolu s commitem upraveného kódu (například nějakého modelu) commitneme i skript, který po aktualizaci projektu na příslušnou revizi spustíme a tím zajistíme upgrade databáze.
Tento skript může mít různé podoby, buď ho můžeme napsat přímo v PHP a nebo v případě jednodušší aktualizace použijeme pouze několik SQL příkazů. Nedávno jsem zrovna řešil poměrně jednoduchý problém, bylo třeba přidat skript do databáze šablonu i s jednou jazykovou mutací — to znamená: přidat jeden řádek do tabulky sablona
a jeden řádek do tabulky sablona_mutace
. Jelikož je však při definici ID šablon i mutací použit extra typ AUTO_INCREMENT, tak vložení druhého řádku s mutací bude záviset na ID vloženého řádku šablony. V SQL skriptu nemůžu přímo ID definovat, protože v době kdy se bude skript (upgrade) provádět může mnou vybrané ID šablony už být použito, proto je potřeba nechat sloupec NULL a MySQL vloží vlastní ID dle hodnoty AUTO_INCREMENT tabulky.
Řešením tohoto problému je použití funkce LAST_INSERT_ID(), po vložení řádku šablony definujeme cizí klíč ID šablony v řádku mutace právě pomocí této funkce. Výsledek by pak mohl vypadat nějak takto:
INSERT INTO sablona (id, nazev, kategorie, datum) VALUES (NULL, 'Neuskutečněná schůzka', 'system', NOW()); INSERT INTO sablona_mutace (id, sablona_id, jazyk_id, telo) VALUES (NULL, LAST_INSERT_ID(), 1, 'Schůzka ne neuskutečnila ...');
Takto jednouše by mohl vypada SQL skript, PHP skript kterým by jste udělali to samé například v Zend_Frameworku by mohl vypadat přibližně takto:
... $sablona = array( 'nazev' => 'Neuskutečněná schůzka', 'kategorie' => 'system', 'datum' => new Zend_Db_Expr('NOW()'), ); $sablona_id = Sablona::getInstance()->insert($sablona); $mutace = array( 'sablona_id' => $sablona_id, 'jazyk_id' => 1, 'telo' => 'Schůzka ne neuskutečnila ...', ); $mutace_id = SablonaMutace::getInstance()->insert($mutace); ...
Poznámka k PHP příkladu: modely jsou potomky třídy Zend_Db_Table a jsou vytvořeny jako singleton; hodnoty sloupce kategorie a jazyk_id v příkladu jsou magic konstanty což není úplně programátorsky čisté — je to pouze pro jednoduchost příkladu.
Komentáře nejsou povoleny
Jun 29 2009
O tom, jak zvýšit bezpečnost PHP aplikací již bylo napsáno moc a moc. Prvním a základním pravidel je filtrování vstupů uživatelů, na toto téma již bylo popsáno nepřeberné množství přístupů a naprogramováno spousty knihoven, které dělají téměř vše za vás. Druhým, neméně důležitým aspektem je konfigurace PHP direktiv, ať již v přímo v php.ini
nebo pomocí ini_set(...)
či v htaccessu. To, na jaké direktivy by jste se měli zaměřit především, se pokouší odpovědět i PHP Security Consortium ve svém projektu PhpSecInfo.
PhpSecInfo se zaměřuje na řadu klíčových direktiv, které jsou z hlediska bezpečné konfigurace PHP důležité, doporučuje jejich nastavení, popisuje důvody proč je nastavit tak a tak a poskytuje vám přehlednou aplikaci v PHP na to, aby jste si sami mohly vyzkoušet nastavení vašeho serveru či virtualhostu.
Pojďme se blíže na jednotlivé testy a k nim odpovídající direktivy podívat (seznam testů je řazen abecedně, nikoliv podle důležitosti):
file_get_contents()
, include()
, require()
, … na vzdálené soubory, což může způsobit vložení závadného kódu z cizího serveru pokud správně neošetříte vstupyaddslashes
addslashes
není zcela dostačující pro některé speciální případy a proto se musí používat speciální escapovací funkce v závislosti na použité databázi, spoléhat tedy na „auto-escapování“ není dobré a proto se doporučuje direktivu nezapínatPopsané výchozí nastavení se vždy nemusí shodovat v vašim výchozím nastavení, některé distribuce a operační systémy a některé verze PHP si nastavují tyto hodnoty různě.
Před nastavením dané proměnné je vždy také třeba zvážit, zda bude server či VirtualHost pro více projektů/uživatelů. Důležité je také zvážit, jestli nasazení omezujících pravidel v podobě změn PHP direktiv neovlivní současné projekty na serveru, které by se pak museli předělat vzhledem ke změně nastavení. Proto je vhodné použít kombinaci jak globálních nastavení v php.ini
, tak lokálních pomocí ini_set(...)
či htaccessu
.
Bohužel, projekt PhpSecInfo už je přes dva roku opuštěn (poslední verze je z 2007/04/06) a těžko říci, zda kdy bude ještě aktualizován. Myslím že je to škoda, protože stále vznikají nové a nové hrozby, přidávají se nové konfigurační direktivy (a některé se ruší) a pro začínajícího ale i pokročilého administrátora mohl být projekt dobrým zdrojem informací a inspirace.
Komentáře nejsou povoleny
Jan 08 2009
Občas je třeba provést na stránkách údržbu tak, že je třeba na chvíli zastavit zobrazování webu – ať už je to údržba databáze nebo cokoliv jiného. V takovém případě se změní například index.php
aby zobrazoval „nějakou hlášku o údržbě“. Problém ale může nastat, když zrovna v tu chvíli indexuje váš obsah třeba GoogleBot. Vaše hláška o údržbě pro něj bude obyčejná informace vrácená typicky s hlavičkou HTTP statusu 200 (viz. RFC HTTP status 200) a GoogleBot stránku pěkně zaindexuje a pak se nestačíte divit co že to google vrací za výsledky na vašem webu :)
Na toto existuje doporučení přímo od Googlu: „… nastavte server tak, aby vrátil status 503 (viz. RFC HTTP status 503) namísto statusu 200 …“. V PHP jednoduchá věc pomocí funkce header(), ideálně ještě doplníme vlastní hlášku o údržbě:
<?php header('HTTP/1.1 503 Service Temporarily Unavailable'); header('Status: 503 Service Temporarily Unavailable'); ?> <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"> <html><head> <title>503 Service Temporarily Unavailable</title> </head><body><h1>Service Temporarily Unavailable</h1> <p>The server is temporarily unable to service your request due to maintenance downtime or capacity problems. Please try again later.</p> </body></html>
Navíc, podle RFC – Header Field Definitions je může být použito v hlavičce u statusu 503 pole Retry-After:
The Retry-After response-header field can be used with a 503 (Service Unavailable) response to indicate how long the service is expected to be unavailable to the requesting client. This field MAY also be used with any 3xx (Redirection) response to indicate the minimum time the user-agent is asked wait before issuing the redirected request. The value of this field can be either an HTTP-date or an integer number of seconds (in decimal) after the time of the response.Retry-After = "Retry-After" ":" ( HTTP-date | delta-seconds ) Two examples of its use are Retry-After: Fri, 31 Dec 1999 23:59:59 GMT Retry-After: 120 In the latter example, the delay is 2 minutes.
Tedy, navíc ještě přidáte hlavičku Retry-After s hodnotou (v sekundách, nebo přímo časem), za kterou předpokládáte že web pojede – na příklad za 1 hodinu (3600s):
header('HTTP/1.1 503 Service Temporarily Unavailable'); header('Status: 503 Service Temporarily Unavailable'); header('Retry-After: 3600');
A závěrem ještě příklad, pro odeslání statusu 503 v Zend Frameworku:
$this->getResponse()->clearBody(); $this->getResponse()->setRawHeader('HTTP/1.1 503 Service Temporarily Unavailable'); $this->getResponse()->setRawHeader('Status: 503 Service Temporarily Unavailable'); $this->getResponse()->setRawHeader('Retry-After: 3600'); $msg = '<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">'; $msg.= '<html><head><title>503 Service Temporarily Unavailable</title>'; $msg.= '</head><body><h1>Service Temporarily Unavailable</h1>'; $msg.= '<p>The server is temporarily unable to service your request due to maintenance downtime or capacity problems. Please try again later.</p>'; $msg.= '</body></html>'; $this->getResponse()->setBody($msg); $this->getResponse()->sendResponse();
Komentáře nejsou povoleny
Nov 19 2008
Vždycky jsem si myslel, že pokud není u funkce v PHP definován parametr jeho defaultní hodnotou — function Foo($a = ''){}
— bere se, že je parametr povinný. Moje chyba, PHP mě opět vypeklo a přitom v tak elementární věci.
Mějme příklad:
<?php function Bar($stuf) { echo $stuf; } Bar();
výsledek:
Warning: Missing argument 1 for Bar(), called in /var/www/error.php on line 6 and defined in /var/www/error.php on line 2
Notice: Undefined variable: stuf in /var/www/error.php on line 4
Výstup mě nemile překvapil, nejen že volání funkce nepadne na Fatal Error ale navíc se tělo funkce provede s nedefinovanou hodnotou proměnné $stuf
. Asi bude třeba na každý parametr volat isset(...)
. Jak geniální. Zdá se že povinné parametry v PHP neexistují.
Myslím že krása PHP spočívá právě v jeho nevyzpytatelnosti ;) Takže zkusme ještě podobný příklad, s určením typu „nepovinného“ parametru:
<?php function Bar(array $stuf) { echo $stuf; } Bar();
Výsledek je pak zcela logicky a jasně odvoditelný (!?!):
Catchable fatal error: Argument 1 passed to Bar() must be an array, none given, called in /var/www/error.php on line 6 and defined in /var/www/error.php on line 2
A proto v PHP rád programuji … spousty let a stále se učím základům :))
Vyzkoušeno na PHP 5.2.6.
Komentáře nejsou povoleny
Mar 31 2008
Routování v Zend Frameworku (ZF) není nic zvláště složitého. Stačí pouze lehce nahlédnout do dokumentance a začít používat defaultní routovaní případně si nastavit nějaká jiná vlastní pravidla.
Problém ale nastává v případě, že chcete do routování zahrnout subdomény. Na to ZF není přímo (zatím? — v době psaní tohoto článku je stable verze 1.5) vybaven a je tedy třeba použít kousek „vlastního kódu“.
Mar 22 2008
Jedná se o velmi triviální příklad dopředné neuronové sítě se třemi neurony a třemi vrstvami (vstupní vrstva, jedna skrytá vrstva a výstupní vrstva) a sigmoidou jako aktivační funkcí. Síť se dvěma vstupy a jedním výstupem lze naučit jednoduché logické funkce (např. XOR).
Mar 03 2008
Co vypíše následující kód?
<?php $klic = 'klic'; $arr = array($klic => 'hodnota'); var_dump(isset($arr[$klic]['cokoliv'])); ?>
Komentáře nejsou povoleny
Feb 14 2008
eAccelerator je velmi užitečný nástroj pro „zrychlení“ PHP. Funguje na principu vytváření mezipaměti (cache) PHP skriptů a v případě že máte na serveru slabší stroj, nebo prostě jen potřebujete maximálně zrychlit vaše stánky je vhodné uvažovat o jeho použití. Jeho výhody se ukáží především při programování projektů založených na mnoha knihovnách, které se v průběhu vývoje nemění nebo pouze minimálně. eAccelerator vytvoří cache všech těchto PHP skriptů a není tedy nutné je při každém načtení stránky znovu kompilovat.