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?
No je možný potlačit zobrazení chyby přes
Nebo v Zendu
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.
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
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ě.
Ještě dodám, že nemusí jít vždy jenom o proměnno GET. Například.:
… 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
resp.
místo
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;
}
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á.
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 neinicializované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á.
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[‚xxx‘]);
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
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($neinicializovana) 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í.
[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_reporting($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ě.
[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.
[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[‚type‘]!=‚hidden‘)…
if($input[‚value‘]!='')…
– 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.
[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:
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í:
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.
[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ářů).
[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ý?
[16] Práce s neinicializovaným polem je samozřejmě nevhodná. Ale práce s neinicializovaný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[„seerch“]) ? $_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“?
[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íň:
Jakube, díky za pěkné přeformulování toho co mě vrtalo v hlavě. Když se teda znovu vyjádřím..
Jinak asi E_NOTICE vypínám budu psát elegantní kód :)
[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_reporting(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“.
[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.
[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.
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_reporting() asi.