07th Úno 2008

Narážím na error_reporting: Notice

Používáte výpis errorů v php i s notičkama? Pokud ano, jak se vypořádáváte s neznámým indexem u pole?

if ( $_GET['udelej_mi_to'] == 'nezapomenutelně' ) {
...

Mám-li tento kód, zapnuté vypisování Notice a řekněme prázdný $_GET, dočkám se chyby

Notice: Undefined index: udelej_mi_to in kdesicosi.php on line 482352 

Měl bych tedy podle vývojářů php nejprve pomocí isset() otestovat zda index existuje a potom se ptát, zda odpovídá mým představám? Přijde mi to zbytečné. Mě zajímá, jestli je v $_GET[‚udelej_mi_to‘] schované můj řetězec. Je mi jedno jestli existuje nebo ne. Nebo co v něm skutečně je.

Používáte tedy E_NOTICE? Dá se nějak z errorů s noticemi vyhodit tento typ chyby? Je to vůbec chyba?

24 Responses to “Narážím na error_reporting: Notice”

  1. Ronnie Says:

    No je možný potlačit zobrazení chyby přes

    if (@$_GET['udelej_mi­_to'] == 'nezapomenutelně') {…

    Nebo v Zendu

    if ($this->_request->getQuery('udelej_mi_to') == 'nezapomenutelně') {…
  2. Ivan Novakov Says:

    Ja to mam pro jistotu zaply, protoze to casto ukazuje na potencialni problemy a to, ze nekde nemam neco spravne nastavene. K promennym GET a POST nepristupuju primo, ale mam je zabalene do trid, ktere krome isset() delaji i validaci i dalsi uzitecne veci. Navic, kontrola pomoci isset() dovoluje odlisit pripady, kdy promenna ma hodnotu 0,'' apod. od pripadu, kdy promenna neni vubec nastavena.

  3. VS Says:

    E_NOTICE používám. Jsou nepostradatelné při ladění.

    Je spousta možností, jak se s tvým problémem vypořádat.
    Ať už zmíněný isset() nebo funkce empty(), která vrací true při neexistující proměnné, null, false, 0 a prázdném řetězci.

    Další možnost je si na to napsat vlastní jednoduchou funkci, které předáš hodnotu k porovnání a třeba i hodnotu defaultní, když nebude proměnná nebude existovat.

    Nebo používat některý framework, který už takovou funkci má napsanou :-)

  4. Hever Says:

    Funkci rozhodně nechci … prostě z toho principu, že mě nezajímá jestli existuje nebo co (pokud chci rozlišovat mezi nulou a neexistencí, řeším to patřičně jinak). Nechci provádět isset ať už inline nebo ve funkci, obojí přivodí podivné znepřehlednění kó­du.

    To co jsem hledal je ten @zavináč. Úžasné. Děkuji. Přes všechno úsilí se mi to nepodařilo najít. Výborně.

  5. Hever Says:

    Ještě dodám, že nemusí jít vždy jenom o proměnno GET. Například.:

    preg_match("~^ *`([a-zA-Z0-9_]+)` *([a-zA-Z0-9]+)(?:\(([^\)]*)\))?(?: [^ ]+)*( NOT NULL)?(?: default (?:'([^']*)')|([0-9]+)|(NULL))?(?: auto_increment)?,~U",$tmp,$arr)

    … mi jakýmsi způsobem analyzuje to co mi vrátí SHOW CREATE TABLE. Poslední tři ukládáné hodnoty jsou ale nepovinné. Když si je potom někde přepisuju, stačí přece

    'default'=>$arr[5].$arr[6].$arr[7]

    resp.

    'default'=>@$arr[5].@$arr[6].@$arr[7]

    místo

    'default'=>isset($arr[5]?$arr[5]:'').isset($arr[6]?$arr[6]:'').isset($arr[7]?$arr[7]:'')
  6. Dundee Says:

    Je pravda, že toto jsem kdysi také řešil. Nakonec jsem dospěl k tomu, že jsem si všechny typické pole obalil (GET, POST) a pro zjištování jestli můžu pole poslat do cyklu foreach jsem si napsal funkci:

    function iterable($array){
    return isset($array) && is_array($array) && count($array) > 0;
    }

  7. Jan Tichý Says:

    Ano, rozhodně mám E_NOTICE zapnuté, zachrání člověka před hromadou jinak velice špatně odhalitelných chyb. Neexistující index pole testuji přes konstrukty isset či empty – je to úplně stejná neinicializovaná proměnná, jako jakákoliv jiná.

  8. Jakub Vrána Says:

    Já programuji s vypnutými E_NOTICE. Člověk musí dávat trochu víc pozor, ale ušetří si spoustu psaní nebo vytváření obálek.

    Z bezpečnostního hlediska je pokus o přístup k neinicializo­vanému prvku inicializovaného pole v pořádku (na rozdíl třeba od přístupu k neinicializované proměnné nebo užití nedefinované konstanty), takže je škoda, že PHP pro tyto případy používá stejnou úroveň chyb, ale s tím už člověk nic nenadělá.

  9. dgx Says:

    Při ladění je vhodné zapnout vypisování všech chyb a program je potřeba psát tak, aby žádné negeneroval. Chyby kategorie E_NOTICE patří k těm nejkritičtějším, většinou upozorňují na těžko odhalitelný překlep v kódu.

    Potlačování chyb přes @ je nutné se širokým obloukem vyhnout, to je úplně zcestná praktika. Napadá mě jen jedna výjimka – otevírání souboru, u kterého kvůli vícevláknovému běhu skriptu nemůžeme ověřit, jestli existuje. V takovém případě je vhodné do kódu připsat komentář // intentionally @

    Mimochodem, @$_GET[‚xxx‘] je asi 1000× pomalejší, než isset($_GET[‚xxx‘]) ? $_GET[‚xxx‘] : NULL;

    Dalším možným řešením je funkce ifsetor, která kvůli nepochopitelným dohadům dosud není implementovaná v PHP, ale dá se cca nahradit takto:

    function ifsetor(&$key, $default = NULL)
    {
    if ($key === NULL) return $default;
    return $key;
    }

    Pak lze psát: $var = ifsetor($_GET[‚x­xx‘]);
    Vedlejší efekt této implementace je ten, že pokud prvek $_GET[‚xxx‘] neexistuje, bude inicializován na hodnotu NULL..

    ad regulární výraz: lze to řešit také doplněním prázdných () na konec výrazu

  10. Hever Says:

    Ta pomalost je zajímavá (zarážející … mimochodem napadá někoho proč to tak?).

    Zajímala by mě filozofická mě otázka, co by bylo špatně, kdyby se jádro php naprogramovalo tak, že při iniciování neexistujícího indexu by se místo generování E_NOTICE chyby chyba negenerovala a vrátilo se NULL. Stejně isset($var=null) a isset($neinici­alizovana) vrací oboje false – z toho plyne, že se mi smývají rozdíly mezi neinicializovanou proměnnou a inicializovanou na NULL.

    () na konec regulárního výrazu mě napadlo dát, ale přišlo mi to nepěkné a pak mi to hryzalo ve svědomí.

  11. dgx Says:

    [10]

    isset($var=null) je nespíš chyba, nechtěl jsi napsat $var === NULL?

    Ano, týká se to filosofie přístupu. Kdybych se měl rozhodnout, jestli neinicializovaná proměnná nevygeneruje nic, nebo vygeneruje fatal error, bez zaváhání bych se hlásil k tomu druhému. Zatímco příznivci volnějších jazyků (například Ruby) by se mohli spíš klonit k prvnímu řešení.

    ad pomalost @:
    tipuju, že kód @$_GET[‚xxx‘] se interně přeloží na:
    $old = error_reporting(0);
    $_GET[‚xxx‘];
    error_reportin­g($old).

    Tedy 2× volání funkce + práce s dočasnou proměnnou vs. varianta bez volání funkce (isset není fce), to dělá hodně.

  12. Jakub Vrána Says:

    [9] Ze stejné škatulky je potlačení chyb při vytváření adresářů.

    [9] Kdo by chtěl variantu bez postranních efektů, může si napsat funkci array_get($pole, $klic, $vychozi).

    [11] Ano, kód je skutečně napsán takto.

  13. Hever Says:

    [11] kdybych se měl rozhodnout, jestli neinicializovaná proměnná nevygeneruje nic, nebo vygeneruje fatal error …

    U komentářů jsem se toho moc nedržel, ale celkově se chci bavit ne o proměnných, ale o indexu u pole.

    isset($var=null) … ne, myslel jsem to jak jsem to napsal, akorát jsem to zdrcl –

    $var = NULL;
    isset($var);

    V tomto případě vrací isset false. Stejně si neověřím, zda je v proměnné NULL nebo zda je neiniciovaná … protože když se se budu ptát $var === NULL a bude neiniciovaná, tak mám na talíři error.

    Ještě k tomu poli … vidím skutečně rozdíl mezi indexem pole a proměnnou. Třeba si nadefinuju nějakou strukturu pole, která se bude předávat funkcím. Potom se postupně dotazuju co je obsahem pole a provádím co je potřeba. Mě přijde elegantní psát prostě


    if($input[‚ty­pe‘]!=‚hidden‘)…
    if($input[‚va­lue‘]!='')…

    – přímočaře, jasně, elegantně. Proč se ještě předtím dotazovat na existenci? Ano, místo neustálého dokola se opakujícího dotazu isset, bych mohl na začátku každé funkce, která přebírá to moje pole pole projít, protože znám jeho předpokládanou strukturu a všude, kde mi isset odpoví false nastavit NULL. Dokud se ty strukury nezanořují a jsou relativně malé… pak by se to asi nevyplácelo. Ale nevím, je to nezbytné?

    E_NOTICE jsem nepoužíval, nedávno jsem je zapl a toto mě překvapilo. Po výše zmíněném uznávám nešikovnost @ zavináče… pak mi ale teda šikovnější zase ty E_NOTICE vypnout.

  14. VS Says:

    [13]

    Na získávání dat z vícerozměrných polí jsem si chtěl napsat pěknou funkci, která by fungovala takhle:

    getInput($_GET, '1.rozmer', '2.rozmer', '3.rozmer');

    a zároveň by při neexistujícím indexu vracela null.

    Ale nebyl jsem ji sám schopen napsat. Nakonec mi pomohl na jednom diskuzáku jeden člověk touto elegantní konstrukcí:

    // predtim tu byla spatna verze, ktera neodpovidala zadani
    function getInput($input)
    {
        $keys = func_get_args();
        array_shift($keys);
        foreach($keys as $key) {
            if (array_key_exists($key, $input)) {
                $input = $input[$key];
            } else {
                return null;
            }
        }
        return $input;
    }
  15. hever Says:

    V napsání nějaké funkce není problém myslím. Jde mi o přístup. Vždyť je to hovadina, takové obezlice. Jenom proto, aby se poddalo pravidlu E_NOTICE.

    … ta má kontrolovat překlepy apod. ale uvedenými způsoby se stejně dosahuje akorát toho, že následné překlepy zůstanou, akorát se nevypíšou jako chyba.

  16. Pavel Šindelka Says:

    [15] Rozhodně souhlasím s Davidem. Já naopak tuto vlastnost PHP vítám a ani náhodou ji nepovažuji za nepříjemnost nebo dokonce „hovadinu“. Podobně jako přístup k neinicializované proměnné, je i přístup a vůbec práce s neinicializovaným polem z principu nevhodná.

    Bez těchto hlášek by se mnohem hůře odhalovaly nejen zmiňované překlepy, ale obecně případy, kdy přijde neočekávatelně asociativní pole v jiném formátu atp. Má praxe říká jednoznačně – E_NOTICE zapnout a poctivě se jin věnovat, jinak se to dříve či později vymstí (hlavně u větších aplikací, na kterých se podílí větší množství vývojářů).

  17. hever Says:

    [16] „Hovadinou“ jsem nazval ty funkce, které místo erroru vracejí null (protože mi přijdou nesmyslné a očekával bych tuto vlastnost od php samotného).

    Můžu se zeptat, jakým postupem kontrolujete ta asociativní pole přicházející do nějaké funkce? Hned na začátku, kdy zkontrolujete strukturu a případě špatné struktury vypisujete error? Pokud ano, jak probíhá kontrola těch částí pole, která nemusejí být vyplněna – jsou nepovinná? Nebo kontrolujete v běhu, kdy se před dotazem na ten který index vždy první ptáte na existenci ..? Co když neexistuje a je nepovinný?

  18. Jakub Vrána Says:

    [16] Práce s neinicializovaným polem je samozřejmě nevhodná. Ale práce s neinicializo­vanými prvky inicializovaného pole je bezpečná a spolehnutí se na výchozí hodnotu neinicializovaného prvku často dává velmi dobrý smysl. Samozřejmě to je zkratka, ale „značená“, abych tak řekl

    Kód (isset($_GET[„s­eerch“]) ? $_GET[„seerch“] : "") tě od překlepů stejně jako samotné $_GET[„seerch“] nijak neuchrání. Mohl bys rozebrat „přijde neočekávatelně asociativní pole v jiném formátu“?

  19. VS Says:

    [17] v zásadě máš pravdu.

    Tohle sou možnosti, který můžes použít, seřazeno podle mě od nejvíc košér po nejmíň:

    1. používat isset() (takto to použití asi vývojáři php zamýšleli, aby nejméně dvojitým zapsáním proměnné byla eliminována pravděpodobnost překlepu, bohužel pro programátora na psaní nejvíce ukecané.)
    2. napsat si vlastní fci, která vrací null při neexistenci. (pokud se nebojíte překlepů a nechcete vypínat E_NOTICE)
    3. používat @ na potlačení chyb, tam kde chcete.
    4. vypnout zobrazování E_NOTICE.
  20. Hever Says:

    Jakube, díky za pěkné přeformulování toho co mě vrtalo v hlavě. Když se teda znovu vyjádřím..

    • > Chci se spolehnout na výchozí hodnotu (NULL) neiniciovaného prvku pole.
    • > Současné řešení je nevhodné, v zásadě nic neřeší, pouze mě otravuje (vypisováním E_NOTICE).
    • > Dokud bude php tyto E_NOTICE vypisovat a nebude implemetované nějaké přijatelné řešení (např. zmíněné ifsetor), v kódu který má být schopný fungovat všude, budu nejspíš kacířsky používat @zavináč.

    Jinak asi E_NOTICE vypínám budu psát elegantní kód :)

  21. dgx Says:

    [20] počítej s tím, že tato vlastnost PHP se nezmění. Možná se objeví nějaký ifsetor nebo zkrácený ternární operátor, do té doby doporučuji napsat vlastní funkci (jen v komentářích jich bylo zmíněno několik). Dělám to tak taky.

    Ještě tu nebyla zmíněna věc, že jedna věc je psaní kódu, a druhá věc jsou jeho pozdější úpravy. Těm se obvykle věnuje víc úsilí než samotnému psaní, navíc často probíhá s časovým odsupem. Nezavař si do budoucna!

    Až po čase šáhneš do kódu, bude ti důležitým pomocníkem výpis error_reportin­g(E_ALL). Pokud error reporting neporadí, nastoupí zdlouhavé hledání chyby, na jehož konci nezřídkakdy bude řádek „utlumený“ zavináčem. Právě proto radím zavináče nepoužívat. Nedají se totiž narozdíl od úrovní error_reporting snadno vypínat a zapínat.

    Zároveň doporučuji psát kód tak, aby nevyhazoval E_NOTICE, protože když zapnu reportování kvůli hledání chyby, je nepohodlné ji hledat utopenou v hromadě „nechyb“.

  22. Jakub Vrána Says:

    [20] Kód, který má být schopný fungovat všude, může na začátku zavolat error_reporting() a E_NOTICE vypnout. Zavináč opravdu není dobré řešení.

    [21] Zkrácený ternární operátor ($a ?: "") už je v PHP 5.3, ale nikoliv ve významu ifsetor() – na nenastavené proměnný také vyhodí E_NOTICE.

  23. Pavel Šindelka Says:

    [17] K tomu asociativnímu poli – měl jsem namysli ten případ, kdy se kód programu příliš spoléhá na to, že existují konkrétní položky v poli. Pokud by PHP v okamžiku, kdy k těmto položkám program přistupuje (a ony neexistují), neházelo NOTICE ale „tiše“ vrátilo NULL, mnohem hůře by se na chybu přišlo. Už mnohokrát jsem byl nucen hledat chybu v cizím robustním kódu a věřtě mi, že v takových chvílích jsem za NOTICE velmi rád.

    [18] Samozřejmě je to otázka přístupu. Já ve svých myšlenkách nerozlišuji proměnnou od prvku pole. Zkrátka nevidím důvod, proč by PHP v jednom případě mělo k problému přistupovat nějak a v druhém zase jinak.

    A právě to isset mě od překlepů často zachrání, už jenom proto, že ten index zkrátka píšu dvakrát :) Pokud je někdo líný to ošetření dělat, ať si opravdu napíše tu pomocnou funkci. Mimochodem – násilím potlačovat chybový výpis přes @ je podle mě ve většině případů cesta do pekel.

  24. Hever Says:

    Mě jako cesta do pekel příjde spíš kód plný kódu

    (isset($arr[‚prvek‘])­?$arr[‚prvek‘]:­NULL)

    která stejný význam jako

    $arr[‚prvek‘]

    Ale to už se opakuju.

    Rozdíl proměnná vs. prvek vidím v tom, že při deklaraci fce můžu neexistujícím proměnným přiřadit defaultní hodnotu

    function fce($prom = null) { }

    u prvků polí nikoliv. Jedině na začátku každé funkce přijímající pole, které může obsahovat různou množinu prvků, nejprve všechny používané prvky otestovat na existenci a případně jim dát NULL (tím si vymezím co všechno se může v poli předávat). To mi přijde jako jediné možné řešení, ostatní, kdy se neustále v kódu issetuju mi přijde cesta do pekel. Trochu pitomé to je, když se může to pole různě zanořovat.

    Jinak budu nejspíš používat [22] vypnutí E_NOTICE pomocí error_reportin­g() asi.

Leave a Reply

grupa LGBT