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:

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.

Jakub Vrána, Řešení problému, 7.11.2008, diskuse: 22 (nové: 0)

Diskuse

ikona Karel Dytrych:

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.

ikona Jakub Vrána OpenID:

PHP má i další implementaci: http://www.php-compiler.net.

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 :-(

ikona Jakub Vrána OpenID:

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ý.

ikona 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)

ikona Jakub Vrána OpenID:

Dobrý trik.

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?

ikona Jakub Vrána OpenID:

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?)

ikona Jakub Vrána OpenID:

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.

Používejte diakritiku. Vstup se chápe jako čistý text, ale URL budou převedeny na odkazy a PHP kód uzavřený do <?php ?> bude zvýrazněn. Pokud máte dotaz, který nesouvisí s článkem, zkuste raději diskusi o PHP, zde se odpovědi pravděpodobně nedočkáte.

Jméno: URL: Reakce na: Karel

ikona 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).

LLook:

V komentářích v manuálu je k nalezení ještě jiný trik, využívá toho, že parse error uvnitř evalu vyvolává pouze warning: http://cz2.php.net/manual/en/function.php-check-syntax.php#77318

Akorát místo ob_start() + ob_end_clean() by bylo daleko vhodnější použít at-operátor.

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".

ikona Jakub Vrána OpenID:

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.

Melda:

A co treba http://cz2.php.net/manual/en/reserved.variables.phperrormsg.php

ikona v6ak:

Kromě toho, že tu jsou modernější metody, toto nemůže fungovat s chybou, která přeruší chod.
avatar © 2005-2018 Jakub Vrána. Publikované texty můžete přetiskovat pouze se svolením autora. Ukázky kódu smíte používat s uvedením autora a URL tohoto webu bez dalších omezení Creative Commons. Můžeme si tykat. Skripty předpokládají nastavení: magic_quotes_gpc=Off, magic_quotes_runtime=Off, error_reporting=E_ALL & ~E_NOTICE a očekávají předchozí zavolání mysql_set_charset. Skripty by měly být funkční v PHP >= 4.3 a PHP >= 5.0.