php.de

Zurück   php.de > php.de Intern > Wiki Diskussionsforum > Tutorials

Tutorials Hier findest Du Tutorials, welche nach und nach ein fertiges Script ergeben. Sehen, lernen & verstehen!

Antwort
 
LinkBack (1) Themen-Optionen Thema bewerten
Alt 26.04.2008, 10:36  
Erfahrener Benutzer
 
Registriert seit: 21.05.2008
Beiträge: 9.937
Zergling-new wird schon bald berühmt werden
Standard PHP: Exceptions - Teil 2

Hallo,
ich moechte euch heute mal mein Exception-Handling vorstellen, das meiner Meinung nach sehr flexibel und maechtig geworden ist und sich schon in einigen Projekten bewaehrt hat.

Dazu verwende ich zunaechst eine ApplicationException die direkt von der PHP Exception-Klasse ableitet und eine oeffentliche Execute() Methode zur Verfuegung stellt.

Anwendungsbeispiel fuer das Laden einer Kategorie aus der Datenbank in einem Administrations-System:
PHP-Code:
<?php
try {
  require_once 
dirname(__FILE__) . "/../global.inc.php";
  
$objAdmin Admin::Autoload(true); // true = check logged in
  
$intCategory Category::AutoloadId(); //  $_REQUEST["category_id"]
  
$objCategory Category::Load($intCategory); // aus Datenbank laden
  
if (!$objCategory instanceof Category) {
    throw new 
CategoryException(CategoryException::IdNotFound$intCategoryId);
  }
  
$objTpl = new Template();
  
$objTpl->Admin $objAdmin;
  
$objTpl->SetContext($objCategory);
  
$objTpl->Render();
} catch (
ApplicationException $objExc) {
  
$objExc->Execute()
}
?>
Diese Anwendung koennte mindestens zwei offensichtliche Exceptions werfen, die erste durch Admin::Autoload(true) die wie im Kommentar beschrieben den aktuellen Session-Benutzer fuer den Administrationszugang prueft oder aber die CategoryException.

Die AdminException koennte so aussehen:
PHP-Code:
<?php
class AdminException extends ApplicationException {
  const 
UsernameNotFound 1;
  const 
PasswordWrong 2;
  const 
SessionEmpty 3;
  
  protected 
$arrMessages = array(
    
self::UsernameNotFound => "Der Benutzer (%s) wurde nicht gefunden.",
    
self::PasswordWrong => "Das Passwort fuer den Benutzer %s ist falsch.",
    
self::SessionEmpty => "Bitte loggen Sie sich ein.",
  );
  public function 
__construct($intCode) {
    if (
func_num_args() > 1) { // mehr Argumente als nur $intCode?
      
$arrArguments func_get_args();
      
array_shift($arrArguments); // $intCode aus dem Ergebnis nehmen
      
$strMessage vsprintf(gettext($this->arrMessages[$intCode]), $arrArguments);
    } else {
      
$strMessage NULL;
    }
    
parent::__construct($strMessage$intCode);
  }
  public function 
Execute() {
    
// Weiterleitung zur Login-Seite
    
$this->Redirect();
  }
  protected function 
Redirect() {
    
header("Location: " HTTP_HOME_ADMIN "/login.php");
  }
}
?>
PHP-Code:
<?php
class ApplicationException extends Exception {
  protected static 
$objExcInExc;
  public function 
__construct($strMessage$intCode) {
    if (
self::$objExcInExc instanceof self) {
      
// Exception in Exception, z.B. wenn Rendering der Fehlerausgabe im Template scheitert
      // besser hart aussteigen und ersten Fehler (und nicht Folgefehler) anzeigen
      
self::$objExcInExc->HardExit();
    }
    
self::$objExcInExc $this;
    
$this->Log();
  }
  public function 
__toString() {
    return 
sprintf("Error - #%u: %s thrown"$this->GetCode(), get_class($this));
  }
  public function 
Execute() {
    
$this->CleanBuffer();
    
$this->Render(); // default, kann von erbenden Klassen ueberschrieben werden
  
}
  protected function 
Render() {
    
$objTpl = new Template();
    
$objTpl->SetName("error/exception");
    
$objTpl->SetContext($this);
    
$objTpl->Render();
  }
  protected function 
HardExit() {
    
$this->CleanBuffer();
    
header("HTTP/1.0 500 Internal Server Error");
    require_once 
PATH_TEMPLATES "/error/500.html";
    exit(
1);
  }
  protected function 
CleanBuffer() {
    @
ob_end_clean();
    
// wenn unsere "global.inc.php" aus unserem try-catch Block
    // ein ob_start() aufgerufen hat, koennen wir die moeglicherweise
    // bisherigen Ausgaben aus dem Buffer entfernen
  
}
  protected function 
RenderImage($intWidth 500$intHeight 100) {
    
header('Content-type: image/png');
    
$errImg ImageCreate($intWidth$intHeight);
    
$bgColor imagecolorallocate($errImg,0,0,0);
    
$fgColor1 imagecolorallocate($errImg,255,255,255);
    
$fgColor2 imagecolorallocate($errImg,255,0,0);
    
imagestring($errImg,3,6,6strip_tags($this), $fgColor2); // __toString
    
imagestring($errImg,3,6,21,$this->GetMessage(),$fgColor1);
    
imagepng($errImg);
    
imagedestroy($errImg);
    exit(
1);
  }
  protected function 
Log() {
    
// hier einfaches Logging ueber DB oder z.B. syslog() durchfuehren
  
}
}
?>
Wie man sieht ist die Fehlerbehandlung jetzt relativ flexibel:
- Wir tippen uns keinen Wolf bei Fehlermeldungen (nurnoch die Fehlerkonstante)
- Die Fehlermeldungen koennen in verschiedenen Sprachen ausgegeben werden (gettext),
- Trotzdem koennen Platzhalter verwendet werden
- Unsere Fehlermeldungen sind unabhaengig vom Ausgabemedium (HTML, Grafik, PDF denkbar, ..)
- Wir koennen unsere Fehlermeldungen an zentraler Stelle (Log() Methode) mitloggen und
- Wir vermeiden Exceptions in Exceptions, die nur Verwirrung stiften.
- Die Fehlerausgabe ist zentral gesteuert und kann je nach Produktions- oder Entwicklungsstatus unterschiedlich ausfallen und zu guter letzt:
- Unsere Fehlermeldungen haben das Look&Feel der Website, wenn sie ueber die Template-Klasse angesteuert werden.

Ich verwende dieses Exception-Handling auch fuer Assertions und PHP-Fehler. Dazu einfach mit assert_options() eine Callback-Funktion aufrufen, die ihrerseits wiederum eine z.B. PhpAssertException() wirft. Gleiches fuer PhpErrorException() mit set_error_handler().
Zergling-new ist offline   Mit Zitat antworten
Sponsor Mitteilung
PHP Code Flüsterer

Registriert seit: 21.08.2005
Beiträge: 4682
PHP-Kenntnisse:
Fortgeschritten

Alt 10.05.2008, 22:54  
Erfahrener Benutzer
 
Registriert seit: 16.07.2005
Beiträge: 1.007
PHP-Kenntnisse:
Fortgeschritten
brian johnson befindet sich auf einem aufstrebenden Ast
Standard

gut, du lässt exceptions in exceptions nicht zu, ich sehe aber auch keine __destruct() funktion die nach dem handling die exception zurücksetzt. das heißt, das du maximal eine exception in einem script haben kannst, was aber dem sinn von exception, der behandlung selbiger, entegen steht.

bsp, erzeugt HTTP 500 was aber kompletter blödsinn ist, denn die exceptions wurden gefangen.

PHP-Code:
try
{
    throw new 
ApplicationException('blaa',1);
}
catch(
ApplicationException $e)
{
    echo 
2;
}


try
{
    throw new 
ApplicationException('blaa',1);
}
catch(
ApplicationException $e)
{
    echo 
3;

zum 2., was ist hierbei? auch in der behandlung können exceptions auftreten...
(hier hilft __destruct() übrigens nur, wenn ich das exception object selbst zerstöre...)

PHP-Code:
try
{
    throw new 
AdminException(1);
}
catch(
ApplicationException $e)
{
    try
    {
        throw new 
AdminException(1);
    }
    catch(
ApplicationException $e)
    {
        throw new 
ApplicationException('finished!');
    }

erweiterned benutze ich kein array, sondern klassen konstanten für die fehlermeldungen. das hat den vorteil, das ich eine exception klasse habe die loggings durchführt, ich diese unendlich erweitern kann und zwar um fehlermeldungen:

PHP-Code:
class e
{
       public function 
__construct();
       public function 
log();
       public function 
__toString();
}

class 
e_argument extends e
{
         const 
IS_EMPTY=            'Argument %s is empty.';
         const 
EXPECTING_ARRAY 'Excpecting array, %s given.';

was ich sonst noch gerne mache, ist für eine session serialize zu benutzen um mir eine exception zu speichern. diese kann man dann hinterher mit unserialize wieder auslesen. die informationen dazu kann ich mir dadurch nicht (nur) auf der seite selber anschauen, sondern auch zusammengefasst und schön formatiert auf einer extra seite.

was sonst noch schön zu wissen ist, das eine exception eben auch nur logging funktionen haben kann. deswegen sieht mein error_handler einfach so aus:

PHP-Code:
public static function error($code,$string,$file,$line)
{
    new 
e_error_handler($string,$code,$file,$line);
    return 
FALSE;

zusammen mit obigem ist das ein nettes debugging feature.
__________________
PHP4?!?>>>Aktuelle PHP Version: 5.2.11 || 5.3.0
Suse 11.2 *vorfreude*
brian johnson ist offline   Mit Zitat antworten
Alt 11.05.2008, 02:16  
Erfahrener Benutzer
 
Registriert seit: 21.05.2008
Beiträge: 9.937
Zergling-new wird schon bald berühmt werden
Standard

Hallo,
mit dem ersten Punkt hast du vollkommen Recht - ich hab das "Feature" erst eingebaut, als ich mich um Exceptions kuemmern musste, die von Exceptions geworfen wurden. Beispielsweise wenn die Exception sich selbst HTML-rendern sollte, aber der Zugriff auf die Template-Klasse fehlgeschlagen ist. Die Idee mit dem Destructor ist sehr gut.

Zum Logging, ich wollte damals eindeutige Exception-Codes, die unabhaengig von den Parametern (%s) den selben Exception-Code erzeugen. Ich denke ich bin da mit der Zeit und den ganzen Anpassungen etwas am Ziel vorbeigerudert, ich sehe aktuell keinen Grund mehr, die Texte nicht in die Klassen-Konstanten zu schreiben, zumal ich mir die doppelte Nennung der Namen dann spare.

Danke fuer deine Hinweise!
Zergling-new ist offline   Mit Zitat antworten
Alt 11.05.2008, 12:19  
Erfahrener Benutzer
 
Registriert seit: 16.07.2005
Beiträge: 1.007
PHP-Kenntnisse:
Fortgeschritten
brian johnson befindet sich auf einem aufstrebenden Ast
Standard

Zitat:
Die Idee mit dem Destructor ist sehr gut.
denk nur dran, das du das object immer selbst zerstören musst.

Zitat:
Zum Logging, ich wollte damals eindeutige Exception-Codes, die unabhaengig von den Parametern (%s) den selben Exception-Code erzeugen.
ich habe immer 2 fehlercodes, einmal ohne ersetzungen (also mit %s), einmal mit ersetzungen.
__________________
PHP4?!?>>>Aktuelle PHP Version: 5.2.11 || 5.3.0
Suse 11.2 *vorfreude*
brian johnson ist offline   Mit Zitat antworten
Alt 11.05.2008, 18:30  
Erfahrener Benutzer
 
Registriert seit: 16.07.2005
Beiträge: 1.007
PHP-Kenntnisse:
Fortgeschritten
brian johnson befindet sich auf einem aufstrebenden Ast
Standard

hmm vllt. noch an den obigen beitrag anhängen:

Exceptions und Magische Funktionen

vermieden werden sollten exceptions in __construct, weil eine geworfene exception darin die initierung des objects beendet. im klartext heißt das, das das object nie existieren wird. das schlechte daran ist, das dies gerne später, wenn auf dieses object zugegriffen werden soll, mit "Fatal Error: Call to a member function xxx on a non-object" endet.

PHP-Code:
<?php
try
{
      
$pdo=new PDO('nada');
}
catch(
PDOException $pdo_exception)
{
      
}

$pdo->exec('SET CHARACATER SET utf8'); //fatal error, script ende...
?>
in __destruct() sind exceptions schlecht, wenn das jeweilige object erst beim PHP shutdown zerstört also nicht explizit vorher zerstört wird. ansonsten endet die seite gerne mit "Fatal error: Exception thrown without a stack frame in Unknown on line 0" und behindert nachfolgende shutdowns durch einen sofortigen abbruch von PHP hierdurch.

PHP-Code:
<?php
$x
=new x;
class 
x
{
         public function 
__destruct()
         {
                   throw new 
Exception('blaaa');
         }
}
?>
während Exceptions in __toString() ebenfalls zu einem Fatalen Error führen: "Fatal Error: __toString() must not throw an Exception".

PHP-Code:
<?php
echo new x;
class 
x
{
        public function 
b()
        {
                throw new 
Exception('blaa');
        }

        public function 
__toString()
        {
                return 
$this->b();
        }
}
?>
zu beachten sei KONTEXT bei __toString() sowie bei __destruct() beim PHP shutdown. das heißt, wie hier im obigen beispiel, das keine methode / funktion die von __toString() und __destruct() aufgerufen wird eine exception werfen darf.

__get(), __set(), __isset() sowie __unset() unterliegen diesen beschränkungen nicht.
__________________
PHP4?!?>>>Aktuelle PHP Version: 5.2.11 || 5.3.0
Suse 11.2 *vorfreude*
brian johnson ist offline   Mit Zitat antworten
Alt 15.03.2009, 11:00  
Neuer Benutzer
 
Registriert seit: 12.03.2009
Beiträge: 4
Anybody befindet sich auf einem aufstrebenden Ast
Standard

Mhm ganz ehrlich, ich finde die Logik nicht so richtig passend aufgebaut... eigentlich können die Methoden außerhalb garnichts davon wissen, dass du z.B. in der Exception "Execute" anbietest. Dafür wäre zumindest ein Interface passend.

Des Weiteren halte ich es nicht unbedingt für sinnvoll, die Exceptionklassen so böse zu zu "manipulieren", auch wenn es sicher möglich ist, hier zusätzliche Informationen zu hinterlegen.
Anybody ist offline   Mit Zitat antworten
Antwort


Themen-Optionen
Thema bewerten
Thema bewerten:

Forumregeln
Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are an
Gehe zu

LinkBacks (?)
LinkBack to this Thread: http://www.php.de/tutorials/45839-php-exceptions-teil-2-a.html
Erstellt von For Type Datum
mikk4's exception Bookmarks on Delicious This thread Refback 19.02.2009 17:21

Ähnliche Themen
Thema Autor Forum Antworten Letzter Beitrag
JavaScript/PHP: qooxdoo 0.7.1 - Teil 1 : Eine Einführung Zergling-new Tutorials 6 25.01.2008 10:41
PHP: Exceptions - Teil 1 Zergling-new Tutorials 4 05.12.2007 23:31
PHP-Errors zu exceptions brian johnson PHP-Fortgeschrittene 6 06.11.2007 12:45
Teil einer Variablen mit einer Variablen ersetzen ? simsalabim PHP Tipps 2007 11 20.03.2007 20:36
Suche Coder für kleinen Teil einer Datenbankoberfläche DiplWI_BA Trash 0 01.05.2006 10:10
include, nur bestimmten teil neu laden Calli PHP Tipps 2006 2 27.01.2006 15:57
erbende Exceptions mit PHP 5.1.1 nicht mehr möglich? HStev PHP-Fortgeschrittene 4 27.01.2006 14:32
den buchstaben teil ermitteln Dilandau PHP Tipps 2006 2 25.01.2006 16:49
PHP-GTK Tutorial Beitragsarchiv 9 02.11.2005 21:07
nur bestimmten teil von php anzeigen se-clan PHP Tipps 2007 6 31.10.2005 20:52
[Erledigt] Nur Teil von Zelle auslesen Datenbanken 7 16.09.2005 13:38
Immer nur der erste Teil der if-Abfrage? JonathanArcher PHP Tipps 2005-2 5 04.06.2005 14:28
teil eines strings ausschneiden janni PHP Tipps 2007 6 03.01.2005 14:21
Fehler im BETWEEN Teil? R4v3r Datenbanken 4 22.11.2004 19:42
makierten teil herausfinden Filewalker PHP Tipps 2004 5 18.10.2004 22:28

Besucher kamen über folgende Suchanfragen bei Google auf diese Seite
http://www.php.de/tutorials/45839-php-exceptions-teil-2-a.html, php exception class, php exception class extend, __tostring() must not throw an exception, throw new exception php, php exception.inc, php exception klasse, php exception code, php alle exception klasse, php class exception, __tostring() must not throw an exception in, php exception, php klasse exception, php exception erweitern, php exception ableiten, exception klasse php, exceptions mit php, php throw new exception, php exception methoden, qooxdoo konstanten oder static

Alle Zeitangaben in WEZ +1. Es ist jetzt 04:56 Uhr.




Powered by vBulletin® Version 3.7.2 (Deutsch)
Copyright ©2000 - 2012, Jelsoft Enterprises Ltd.
Search Engine Optimization by vBSEO 3.2.0
Aprilia-Forum, Aquaristik-Forum, Liebeskummer-Forum, Zierfisch-Forum, Geizkragen-Forum

Creative Commons License
Dieser Inhalt ist unter einer Creative Commons-Lizenz lizenziert.