bugie.cz

Sniperův blog o věcech webových i newebových

10 května

__toString a vyjímky - smrtelná kombinace

V současnosti implementuju komunikaci s ARESem (přístup do registru ekonomických subjektů) a chtěl jsem si trochu ulehčit psaní. Mám objekt dotazu, který na základě nastavených parametrů sestaví buď URL s parametry pro případ dotazu pomocí GET nebo XML dokument pro případ POST dotazu. Jelikož dotaz sám ví, jestli bude pomocí GET nebo POST, rozhodl jsem se generování dotazu implementovat jako magickou metodu __toString(). A zle jsem narazil.

Abych byl přesný - celkem pro generování mám tři metody:

  • toUrl() - pro požadavky GET
  • toXml() - pro požadavky POST
  • __toString() - rozhoduje se podle nastavení objektu

__toString() funguje čistě jako proxy metoda, která na základě nastavení objektu zavolá toUrl() nebo toXml() a vratí její výsledek. Jenomže problém! Metody toUrl() a toXml() používají pro hlášení chyb vyjímky. Ale jakmile se zavolá metoda __toString() a dojde k chybě a vyjímka probublá do __toString(), php vyhodí fatal error:

Fatal error: Method Ss_Ares_Dotaz::__toString() must not throw an exception in /home/sniper/php-projekty/arestest.php on line 30

Cili __toString() nesmí vyhazovat vyjímky. Důvod tohoto opatření mi není zcela jasný a podle mne by ho v dohledné době měli autoři PHP změnit. Ale v součanosti to tak platí a musíme se s tím nějak vypořádat. A řešení je relativně jednoduché - vyjímku vyhodit nemůžeme, ale můžeme vyhodit běžnou chybu. Tedy místo jednoduchého:

public function __toString()
{
    switch ($this->_method) {
        case self::METHOD_GET:
            return $this->toUrl();
        break;
        case self::METHOD_POST:
            return $this->toXml();
        break;
    }
}

je nutno použít takovou trochu kostrbatější konstrukci, na kterou se přeci jenom trochu hůře reaguje než na vyjímku:

public function __toString()
{
    try {
        switch ($this->_method) {
            case self::METHOD_GET:
                return $this->toUrl();
            break;
            case self::METHOD_POST:
                return $this->toXml();
            break;
        }
    } catch (Exception $e) {
        trigger_error($e->getMessage(), E_USER_ERROR);
    }
}

Sdílet
  1. v6ak říká:
    Snad žádný javař zvyklý na řízené výjimky se tomu nediví :-D Toto opatření prostě umožňuje použít __toString() s jistotou, že se to povede. OT: Ta hláška o nedefinovaném indexu HTTPS (dole) nevypadá dobře.
  2. Sniper (admin) říká:
    ad OT: Hops, dik za upozorneni....budiz mi omluvou ze nepochazela z mojeho kodu ale z includu backlinks :)
  3. Ales Hakl říká:
    Ono to vcelku dava smysl, metody typu __toString() jsou obvykle urcene k tomu, aby se objekt nejak inteligentne vypsal uzivateli (tohle pouziti je z tohohle pohledu tedy ponekud netypicke). Typicke misto kde se nejaky objekt vypisuje nejak uzivateli je nejaky debugger / hlaska "Doslo k neocekavane vyjimce" a pokud by __toString() vyhodil vyjimku, tak dojde k zacykleni. Resit se to da ruzne, checked exceptions ala Java uplne reseni neni, protoze Object#toString klidne muze vyhodit nejakeho potomka RuntimeException, asi nejjednodussi je to zacykleni detekovat a proste spadnout, coz ovsem v pripade PHP neni uplne dobra cesta. OT: a z tohohle jsem teda jelen: * 'http://hakl.net/' appears to be a DNS hostname but cannot match TLD against known list * 'http://hakl.net/' does not appear to be a valid local network name
  4. Sniper (admin) říká:
    Ad chyba ve www: To je způsobený použitým validátorem. Celý formulář se generuje pomocí Zend_Formu a použil jsem tam validator hostname, který si bohužel neporadí s url, která začíná http://. O víkendu mam v planu další update, který by tohle měl odstranit