molily: Programmstruktur bei aufeinander aufbauenden Abläufen

Hallo,

ich stelle mir gerade eine grundsätzliche Frage. Man hat ziemlich oft solche Programmabläufe, insbesondere bei Datenbankabfragen (selbst bei OO-Interfaces wie mysqli):

$ergebnis1 = mach_was();  
if ($ergebnis1 === false) {  
   echo(\'Fehler!\');  
   return false;  
}  
$ergebnis2 = fahre_fort1($ergebnis1);  
if ($ergebnis === false) {  
   echo(\'Fehler!\');  
   return false;  
}  
$ergebnis3 = fahre_fort2($ergebnis2);

usw.

Sprich, Code baut aufeinander auf, aber an jeder Stelle kann irgendeine Funktion eine Ausnahme zurückgeben, die einer Sonderbehandlung bedarf. Dann soll der restliche Code nicht ausgeführt werden, sondern dieser Teil des Programmes vorzeitig abgebrochen werden - ich habe dies oben symbolisch mit return false angedeutet.

Ärger wird es dann bei verschachtelten Fallunterscheidungen, d.h. wenn man nicht einfach nur abbrechen will, sondern einen anderen Weg einschlagen will:

$ergebnis1 = mach_was();  
if ($ergebnis1 == \'a\') {  
   $ergebnis2 = mache_dies($ergebnis1);  
   if ($ergebnis2 == ...) {  
      ...  
   } else {  
      ...  
   }  
} elseif ($ergebnis1 == \'b\') {  
   $ergebnis2 = mache_das($ergebnis1);  
   if ($ergebnis2 == ...) {  
      ...  
   } else {  
      ...  
   }  
}

Solcher Code wird, selbst wenn man die Teile in mehrfach verwendbare Funktionen auslagert, schnell unübersichtlich und schwer zu übersehen.

In JavaScript würde ich so etwas weniger »imperativ« lösen, d.h. vielleicht mit Handlerfunktionen arbeiten, die sich dann in einer Kette aufrufen. Beim ersten Beispiel also etwa:

mach_was(fahre_fort1, fehler1);  
function mach_was (erfolg, fehler) {  
   var ergebnis = [mach_es];  
   ergebnis ? erfolg(ergebnis, fahre_fort2, fehler2) : fehler(ergebnis);  
}  
function fahre_fort1 (ergebnis_alt, erfolg, fehler) {  
   var ergebnis = [mach_es];  
   ergebnis ? erfolg(ergebnis) : fehler(ergebnis);  
}  
function fahre_fort2 () {...}  
function fehler1 () {...}  
function fehler2 () {...}

Der Sinn hier ist natürlich, dass man die Funktionen so allgemein hält, dass sie unterschiedliche Handlerfunktionen empfangen können, also in verschiedenen Abläufen/Ketten benutzt werden.

Ich finde das zumindest sinnvoller strukturiert als den obigen »Spaghetticode«. Aber wie löst man eine solche Handlerstruktur in PHP? Funktionen an andere übergeben und damit eine Kette anstoßen geht wohl nicht so einfach - auf Funktionsnamen als Strings will ich verzichten.

Bisher habe ich auf solche grundlegenden Fragen der Programmstrukturierung keine eindeutige Antwort gefunden. Bei OOP lagere ich die Funktionalität letztlich zwar in viele kleine Funktionen aus, die sich dann aufrufen, aber irgendwo habe ich zentral noch einen Ablauf, der aus einer Reihe von Fallunterscheidungen und Fehlerbehandlungen besteht. Wie lässt sich das sinnvoller lösen?

Mathias

  1. hi,

    Funktionen an andere übergeben und damit eine Kette anstoßen geht wohl nicht so einfach - auf Funktionsnamen als Strings will ich verzichten.

    Beziehst du dich da jetzt lediglich auf das in der Tat unschöne Konzept der "Variablenfunktionen" - oder auch auf call_user_func/call_user_func_array, sprich erscheint dir die Nutzung letzterer auch zu "unsauber" und stellt daher keine Option dar?

    gruß,
    wahsaga

    --
    /voodoo.css:
    #GeorgeWBush { position:absolute; bottom:-6ft; }
    1. Hallo,

      Beziehst du dich da jetzt lediglich auf das in der Tat unschöne Konzept der "Variablenfunktionen" - oder auch auf call_user_func/call_user_func_array, sprich erscheint dir die Nutzung letzterer auch zu "unsauber" und stellt daher keine Option dar?

      Beide dieser Vorgehensweisen funktionieren im Grunde wohl gleich, sie sind anscheinend austauschbar, zumindest in diesem Normalfall:
      $funktionsname = 'func'; $funktionsname();
      $funktionsname = 'func'; call_user_func(funktionsname);
      Bei beiden werden die Funktionsnamen als Strings »hartkodiert«. Gut, ob ich wie in JavaScript nun func(handler) oder in PHP func('handler') schreibe, gibt sich wohl wenig. In JavaScript wird aber alles naturgemäß über Events und Handler gesteuert, daher ist diese Praxis normal. In PHP scheint mir das eher verpönt und und das Übergeben einer nicht existierenden Funktion ergibt erst einen Fehler, wenn man sie aufrufen will (es sei denn, man baut darum noch einen Wrapper mit function_exists()). Außerdem sind in PHP gar nicht die ganzen spaßigen Dinge möglich, die durch Funktionsobjekte in JavaScript möglich sind: Ich kann nicht mal eben eine Methode eines Objektes als Handler angeben. Deshalb scheint es mir wenig erfolgsversprechend, meinen JavaScript-Programmierstil auf PHP zu übertragen... ;) In PHP bin ich solchen Fragen bisher immer durch ziemlichen Spaghetticode ausgewichen.

      Mathias

      1. hi,

        Beide dieser Vorgehensweisen funktionieren im Grunde wohl gleich, sie sind anscheinend austauschbar, zumindest in diesem Normalfall:
        $funktionsname = 'func'; $funktionsname();
        $funktionsname = 'func'; call_user_func(funktionsname);

        Ja, das stimmt wohl.

        Bei beiden werden die Funktionsnamen als Strings »hartkodiert«.

        Ja, aber so kann man sich doch zumindest im groben eine Ablaufsteuerung zusammenbasteln, in dem man die Namen der aufzurufenden Funktionen und Parameter in Datenstrukturen ablegt, vielleicht da auch noch definiert, wie auf das Ergebnis der Funktionen reagiert werden soll, etc. ... So "schön" wie in Javascript ist das natürlich nicht - in PHP würdest du damit eher noch eine weitere Meta-Ebene einziehen, auf der dann Logik liegt (und die dort vielleicht nicht so unbedingt hinpassen will).

        Gut, ob ich wie in JavaScript nun func(handler) oder in PHP func('handler') schreibe, gibt sich wohl wenig.

        Vom Schema her sicherlich vergleichbar.

        In JavaScript wird aber alles naturgemäß über Events und Handler gesteuert, daher ist diese Praxis normal. In PHP scheint mir das eher verpönt

        Die Ausrichtung ist ja auch eine andere - PHP ist nicht auf Events ausgerichtet, würde in dem Umfeld vielleicht auch weniger Sinn ergeben.
        PHP macht "sein Ding", und liefert dann ein Ergebnis zurück. Im Normalfall hast du eine Anfrage vom Client, und PHP antwortet darauf - da hat man vermutlich weniger eine Notwendigkeit gesehen, auf irgendeine Art und Weise "Events" zu definieren, bzw. der Event _ist_ für PHP die Anfrage vom Client.

        und das Übergeben einer nicht existierenden Funktion ergibt erst einen Fehler, wenn man sie aufrufen will (es sei denn, man baut darum noch einen Wrapper mit function_exists()).

        Gut, das ist natürlich sprachspezifisch - weil PHP Funktionen/Methoden nicht als Referenzen handhabt wie Javascript, besteht da auch nicht die Möglichkeit, schon vor dem Aufruf auf Interpreter-Ebene darauf zu reagieren.

        Außerdem sind in PHP gar nicht die ganzen spaßigen Dinge möglich, die durch Funktionsobjekte in JavaScript möglich sind: Ich kann nicht mal eben eine Methode eines Objektes als Handler angeben. Deshalb scheint es mir wenig erfolgsversprechend, meinen JavaScript-Programmierstil auf PHP zu übertragen... ;) In PHP bin ich solchen Fragen bisher immer durch ziemlichen Spaghetticode ausgewichen.

        OOP ist unter PHP ja sowieso noch nicht so umfänglich implementiert - Javascript ist ja schon sehr früh daraufhin konzipiert worden, während man PHP das erst später "aufgepropft" hat.
        In so fern darfst du wohl gar nicht erst mit der Erwartung herangehen, deinen gewohnten Javascript-Stil darauf übertragen zu können - weil PHP da (noch?) gar nicht alles leisten kann, was du von Javascript her kennst und dir vielleicht wünschst.

        gruß,
        wahsaga

        --
        /voodoo.css:
        #GeorgeWBush { position:absolute; bottom:-6ft; }
      2. echo $begrüßung;

        In JavaScript wird aber alles naturgemäß über Events und Handler gesteuert, daher ist diese Praxis normal. In PHP scheint mir das eher verpönt

        Kein Wunder. Unterschiedliche Anwendungsfälle verlangen unterschiedliche Konzepte. Ein PHP-Script ist ein großes Ganzes. Javascript wird meist zu vielen kleinen Teilen auf eine Webseite verstreut an verschiedene Dinge gebunden, die wahllos vom Besucher ausgelöst werden können. Damit hast du viele Events im Gegensatz zu dem einen in PHP.

        Außerdem sind in PHP gar nicht die ganzen spaßigen Dinge möglich, die durch Funktionsobjekte in JavaScript möglich sind: Ich kann nicht mal eben eine Methode eines Objektes als Handler angeben.

        Doch, das geht. Die Dokumentation gibt an den Stellen, an denen eine Funktion erwartet wird, nicht den Typ "string" für einen Funktionsnamen an, sondern den Pseudo-Typ callback. Damit kann man auch Klassenmethoden und Instanzmethoden übergeben.

        Klassenmethoden: array('Klassenname', 'Methodenname')
          Instanzmethoden: array(&$instanz, 'Methodenname')  (für PHP5 ohne das & für die Referenz)

        Deshalb scheint es mir wenig erfolgsversprechend, meinen JavaScript-Programmierstil auf PHP zu übertragen... ;)

        Unterschiedliche Anwendungsfälle verlangen unterschiedliche Konzepte. Aber das hatten wir ja schon. :-)

        echo "$verabschiedung $name";

  2. Hallo,

    mal konkret: http://molily.de/temp/weblog-recode-pingbacks.phps

    Das Script geht ein paar Datenbankzeilen durch, sucht einige Fehler, korrigiert diese und ändert die Zeilen entsprechend.

    Die Aufgabe des Scripts ist eigentlich supereinfach und man könnte das ganze sicher in ein paar Zeilen 08/15-Spaghetticode runterschreiben, das würde denselben Effekt haben. Ich habe die eigentlich simple Aufgabe versucht umzusetzen, als wollte ich wartbaren, ausbaubaren und recyclebaren Code schreiben, mit dem »Ereignis«- und Ausnahmebehandlung einfach, zentral und flexibel möglich ist. (Ich bin kein PHP-Experte, man kann sicher im Detail Verbesserungen und Ergänzungen vornehmen - es geht mir nur um die grobe Organisation. Ich sage nicht, dass der Code diesen Ansprüchen schon entspricht, es ist nur ein Versuch.)

    Nun gibt es (abgesehen von PDO usw.) standardmäßig nur ein nicht-OOP-Interface für PostgreSQL, also gibts eine kleine Wrapper-Klasse. Dann gibts einen Haufen leider loser Funktionen, die als Handler dienen. Letztlich eine spezielle abgeleitete Klasse, die eine Verbindung zur konkreten Datenbank erstellt und die die konkreten Manipulierungsfunktionen enthält.

    Die Handlerfunktionen für Erfolgs- und Fehlerfall liegen nun leider abseits der Klassen - man kann halt nur $this->$handlername() oder $handlername() machen bzw. if (function_exists($handlername)) $handlername; else $this->$handlername();. Das wäre in JavaScript kein Problem. Nutzbar für Fallunterscheidungen und alternative Programmabläufe sind die Handler dadurch nicht wirklich.

    Wie würdet ihr so ein Programm strukturieren, also in Klassen, Methoden usw. aufsplitten? (Angenommen, es soll nicht quick'n'dirty sein, siehe oben.) Bringt dieses »Handler«-Konzept überhaupt was?

    Mathias

    1. echo $begrüßung;

      mal konkret: http://molily.de/temp/weblog-recode-pingbacks.phps
      Wie würdet ihr so ein Programm strukturieren, also in Klassen, Methoden usw. aufsplitten?

      Kommt drauf an. Wenn es ein einmaliges Script ist, das irgendwo als einziges in etwas bestehendes eingebunden werden soll, dann nähme ich sicher den Weg des geringstmöglichen Aufwands. Wenn es sich lohnt kommen vorgefertigte Komponenten zum Einsatz, wenn nicht, wird nur mit Funktionen und vielleicht auch Klassen strukturiert.

      Wenn es eine komplexe Anwendung werden soll, muss mehr Aufwand und Strukturierung her.

      Bringt dieses »Handler«-Konzept überhaupt was?

      Ich denke, es gibt übersichtlichere Mittel.

      Ich kommentiere mal einige Teile, sowohl zu deinem Konzept als auch allgemeine Dinge.

      /* ####################################################################################### */
      /* *********************************************************************************** */

      Interessante Kommentare, leider sind es die einzigen ...

      class DB {
          private $connection;
          private $result;

      Eine Datenbankklasse erledigt zwar im Allgemeinen alles über eine einzelne Verbindung, jedoch ist es nicht immer so, dass das Ergebnis vollständig ausgewertet ist, bevor eine neue Abfrage gestartet wird. Es in einer Instanzvariable zu halten, halte ich nicht für sinnvoll. Sinnvoller, finde ich, sollte es eine eigene Klasse bilden, oder das komplette Ergebnis sollte per Fetch-Methoden - wie fetchall, fetchone, fetchcol, fetchrow, usw., die das Ergebnis aus einem komplett übergebenen SQL-Statement bilden - an die aufrufende Instanz weitergegeben werden.

      function __construct (...) {
              return irgendwas;
          }

      Ein Konstruktor gibt unter PHP5 nur eine Instanz seiner Klasse zurück, nichts anders. Mit return etwas anderes zurückzugeben funktioniert nicht. Das war nur unter PHP4 möglich (möglicherweise auch noch in frühen 5er Versionen). Wenn du unterschiedliche Ergebnisse haben möchtest, verwende das Factory-Pattern.

      Im Constructor: $this->connect(...);

      Nicht immer ist es sinnvoll, sofort nach Instanziieren der Klasse eine Verbindung aufzubauen, und diese dann erst eine Weile rumliegen zu lassen, bevor eine der Methoden überhaupt eine Verbindung benötigt. Vielleicht braucht es auch gar keine Verbindung, weil wegen eines Fehlers im Vorfeld das DB-Handling abgebrochen wird, dann hat man umsonst eine Ressource belegt. Vorschlag: Jede Methode, die eine Verbindung benötigt, holt sie sich bei einer getConnection-Methode, welche ihrerseits ein geöffnetes DB-Handle zurückgibt und es ggf. vorher initialisiert.

      while ($rowObject = pg_fetch_object($this->result))
          $objectArray[] = $rowObject;
        foreach ($objectArray as $rowObject) ...

      Variablen müssen zwar unter PHP nicht initialisiert werden, es ist aber dringend empfehlenswert, das zu tun. Besonders wenn man nur bedingt (if, while, ...) schreibend darauf zugreift, kann es passieren, dass sie nicht existieren. In dem Konstrukt wirft dir das foreach eine Fehlermeldung um die Ohren, wenn die Query eine leere Ergebnismenge zurückliefert und $objektArray keins ist.

      (Außerdem ist $rowObject keins, wenn es nichts mehr zu fetchen gibt, sondern false. Das Benennen der Variablen nach ihrem angeblichen Typ halte ich nicht für sonderlich sinnvoll.)

      $dataArray[] = '"' . pg_escape_string($fieldName) . '" = '' . pg_escape_string($value) . ''';

      PHP kennt eine Funktion namens sprintf, mit der man Strings etwas übersichtlicher zusammenstellen kann: $dataArray[] = sprintf('"%s" = '%s'', pg_escape_string($fieldName), pg_escape_string($value));

      $updateClause = implode(', ', $dataArray);

      Auch hier ist $dataArray nicht unbedingt eins, weil Schreibzugriffe nur im Körper einer foreach-Schleife stattfanden, der bei leerem Array nicht ausgeführt wird. Und gleich im Anschluss mit $whereArray das gleiche.

      Deine Version:

      public function analyzePingbacks () {
              $this->query(
                  'SELECT "id", "value" FROM "block_attributes" WHERE "name" = 'pingback'',
                  'handleSelect',
                  'handleSelectError'
              );
          }

      Mein Vorschlag:

      public function analyzePingbacks () {
              try {
                  $result = $this->query(...);
                  mach;
                  was;
                  mit;
                  result;
                  oder
                  $this->handleResult($result);
              } catch(Exception $ex) {
                  mach;
                  was;
                  bei;
                  fehler;
                  oder
                  $this->handleError($ex);
              } finally {
                  ggf. aufräumarbeiten;
              }
          }

      Handler haben ihre Vorzüge im ereignisgesteuerten Umfeld. Da weiß man nie, was als nächstes passiert. Mal wird dieser Handler aufgerufen, mal jener. Sie für ein ereignisloses System nachzubilden halte ich nicht für sehr sinnvoll. Hier hat man den Programmfluss selbst in der Hand. Was aber nicht heißt, dass man nicht sinnvollerweise Teilaufgaben an Funktionen und Methoden deligieren kann.

      echo "$verabschiedung $name";

      1. echo $begrüßung;

        Ich kommentiere mal einige Teile, sowohl zu deinem Konzept als auch allgemeine Dinge.

        Noch eine Kleinigkeit:

        echo("Update\n" . $sql . "\n");

        echo ist im Gegensatz zu print() keine Funktion sondern ein Sprachkonstrukt, ähnlich wie return. Man muss übergebene Werte weder klammern *) noch muss man Strings erst zu einem großen String verbinden. Man kann die Einzelteile einfach mit Komma getrennt übergeben.

        echo "Update\n", $sql, "\n";

        *) Die Klammern fassen hier lediglich die Stringkonkatenation zu einem Ausdruck zusammen.

        echo $verabschiedung, ' ', $name;

        1. Hallo,

          echo ist im Gegensatz zu print() keine Funktion sondern ein Sprachkonstrukt

          Ich weiß, das schreibe ich aus Gründen der Einheitlichkeit wie einen normalen Funktionsaufruf.

          Für print gilt übrigens dasselbe wie für echo, es ist auch ein Sprachkonstrukt.

          Mathias

          1. echo $begrüßung;

            echo ist im Gegensatz zu print() keine Funktion sondern ein Sprachkonstrukt
            Für print gilt übrigens dasselbe wie für echo, es ist auch ein Sprachkonstrukt.

            Ähm, stimmt. Wieso hab ich dann in Erinnerung, dass man da Klammern zu setzen hätte?

            echo "$verabschiedung $name";

      2. Hallo,

        Ich kommentiere mal einige Teile, sowohl zu deinem Konzept als auch allgemeine Dinge.

        SELFHTML Forum halt, man bekommt nie das, nach dem man fragt. ;)

        private $result;

        Eine Datenbankklasse erledigt zwar im Allgemeinen alles über eine einzelne Verbindung, jedoch ist es nicht immer so, dass das Ergebnis vollständig ausgewertet ist, bevor eine neue Abfrage gestartet wird.

        Klar, das Beispiel liest ein Result erst komplett aus, bevor eine weitere Abfrage gesendet wird.

        Sinnvoller, finde ich, sollte es eine eigene Klasse bilden

        Ja, das wäre wohl angebracht, wenn man Results gleichzeitig abfragen will. Im Beispiel läuft noch streng alles nacheinander.

        Ein Konstruktor gibt unter PHP5 nur eine Instanz seiner Klasse zurück, nichts anders.

        Da habe ich zu sehr in JavaScript gedacht. ;)

        Mit return etwas anderes zurückzugeben funktioniert nicht. Das war nur unter PHP4 möglich (möglicherweise auch noch in frühen 5er Versionen). Wenn du unterschiedliche Ergebnisse haben möchtest, verwende das Factory-Pattern.

        Im Grunde sollte ja nur überprüft werden, ob der Konstruktor die Datenbankverbindung herstellen konnte, das lässt sich sicher auch über eine status-Eigenschaft lösen.

        Im Constructor: $this->connect(...);

        Nicht immer ist es sinnvoll, sofort nach Instanziieren der Klasse eine Verbindung aufzubauen

        Ist mir klar - diesen Code soll weder universell sein noch will ich ihn wirklich an anderer Stelle weiterverwenden.

        while ($rowObject = pg_fetch_object($this->result))
            $objectArray[] = $rowObject;
          foreach ($objectArray as $rowObject) ...

        ... In dem Konstrukt wirft dir das foreach eine Fehlermeldung um die Ohren, wenn die Query eine leere Ergebnismenge zurückliefert und $objektArray keins ist.

        Ja... mir sind PHP-Grundlagen durchaus geläufig. ;) Ist bei dir nicht angekommen, warum ich den Code geschrieben und hier gepostet habe? ;)

        $dataArray[] = '"' . pg_escape_string($fieldName) . '" = '' . pg_escape_string($value) . ''';

        PHP kennt eine Funktion namens sprintf, mit der man Strings etwas übersichtlicher zusammenstellen kann: $dataArray[] = sprintf('"%s" = '%s'', pg_escape_string($fieldName), pg_escape_string($value));

        Kenn ich, find ich aber nicht sonderlich übersichtlicher. Wenn ich das verbessern wollte, würde ich wohl auf PDO mit prepare und bindParam umsteigen.

        Mein Vorschlag:

        public function analyzePingbacks () {
                try {
                    $result = $this->query(...);
                    mach;
                    was;
                    mit;
                    result;
                    oder
                    $this->handleResult($result);
                } catch(Exception $ex) {
                    mach;
                    was;
                    bei;
                    fehler;
                    oder
                    $this->handleError($ex);
                } finally {
                    ggf. aufräumarbeiten;
                }
            }

        Interessant, das werde ich mal umzusetzen versuchen. Ich fürchte nur, ich habe letztlich überall nur try-catch-Strukturen, wenn ich das Error-Handler-Konzept durch das Exceptions ersetze. Sowas soll natürlich auch nicht herauskommen:

        try {  
           a();  
           try {  
              b();  
              try {  
                 c();  
              } catch () {}  
           } catch () {}  
        } catch () {}
        

        Das ist ja gerade die Crux bei aufeinander aufbauenden Abläufen.

        Mathias

        1. echo $begrüßung;

          ... In dem Konstrukt wirft dir das foreach eine Fehlermeldung um die Ohren, wenn die Query eine leere Ergebnismenge zurückliefert und $objektArray keins ist.
          Ja... mir sind PHP-Grundlagen durchaus geläufig. ;) Ist bei dir nicht angekommen, warum ich den Code geschrieben und hier gepostet habe? ;)

          Soweit ich das verstanden habe, wolltest du eigentlich was zur Strukturierung wissen, aber das ist für mich kein Grund andere Fehler unbeachtet zu lassen. Wenn das Absicht war, so hilft es dir bei dem Problem nicht weiter, wenn PHP danach einfach versucht weiterzumachen, mit Folgefehlern um sich wirft und am Ende der aufrufenden Instanz noch nicht mal das Auftreten eines Fehlers melden kann. Das Fehlermeldungskonzept von PHP ist historisch bedingt nicht gerade sehr Exceptions-freudig angelegt. Exceptions findet man eingebaut nur an sehr wenigen Stellen (PDO und mysqli könnte ich nur aufzählen). Ansonsten bekommt man für Fehler, die man nicht durch Auswerten von Funktionsergebnissen beachtet, einfach nur Meldungen angezeigt und kann darauf nur in einem eigenen Error-Handler reagieren.

          Interessant, das werde ich mal umzusetzen versuchen. Ich fürchte nur, ich habe letztlich überall nur try-catch-Strukturen, wenn ich das Error-Handler-Konzept durch das Exceptions ersetze. Sowas soll natürlich auch nicht herauskommen:

          try {

          a();
             try {
                b();
                try {
                   c();
                } catch (…) {
                   …
                }
             } catch (…) {
                …
             }
          } catch (…) {
             …
          }

          
          >   
          > Das ist ja gerade die Crux bei aufeinander aufbauenden Abläufen.  
            
          Es kommt auf den jeweiligen Fall an. Generell jede Anweisung einzeln in einen try-catch-Block zu stecken halte ich auch nicht für besonders sinnvoll \*). Wenn der Ablauf innerhalb einer Methode zu unübersichtlich wird, würde ich versuchen, Teilaufgaben davon zu ermitteln und in eigene Methoden auszulagern.  
            
          \*) Vielleicht missinterpretiere ich auch nur dein Beispiel ein wenig :-)  
            
            
          echo "$verabschiedung $name";
          
        2. echo $begrüßung;

          das werde ich mal umzusetzen versuchen.

          Anbei mein Vorschlag.

            
          class DBException extends Exception {}  
          // Tut nichts besonderes, dient nur zur Unterscheidung von anderen Exceptions  
            
          class DB {  
            
            private $host;  
            private $database;  
            private $user;  
            private $password;  
            
            private $connection = null;  
            
            function __construct ($host, $database, $user, $password) {  
              $this->host = $host;  
              $this->database = $database;  
              $this->user = $user;  
              $this->password = $password;  
            }  
            // __destruct() weggelassen, wegen unwichtig  
            
            private function getConnect() {  
              if (!$this->connection)  
                $this->connection = pg_connect('etc.');  
              if (is_resource($connection))  
                return $this->connection;  
              else  
                $this->connection = null;  
                throw new DBException('Connection failed: ' . pg_last_error());  
            }  
            
            public function fetchAll($sql) {  
              $result = $this->query($sql);  
              // Fehler treten beim Abarbeiten der Query auf.  
              // Wenn dort eine Exception ausgelöst wird, bricht auch fetchAll ab  
              // und reicht die Exception an den Aufrufer durch.  
              $rows = array();  
              while ($row = pg_fetch_object($result))  
                $rows[] = $row;  
              pg_free_result($result);  
              return $rows;  
            }  
            
            protected function query($sql) {  
              $connection = $this->getConnection();  
              // Connection-Exceptions werden unbehandelt dem Aufrufer durchgereicht.  
              $result = pg_query($connection, $sql);  
              if (!$result)  
                throw new DBException('Query failed: ' . pg_last_error($connection));  
              return $result;  
            }  
            
            public function update($table, $data, $where = array()) {  
              $sql = 'UPDATE ...'; // Herstellen der Query für die Struktur unwichtig, deswegen gekürzt  
            
              $result = $this->query($sql);  
              // gleicher Kommentar wie bei fetchAll()  
              return pg_affected_rows($result);  
            }  
          }  
            
            
          class weblogDB extends DB {  
            public function __construct() {  
              parent::__construct('host', 'database', 'user', 'password');  
            }  
            
            public function analyzePingbacks() {  
              $errors = array();  
            
              $sql = 'SELECT "id", "value" FROM "block_attributes" WHERE "name" = \'pingback\'';  
              try {  
                $rows = $this->fetchAll($sql);  
              } catch (DBException $ex) {  
                // Hier können wir abbrechen, ohne DB-Ergebnis kann analyzePingback() nichts sinnvolles machen.  
                // Das Reinschachteln des nachfolgenden foreach in obigen try-Block tut auch nicht Not.  
                // Anders sähe es aus, wenn in beiden Fällen (Fetch-Fehler und erfolgreiches foreach) noch was Auszuführendes folgte.  
                $errors[] = array($ex, null);  
                return $errors;  
              }  
            
              foreach ($rows as $row)  
                try {  
                  $this->fixPingback($row);  
                } catch (DBException $ex) {  
                  $errors[] = array($ex, $row);  
                } catch (Exception $ex) {  
                  $errors[] = array($ex, $row);  
                } // ggf. die einzelnen Exceptionsarten unterschiedlich behandeln  
            
              return $errors;  
            }  
            
            public function fixPingback($row) {  
              // Geschäftslogik weggekürzt  
              // Bei Bedarf können darin auch Exceptions geworfen werden.  
            
              try {  
                $this->update('block_attributes', $data, $where);  
              } catch (DBException $ex) {  
                // Logfile schreiben, ignorieren oder  
                // try-catch ganz weglassen, wenn der Aufrufer vom Fehler erfahren soll  
              }  
            }  
            
          }  
            
          $weblogDatabase = new weblogDB();  
          if ($errors = $weblogDatabase->analyzePingbacks()) {  
            echo "<pre>Fehler: \n";  
            foreach ($errors as $error) {  
              printf("Meldung: %s\nRow-ID: %s\n\n",  
                $error[0]->getMessage(),  
                is_null($error[1]) ? '-' : $error[1]->ID);  
            }  
          }  
          
          

          echo "$verabschiedung $name";

          1. Hallo,

            Danke erstmal! Das sieht gleich viel aufgeräumter aus als meine Handler-Lösung. Ich bin nur gerade mit etwas anderem beschäftigt, um genauer darauf einzugehen, werde aber irgendwann darauf zurückkommen.

            Mathias

      3. hi,

        while ($rowObject = pg_fetch_object($this->result))
            $objectArray[] = $rowObject;
        [...]
        (Außerdem ist $rowObject keins, wenn es nichts mehr zu fetchen gibt, sondern false. Das Benennen der Variablen nach ihrem angeblichen Typ halte ich nicht für sonderlich sinnvoll.)

        Das erscheint mir jetzt aber sehr "kleinlich".
        Solange $rowObject _innerhalb_ der Schleife zur Anwendung kommt, ist es vom Typ Objekt - der Name passt dort also.
        Dass es nach dem Holen des letzten Datensatzes beim erneuten Auswerten der Bedingung dann Boolean wird, erscheint mir unerheblich - es wird anschliessend auch nicht mehr als Objekt verwendet.

        gruß,
        wahsaga

        --
        /voodoo.css:
        #GeorgeWBush { position:absolute; bottom:-6ft; }
        1. echo $begrüßung;

          Das erscheint mir jetzt aber sehr "kleinlich".

          Macht nichts :-) Gerade die kleinen Dinge sind es beim Programmieren oft, die bei Nichtbeachtung große Folgen nach sich ziehen können.

          Solange $rowObject _innerhalb_ der Schleife zur Anwendung kommt, ist es vom Typ Objekt - der Name passt dort also.

          Ich finde es einfach überflüssig, den Typ einer Variablen mit in ihren Namen aufzunehmen. Ihr Verwendungszweck sollte beschrieben werden, mehr nicht. Welchen Typ der Inhalt hat kann ja nicht über den Namen festgelegt werden. Vielmehr sehe ich das als (auch wenn es unbewusst geschieht) Legen einer falschen Fährte: Der Name verspricht etwas, was er nicht halten kann.

          echo "$verabschiedung $name";

          1. hi,

            Ich finde es einfach überflüssig, den Typ einer Variablen mit in ihren Namen aufzunehmen.

            Unter PHP, wo so gut wie alles schon nach der nächsten Anweisung ex- oder implizit in etwas beliebiges anderes gecasted worden sein kann, stimme ich dir da zu.

            Welchen Typ der Inhalt hat kann ja nicht über den Namen festgelegt werden. Vielmehr sehe ich das als (auch wenn es unbewusst geschieht) Legen einer falschen Fährte: Der Name verspricht etwas, was er nicht halten kann.

            Unter eine typschwachen Sprache wie PHP vielleicht - aber in typstrengen Sprachen halte ich das manchmal durchaus für sinnvoll, wenn ich einer Variable sofort ansehe, was sie sein soll, und nicht erst an der Stelle ihrer Deklaration nachschauen muss.

            gruß,
            wahsaga

            --
            /voodoo.css:
            #GeorgeWBush { position:absolute; bottom:-6ft; }
  3. Bisher habe ich auf solche grundlegenden Fragen der Programmstrukturierung keine eindeutige Antwort gefunden. Bei OOP lagere ich die Funktionalität letztlich zwar in viele kleine Funktionen aus, die sich dann aufrufen, aber irgendwo habe ich zentral noch einen Ablauf, der aus einer Reihe von Fallunterscheidungen und Fehlerbehandlungen besteht. Wie lässt sich das sinnvoller lösen?

    Es gibt da wohl einen Logikbaum, den man zum Verrecken nicht los wird.   ;)
    Diesen kannst Du funktions- oder objektorientiert bearbeiten, letzteres fällt dem Menschen offiziell leichter, wenn eine gewisse Komplexität überschritten wird.
    Wir raten grundsätzlich funktionsorientiertes Vorgehen, also "Spaghetticode", an. Gerne auch mit GoTos.   ;)

    1. Hallo,

      Es gibt da wohl einen Logikbaum, den man zum Verrecken nicht los wird.   ;)
      Diesen kannst Du funktions- oder objektorientiert bearbeiten (...)

      Oder aspektorientiert, eine kleine, feine Erweiterung der OOP.

      das heißt (heute noch), statt:

      (Klassen-)function xyz {
         /*Hier Code*/
         return rueckgabewert
      }

      schreibst Du:

      (Klassen-)function xyz {
         if(testeIrgendwasGlobalesBevorMethodenaufruf()) {
            /*Hier Code*/
         } else return Null
         if (testeIrgendwasGlobalesNachMethodenaufruf) {
            return rueckgabewert
         } else return Null
      }

      Gruß, dieTante...

      1. (Klassen-)function xyz {
           if(testeIrgendwasGlobalesBevorMethodenaufruf()) {
              /*Hier Code*/
           } else return Null
           if (testeIrgendwasGlobalesNachMethodenaufruf) {
              return rueckgabewert
           } else return Null
        }

        So, so, ist aber höchst verdächtig er Code. OOP haben wir ja noch halbwegs verstanden, denn diese Entwicklungsart fordert sich so zu sagen von selbst, wenn man etliche Rudel Funktionen hat und diese ordnet, wobei sich oft herausstellt, dass einige davon zusammenghörende Datenstrukturen bearbeiten, also Entitäten, also Objekte.

        Das mit dem TestIrgendwas...() oder TestIrgendwasNach... ist etwas blass. Vermutlich soll da eine neue Abstraktionsebene eingezogen werden, dennoch...