ugrás a tartalomhoz

Kivételkezelés alkalmazása

chop · 2012. Jan. 27. (P), 00.57
Sziasztok!

Olvasgatok kivételkezelés témakörben és van pár fehér folt, ami csak nem akar kitisztulni a fejemben.

Adott ugye egy szituáció, amikor valami rosszul sülhet el, legyen mondjuk most ez egy szimpla fájlmegnyitás, majd beolvasás. Valamelyik sikertelen művelet esetünkben egy végzetes hiba, amikor emiatt le kell lőni az egész programot. Miért jobb az, ha try...catch-csel kapom el ezt az eseményt annál, minthogy egy feltételes szerkezettel vizsgálom, hogy sikerült-e a művelet vagy sem?

Ezt egy külföldi fórumon találtam és nagyjából leírja, hogy mi a problémám.

class FileOutputter
{
 public function outputFile($file)
 {
  if (!file_exists($file))
   return throw new Exception("File not found.",123);
  try
  {
   $contents = file_get_contents($file);
  }
  catch (Exception $e)
  {
   return $e;
  }
  return $contents;
 }
}
Ha nincs file, akkor dob egy kivételt, eddig rendben is van. Utána viszont ugyanúgy megtehetné ugyanezt, de ehelyett try...catch-csel kezeli le, ha nem sikerül a beolvasás. Mi a különbség (a hasznosságát illetően) és miért jobb try...catch-elni annak ellenére, hogy sokkal jobban eszi az erőforrást, mint egy sima feltételvizsgálat?

Magyarul az nem tiszta, hogy miért jó nekem, hogy try...catch-csel fogom el a kivételeket, azonkívül, hogy ez utóbbi nem feltétlenül okoz fatal errort a programban?

(Ha hülyeségeket beszélek, akkor elnézést...:) )
 
1

Hajjajj

MadBence · 2012. Jan. 27. (P), 02.00
Ez itt több sebből vérzik.
return throw new Exception(... ilyet se láttam még...
Aztán a file_get_contents nem dob kivételt (illetve valahogy rá lehet venni a PHP-t, hogy a saját hibakezelője helyett inkább exceptionokat dobáljon), szóval a blokknak alapból nincs értelme. Aztán Exception-t nem ad vissza függvény (nem mintha bármi baj lenne vele szintaktikailag, de így értelmét veszti a kivételkezelés)

A kivételdobálgatás azért jó dolog, mert nem kell adott esetben hibakódokat adogatni akár több szinten keresztül. Ebből következik, hogy helyben egyáltalán nem kötelező elkapni őket (akár a fél stacken is átugorhat, magyarán ott kell csak foglalkoznod a hibával, ahol logikailag kell). Másrészt sokkal beszédesebb is, mint egy hibakód (még ha el is van fedve valami FILE_NOT_EXISTS fantázianevű konstanssal). Egyszóval használjuk őket, mert hasznosak. Bár tényleg több erőforrást eszik, mint integereket egymásnak adogatni, nem valószínű, hogy ezen múlna az alkalmazás sebessége, maga a fájl beolvasása nagyságrendekkel lassabb lehet.
2

Érdekes... :)

H.Z. v2 · 2012. Jan. 27. (P), 09.22
Ettől a "return throw"-tól nekem is égnek állt a hajam...
php > function y(){
php { return throw new Exception("xx"); }
PHP Parse error:  syntax error, unexpected T_THROW in php shell code on line 2


Kipróbáltam, mert mit lehet tudni... :-)
Szóval már szintaktikailag sem stimmel, de ha működne is, a kivételkezelés értelmét nem vesztené el: ha a return mögé egy függvényhívást teszel, ami Exception-t vált ki, akkor simán végigmegy a kivételkezelő mechanizmuson.
Egy gyanúm van: aki az eredeti kódot írta, csak véletlenül tette a throw-t a return mögé, ő egy Exception objektumot akart volna visszaadni. Ebből viszont az feltételezném, hogy a szerzője nem volt tisztában a kivételkezelés működésével.
Másik variáció, hogy a return a sajtóhiba és tudta, mit csinál (bár a további sorok ezt számomra kétségessé teszik, de nem kizárt, hogy én vagyok tudatlan)
5

Biztosan nem olyasvalakinek a

tgr · 2012. Jan. 28. (Szo), 13.29
Biztosan nem olyasvalakinek a kódjából tanulnék kivételkezelést, aki egy file input függvényt outputFile-nak nevez el.
6

Link

chop · 2012. Jan. 28. (Szo), 13.48
Itt olvastam. Pontosan, nem volt tisztában a működéssel, ezért írt különböző variánsokat ugyanarra a problémára.
8

stackoverflow

H.Z. v2 · 2012. Jan. 28. (Szo), 14.53
Egy tipp a használatához: a kérdésekből ne akarj tanulni, jobb ha a válaszokból próbálsz tájékozódni! ;)
Azok közül is inkább a több pontot kapott változatok lehetnek érdekesek.
3

Csináltam egy gyors példát,

Kubi · 2012. Jan. 27. (P), 12.45
Csináltam egy gyors példát, hogy érthetőbb legyen, mire is jó a kivételkezelés.
Nem teszteltem, lehet benne elírás...

Az egyik lényegét, amit már előttem is írtak, hogy a kivétel dobásától eltérő helyen tudod elkapni a hibaüzenetet. Az alábbiban, ha a fájl nem található, vagy nem olvasható az Framework class azt elkapja (a page renderelésénél lehetne bármi más hiba, db olvasás error vagy akármi) és a hibát formázva megjeleníti. Ez felhasználói élmény szempontjából jó, development környezetben az a jó ha megjelenik a hiba szövege, production környezetben már csak az a jó, ha egy dizájnos felület jelenik meg ahol értesítjük a usert, elnézést, hiba volt. Dolgozunk rajta.

Sokat lehetne még írni a kivételkezelésről, pl. nem csak hibák esetén érdemes exceptiont dobni, hanem pl egy form mezőinek validálása során exception-ökkel összegyüjthetőek a hibaüzenetek (bővebben: symfony 1.4 form validálás, validatorSchema).

Másik lényeges pont, lehet saját exception classt létrehozni, és a catch résznél megadni, hogy csak ezt a classt kapja el, így megoldva, hogy csak bizonyos kivételeket kezeljen le az adott programrész, másokat hagyjon tovább emelkedni vagy hogy mondjam, az action stack-ben.

class Framework
{
  protected $env;

  public function __construct($env)
  {
    $this->env = $env;
  }

  public function page1()
  {
    $reader = new FileReader('oldal.txt');
    return $reader->getFileContent();
  }

  public function renderer($page)
  {
     $content = '';
     try
     {
       switch($page)
       {
         case '1':
           $content = $this->page1();
         break;
         default:
           $content = $this->get404();
       }
     }
     catch(Exception $e)
     {
       $content = $this->getError($e);
     }

     return $content;
  }

  public function get404()
  {
     return "az oldal nem található";
  }

  public function getError(Exception $e)
  {
     if($env = 'prod')
     {
       return 'Hiba történt, elnézést a kellemetlenségekért, dolgozunk rajta';
     }
     else
     {
       return "Hiba történt: ".$e->getMessage();
     }     
  }
}

class FileReader
{
  protected $file;

  public function __construct($file)
  { 
    $this->file = $file;
  }

  public function getFileContent()
  {
     // file_exist nem jó, mert könyvtárra is true!!
     if(!is_file($this->file))
     {
        throw new Exception('nem találom a fájlt');
     }
     
     // @ használata nem szép megoldás, de egyébként
     // error_handler-el kellene szórakázni, most kihagynám
     $content = @file_get_contents($thsi->file);
     if($content === false)
     {
        throw new Exception('nem tudom olvasni a fájlt');
     }

     return $content;
  }
}


// index.php
$fw = new Framework();
echo $fw->page(1);

4

Az Exception subclassok

tgr · 2012. Jan. 28. (Szo), 13.27
Az Exception subclassok használata azért is jó, mert így különféle hibákhoz különböző megjelenítést tudsz írni (pl. egy DB hibánál más debug információkat akarunk mutatni, mint egy syntax errornál) anélkül, hogy katyvaszt csinálnál a hibakezelő kódból. Plusz ha eszedbe jut, hogy valami extra információt akarsz tudni a hibáról (mondjuk egy stack trace-t), akkor az exception objektumba triviális felvenni +1 elemet, return-lánccal megoldott hibakezelésnél meg vért izzadsz, mire mindenhol átírod (megváltozik a függvényed szignatúrája, minden létező helyen, ahol meghívtad, le kell kezelni a visszatérési értéket stb).

Én mondjuk azt se hinném el bemondásra, hogy a kivételkezelést használó kód számottevően lassabb lesz.
7

Köszi, kipróbálom.

chop · 2012. Jan. 28. (Szo), 14.02
Köszi, kipróbálom.
9

:D most nézem, utsó sort

Kubi · 2012. Jan. 28. (Szo), 19.49
:D most nézem, utsó sort elbasz..tam
$fw->renderer(1)