Vkládání souborů se syntaktickou chybou
Školení, která pořádám
Pokud PHP nenajde vkládaný soubor, vyvolá při použití include pouze varování (require skončí chybou). Toto varování se dá následně odchytit a zobrazit slušnou chybovou stránku.
Horší je situace v případě, kdy soubor existuje, ale obsahuje syntaktickou chybu. V tom případě PHP vždy skončí chybou. Přemýšlel jsem, jak se s touto chybou vypořádat a po pravdě řečeno jsem pouze navštívil několik slepých uliček:
- auto_append_file se už vložit nestihne
- set_error_handler se nezavolá
- php_check_syntax byl k dispozici jenom chvíli a náhrada přes
`php -l`
mi přijde krkolomná
- Napadlo mě použít možností akcelerátoru, které soubor samozřejmě také musí nejdřív zkontrolovat, ale apc_compile_file mi vždy vrací false a obdobu této funkce v rozšířenějším eAcceleratoru jsem nenašel
- Funkce runkit_lint_file funguje spolehlivě, ale k jejímu použití je nejprve potřeba zapnout extenzi Runkit, která je dle vlastního sloganu „na všechny ty věci, které byste stejně neměli dělat“
- Funkce token_get_all rozebere i soubory se syntaktickou chybou
Na závěr je vhodné poznamenat, že soubor se syntaktickou chybou by se samozřejmě do ostrého provozu vůbec neměl dostat. To ovšem znamená mít propracovaný deployment, protože syntaktickou chybu může způsobit např. i nedokončený FTP upload.
Nakonec jsem přece jen jedno řešení našel. Kromě toho by šla na vstup zavolat funkce eval, to by ale zbytečně zdržovalo.
Diskuse
Prijde mi to jako zbytecne nadmerne zatizeni aplikace a serveru neustale kontrolovat jestli je zdrojovy kod s chybou nebo ne... Jak pisete soubor s chybou by se do ostre aplikace vubec dostat nemel a pak uz je to jen o tom deploymentu...
Radeji bych zvolil cestu jednorazove kontroly ihned po uploadu. Asi by se take zatizena ostra aplikace nemela jen tak prehravat novymi soubory, ne?
Asi bych zvolil cestu upload > kontrola > prekopirovani skriptem na strane serveru...
Jirka Grunt:
Jako obvykle v případě PHP, tedy jazyka definovaného pouze jeho jedinou implementací, je to zcela bez záruky a může to kdykoliv v budoucnosti přestat fungovat, ale zatím se funkce registrovaná přes ob_start() na závěr zavolá i v případě, že vykonávání skriptu skončí nějakou fatální chybou (například
syntaktickou).
main.php:
<?php
function out($buffer) {
$buffer .= "after\n";
return $buffer;
}
ob_start('out');
echo "1\n";
require('bad.php');
echo "2\n";
?>
bad.php:
<?php
f(
?>
vypíše:
PHP Parse error: syntax error, unexpected $end, expecting ')' in /home/jirig/test/bad.php on line 5
PHP Stack trace:
PHP 1. {main}() /home/jirig/test/main.php:0
1
after
kozotoč:
napadlo mě prakticky to samé - původně jsem uvažoval stránku nechat vypsat pomocí output bufferingu do proměnné a v ní najít řetězce typu "PHP Parse error: " apod. (dá se předpokládat, že nic takového se na stránkách neobjeví (pokud se daný projekt nezabývá programováním v php).)
nevím, jak udělat kontrolu při samotném uploadu.
místo toho mě napadlo udělat "robota", který bude na pokyn admina nebo crona stránky procházet a tadyto kontrolovat.
Jirka Grunt:
Můj příklad bych snad až příliš triviální.
Ono by stačilo někdy "hodně brzo" nastavit nějakou stavovou informaci na 'začato' a někde "hodně pozdě" ji nastavit na 'dokončeno', takže když by se do výstupní rutiny program dostal ve stavu 'začato', byl by někde problém.
Samozřejmě to není řešení, které má úspěšnost 100%, a samozřejmě se také na problém přijde až dodatečně, ale nestojí téměř nic ho implementovat. Jen je potřeba být hodně pečlivý, aby ve výstupní rutině nebyla žádná chyba ;-)
Jinak s výpisem chyb do stránky jde v zásadě odchytávat přímo i fatal error chyby, jak se zmiňujete, ale bohužel to nefunguje vždy. Vzpomínám si, že v některých starších verzích PHP5 to nefungovalo přinejmenším při některých chybách v metodě __toString(), ale protože její chování se mezitím změnilo a já už to přestal sledovat, nemám žádný aktuální protipříklad.
Jirka Grunt:
To jsem nevěděl, díky.
Nicméně, znamená to tedy, že správně napsaný kód v PHP je takový, který funguje v obou implementacích? Nebo má jedna z nich přednost? ;-)
Nebo je jazyk PHP definovaný tím, co popisuje manuál? Situace se sice pomalu zlepšuje, ale u řady poměrné důležitých vlastností trvalo opravdu dlouho, než se v manuálu objevily; a i pak tam často jsou jen jako neformální poznámka (vzpomínám si třeba na dědičnost rozhraní nebo definici konstant v rozhraních).
Začalo mě to zajímat a trápit teprve ve chvíli, kdy jsem začal číst changelogy -- když občas vidím, jak se chyby mění ve vlastnosti a naopak (třeba u reflexí nebo magických metod to byl vývoj přes mnoho verzí), skoro se mi zdá, že řadu i dokumentovaných rysů je bez vlastních jednotkových testů dost nebezpečné využívat :-(
Jakub Vrána :
Situace je bohužel opravdu taková, že PHP je „definováno“ svou hlavní implementací. Vývoj je i po těch letech bohužel stále živelný.
Jan Dolezel:
Nevim, jestli je toto reseni nebo alepson zarodek toho co hledate:
1. presmerovat na chybovou stranku (header("Location: error.html");)
2. zprocesovat soubory...
3. zrusit presmerovani
4. standardni vystup...
pri chybe (i fatal erroru) se provede presmerovani na zadanou adresu (error.html)
Karel:
nerozumím. můžeš vysvětlit podrobněji?
header() se dá poslat před zahájením výpisu na výstup. poté, co začneš něco vypisovat, jak provedeš "zrušení přesměrování" aniž by ti to zahlásilo "headers already sent"? (možná, že to je podstata, proč zatím nechápu) je ten trik právě v tomto?
jinými slovy: jak se vyrovnáš s chybou, která se může ukrývat v momentu, když už máš něco na stránku vypsáno?
Jakub Vrána :
Finta spočívá v tom, že se přesměrování nastaví dřív, než se začne cokoliv provádět. Pokud se něco nepovede, přesměrování se provede. Když se všechno povede, tak předtím, než se začne něco vypisovat, se přesměrování zruší.
Karel:
Aha, díky (mě to pak stejně napadlo). Nicméně:
1) Dá se tímto způsobem zjišťovat syntaktická správnost i poté, co se začne vypisovat (resp. je na to tato finta vhodná/aplikovatelná)?
2) A pokud ne, dají se mimochodem nějak jednoduše "umlčet" všechna echo, print &spol. jenom pro případ vyzkoušení syntaktické správnosti pomocí této finty? (že by zase output buffering?)
Jakub Vrána :
1. Aplikace se dá navrhnout tak, že jakmile se něco začne vypisovat, už se žádné soubory nevkládají.
2. Ano, slouží k tomu opět output buffering.
Karel:
Když už o tom uvažuju - nedá se udělat prvotní přesměrování v <meta> v HTML. A to konkrétně opožděně? (Ale, jak by se potom nakonec rušilo, hm?)
A když už tak nepřesměrovávat na error.html ale na syntax-error.php?soubor=includovany_soubor.inc.php&hlavni=main.php, který chybu zaregistruje a nějak na ní zareaguje - minimálně to oznámí adminovi po přihlášení, zapíše do logu, pokud není v debug módu, tak pošle zprávu atd. atp.
v6ak:
No myslím, že se provedou destruktory i funkce v register_shutdown_function.
Jirka Grunt:
Nevím, jaký je aktuální stav, ale mám dojem, že to kdy a zda vůbec se zavolají destruktory, se v průběhu vývoje PHP5 několikrát změnilo. Jestli si to dobře pamatuji, záviselo to i na tom, jaké části/vlastnosti jazyka se zrovna používají, ale můžu si to pamatovat špatně (myslím, že to souviselo se sessions).
Non_E:
Ideální mi přijde si na serveru nejdříve v jiném virtualhostu stánky otestovat a ostré nasazení provést například přes "svn up".
Jakub Vrána :
Ano, mě taky. To je ten propracovaný deployment, o kterém jsem mluvil.
Elixon:
Nebo mít daemon, který monitoruje změny v daném adresáři a při jakékoliv změně PHP souboru spustí `php -l` a poškozené soubory přejmenuje/přesune a upozorní příslušného člověka.
v6ak:
Kromě toho, že tu jsou modernější metody, toto nemůže fungovat s chybou, která přeruší chod.
Diskuse je zrušena z důvodu spamu.