Kirjoittaja: map_
Kirjoitettu: 30.12.2009 – 30.12.2009
Tagit: koodi näytille, vinkki
PHP:ssa halutaan usein pitää E_NOTICE-virheilmoituksia päällä.
Eräs toisinaan ärsyttävä E_NOTICE-tason virhe on taulukon indeksoiminen olemattomalla avaimella. Esim. $_GET['sana']
aiheuttaa E_NOTICE-tason virheen, mikäli 'sana'-parametri jostain syystä puuttuu.
Suoraviivaisin tapa päästä näistä virheistä eroon on tehdä isset-tarkistuksia, kuten $sana = isset($_GET['sana']) ? $_GET['sana'] : '';
, mutta tämä käy pidemmän päälle tylsäksi.
Listauksessa määritellään luokka DefaultingArrayObject
, jonka ilmentymät toimivat kuin taulukot, mutta niitä voi aina indeksoida millä tahansa avaimella. Toteutus on suoraviivainen, sillä PHP:ssa on jo valmiina taulukon lailla käyttäytyvä luokka, ArrayObject
, joka voidaan periä.
Lisäys: Eräs toinen ratkaisu esitettyyn ongelmaan on @
-operaattori. Myöskään @$_GET['sana']
ei aiheuta virheilmoitusta, mutta ratkaisu ei ole ihan ongelmaton. Ks. tämän vinkin kommentit.
<?php // DefaultingArrayObject.php class DefaultingArrayObject extends ArrayObject { protected $_default; public function __construct($array = array(), $default = null) { parent::__construct($array); $this->_default = $default; } public function setDefault($default) { $this->_default = $default; } public function offsetGet($index) { return $this->getWithDefault($index, $this->_default); } public function getWithDefault($index, $customDefault) { if (parent::offsetExists($index)) { return parent::offsetGet($index); } else { return $customDefault; } } }
<?php // Esimerkki error_reporting(E_ALL | E_NOTICE); require_once('DefaultingArrayObject.php'); $input = new DefaultingArrayObject($_GET); if ($input['sana'] == 'Firefly') { // Ei E_NOTICEa vaikka 'sana' puuttuu echo "Hyvä!"; }
Näppärä
Hmm...
Onhan tuo näppärä, mutta $_REQUEST-taulukko ajaa saman asian.
$_REQUEST kyllä yhdistää $_GETin, $_POSTin ja $_COOKIESin, mutta antaa edelleen E_NOTICEn kun sitä indeksoi olemattomalla avaimella.
echo $_REQUEST['asdasd'];
-->
Notice: Undefined index: asd
Miksei voi vaan käyttää echo @$_REQUEST["nothing"]; ?
Kas, tuon operaattorin olemassaolo olikin unohtunut. Tästä vinkistähän tulikin suurin piirtein hyödytön :)
Juuri tuollaista pitkää ja toisteista muotoa oli tarkoitus välttää.
@-operaattori vain piilottaa virheen rumasti kun taas tällä saadaan palautettua ilman virhettä mikä tahansa haluttu arvo.
@ on kyllä yleisesti ottaen ruma piirre kielessä, ja useimmiten sitä ei kannata käyttää, mutta aiheuttaako se tässä tapauksessa mitään konkreettisia ongelmia?
Virhekontrollioperaattori on herkkä aiheuttamaan ennalta odottamattomia ongelmia. Mitä jos odotettua taulukkoa ei löydykään käytettävästä muuttujasta? @-operaattori ohittaa virheen huoletta kun taas oletusarvoista taulukkoa käytettäessä tulkki osaa tiedottaa tilanteesta. $_GET
-taulukon ollessa kyseessä toki harvinaista, mutta särkinäisen PHP:n asennuksen tai eksentrisen sovelluksen tapauksessa mahdollista. Ongelmana tuossa voi siis olla debuggaus: "Osoiterivillä kyllä lukee news.php?id=2, mutta silti arvoa ei vain löydy... <monta tuntia myöhemmin> jaha, käyttämäni lisäpalikka on kutsunut unset($_GET)
..."
Toisena huomiona mainittakoon manuaalin kommenteissa mainittu @-operaattorin harjoittama virhekäsittelijän kutsuminen error_levelin
arvolla 0. Jos sovelluksessa on määritelty esimerkin #1 kaltainen virheenkäsittelijä niin sovellus tulostaa virhetilanteessa @-operaattoria käytettäessäkin virheviestin.
Hyviä pointteja. Itselläni on tapana määritellä vähän isompiin PHP-sovelluksiin aina virheenkäsittelijä, joka muuttaa virheet poikkeuksiksi. Käsittääkseni tilanteen voisi siis vielä korjata tarkistamalla käsittelijässä error_levelin nolluus, mutta, kuten sanottu, hyi.
Tämä toteutus ei valitettavasti auta silloin, kun data sisältää lisää taulukoita. Yksi ratkaisu voisi olla, että offsetGet muuttaisi palautettavat taulukot automaattisesti edelleen DefaultingArrayObject-olioiksi.
Tiedän että on jo ikivanha ja muutenkin aika hulla homma, mutta:
class Request { private $params; public function __construct(array $array) { $this->params = $array; } public function __get($index) { return (array_key_exists($index, $this->params)) ? $this->params[$index] : null; } }