MB: Warum nicht Error & Exception Überprüfung in Validatoren

moin,

warum nicht die Überprüfungs Code in Validatoren auslagern:

function checkCustomerID ( int $id ) : bool {
    if ( Database::fetch ( $id ) === null ) {
        throw new Exception( 'can\'t fint Customer ID' );
    } else { // der struktur IF-ELSE zu liebe
        return true;
    }
    // so on
}

warum nicht einfach den Code auslagern, wenn mehr als eine Prüfung mit dem gleichen Sachverhalt existieren.

function checkCustomerID ( int $id ) : bool {
    if ( DataValidator::existData( $id ) ) {
        // so on
    }
}

…oder…

function checkCustomerID ( int $id ) : bool {
    DataValidator::existData( $id );
    // so on
}

…dann…

class DataValidator {

    public static function existData ( int $id ) : bool {
        if ( Database::fetch ( $id ) === null ) {
            throw new Exception( 'can\'t find Data' );
        } else {
            return true;
        }
    }
}

die herangehensweise spart Code und trennt Code bezügl. der Zuständigkeit und bewart einen besseren überblich finde ich.

lgmb

PS: Warum funktioniert „~~~[code]“ nicht?

  1. Hallo MB,

    PS: Warum funktioniert „~~~[code]“ nicht?

    Was meinst du damit?

    Bis demnächst
    Matthias

    --
    Du kannst das Projekt SELFHTML unterstützen,
    indem du bei Amazon-Einkäufen Amazon smile (Was ist das?) nutzt.
  2. Natürlich lagern wir Code aus wenn sich Redundanzen abzeichnen und machen ihn damit wiederverwertbar. MFG

    1. Natürlich lagern wir Code aus wenn sich Redundanzen abzeichnen und machen ihn damit wiederverwertbar. MFG

      PS: Eine Fehlerbehandlung über Exceptions abzuwickeln ist eine Frage ob damit der Code einfacher und leserlicher wird. In PHP ist es jedoch so, daß eine Exception immer auch einen Trace beinhaltet und das ist das was man einem Anwender bei einer fehlerhaften Eingabe nicht zeigen möchte denn die Zeile wo es knallt interessiert den Anwender sicher nicht.

      MFG

      1. Tach!

        In PHP ist es jedoch so, daß eine Exception immer auch einen Trace beinhaltet und das ist das was man einem Anwender bei einer fehlerhaften Eingabe nicht zeigen möchte denn die Zeile wo es knallt interessiert den Anwender sicher nicht.

        Das klingt, als ob es einen unauflöslichen Zusammenhang gäbe, zwischen den Daten in einer Exception und dem, was man dem Anwender präsentiert. Man kann aber display_errors auf 0 setzen und Fehlermeldungen in Logfiles schreiben. Das wirkt auch bei Exceptions.

        dedlfix.

        1. Tach!

          In PHP ist es jedoch so, daß eine Exception immer auch einen Trace beinhaltet und das ist das was man einem Anwender bei einer fehlerhaften Eingabe nicht zeigen möchte denn die Zeile wo es knallt interessiert den Anwender sicher nicht.

          Das klingt, als ob es einen unauflöslichen Zusammenhang gäbe, zwischen den Daten in einer Exception und dem, was man dem Anwender präsentiert. Man kann aber display_errors auf 0 setzen und Fehlermeldungen in Logfiles schreiben. Das wirkt auch bei Exceptions.

          Fehlerbehandlung über Logfiles abzuwickeln ist keine gute Idee. Erstens kann es da passsieren daß Fehler übersehen werden und zweitens ist das ziemlich umständlich.

          Nicht empfehlenswert!

          1. Tach!

            Fehlerbehandlung über Logfiles abzuwickeln ist keine gute Idee. Erstens kann es da passsieren daß Fehler übersehen werden und zweitens ist das ziemlich umständlich.

            Die bessere Lösung lautet wie?

            dedlfix.

          2. moin,

            Fehlerbehandlung über Logfiles abzuwickeln ist keine gute Idee. Erstens kann es da passsieren daß Fehler übersehen werden und zweitens ist das ziemlich umständlich.

            Nicht empfehlenswert!

            für kleine Projekte mag es sein das es durchaus sinnhaft und empfehlenswert ist es nicht zutun, aber bei komplexen Projekten wo ein Entwickler Team dahinter steckt, ist es - denke ich mal - pflicht es zutun!

            lgmb

        2. Hallo dedlfix,

          ich würde es eher mit try/catch versuchen und im Fall eines fachlichen Fehlers, der per Exception signalisiert wird, auch eine fachliche Fehlermeldung präsentieren.

          Verzeihen Sie, lieber User, aber Ihre Eingabe war einfach nur Scheiße!

          Rolf

          --
          sumpsi - posui - clusi
          1. moin,

            ich würde es eher mit try/catch versuchen und im Fall eines fachlichen Fehlers, der per Exception signalisiert wird, auch eine fachliche Fehlermeldung präsentieren.

            sry, spl_exception_handler() hab ich nicht erwähnt 😕. bei error_report( E_ALL ) fängt der Interpreter auch nach throw

            PS: ich muss dazu sagen das ich leider seit gefühl monaten nicht mehr mit PHP entwickelt habe und jetzt wieder entsteige.

            lgmb

      2. moin,

        [...] und das ist das was man einem Anwender bei einer fehlerhaften Eingabe nicht zeigen möchte denn die Zeile wo es knallt interessiert den Anwender sicher nicht.

        Ich weiß 😉

        lgmb

        1. Dieser Beitrag wurde gesperrt: Perl und dessen Gegebenheiten sind nicht Thema dieses Threads.

          moin,

          [...] und das ist das was man einem Anwender bei einer fehlerhaften Eingabe nicht zeigen möchte denn die Zeile wo es knallt interessiert den Anwender sicher nicht.

          Ich weiß 😉

          PS: In Perl ist es wesentlich einfacher mit Exceptions zu arbeiten. So wirft ein einfaches die "Das eingegebene Datum ist ungültig!\n" eine Exception wobei der Backtrace infolge des angefügten Newline unterdückt wird.

          Wenn man Klassen hat wo es Fehler nur aufgrund fehlerhafte Benutzereingaben geben kann, ist das eine elegante Art und Weise der Fehlerbehandlung.

          Und noch cleverer ist es, die Fehlertexte gar nicht selber zu schreiben sondern das zu nehmen wo die Meldungen schon drinstehen. Beschwerden vom OS z.B. stehen in $^E sogar in deutscher Sprache.

          MFG

          1. moin,

            PS: In Perl ist es wesentlich einfacher mit Exceptions zu arbeiten. […]

            Wie du weißt arbeite ich nicht mit Perl und ich werde es auch in meiner Lage nicht tun. Sicherlich gibt es Problemlösungen, wo diese Programmiersprache wirklich Perfekt ist, aber das sind nicht meine.

            Schönen Abend 😀

            lgmb

  3. Tach!

    warum nicht die Überprüfungs Code in Validatoren auslagern:

    Was genau ist denn dein Problem, das du zu lösen versuchst?

    function checkCustomerID ( int $id ) : bool {
        if ( Database::fetch ( $id ) === null ) {
            throw new Exception( 'can\'t fint Customer ID' );
        } else { // der struktur IF-ELSE zu liebe
            return true;
        }
        // so on
    }
    

    Es gibt jedenfalls kein Gesetz, dass if immer ein else benötigt. Der else-Zweig ist hier überflüssig, das return true kann einfach so notiert werden. Wenn die Bedingung zutrifft, wird die Funktion über das throw verlassen und der nachfolgende Code nicht mehr ausführt. Deshalb ist es nicht notwendig, zwischen Code im false-Fall (else) und allgemein auszuführendem Code zu trennen. Im true-Fall wird weder das eine noch das andere ausgeführt, und im false-Fall sowie beides. Daran ändert auch nicht, dass es nur ein return-Statement und keinen allgemein auszuführenden Code gibt.

    dedlfix.

    1. Hallo dedlfix,

      eine Funktion, die im Fehlerfall eine Exception wirft und ansonsten nur true zurückgibt, braucht gar nichts zurückzugeben.

      Ich halte eine solche Funktion aber für überflüssig. Wenn ich schon die id durch fetch (also den Kunden einlesen) überprüfe und Fehler durch Exception anzeige, dann kann ich auch gleich eine Funktion get schreiben

      function getCustomer( int $id ) : Customer {
          $customer = Database::fetch ( $id );
          if ( $customer === null ) {
              throw new Exception( "can't find Customer ID $id" );
      
          return $customer
      }
      

      das könnte dann in die Customer-Repositoryklasse hinein (wobei Database:fetch ein schlechter Methodenname ist um Kunden zu lesen - aber ich nehme mal an, dass der generische Name dem Umstand geschuldet ist, dass es sich um ein abstraktes Beispiel handelt).

      Wenn ich jetzt nur wissen will, ob die id ok ist, ignoriere ich die Rückgabe. Ansonsten arbeite ich gleich mit dem gefundenen Kunden weiter. So ergibt es einen Sinn.

      Eine echte checkCustomerId Methode sollte sinnvollerweise einen anderen Datenbankzugriff machen, der ausschließlich die ID validiert. Ein voller Fetch dürfte außer einem Index Seek auch das Lesen der Tabledaten benötigen. Liest man nur die ID, reicht ein Index Seek. Das ist schneller. Ich würde einer solchen Funktion aber auch einen echten bool-Rückgabewert verpassen und Exceptions nur im Fall eines fehlgeschlagenen Datenbankzugriffs werfen. Denn wenn ich eine solche Methode tatsächlich brauche, dann ist true und false halbwegs gleichwahrscheinlich, das ist nicht der Anwendungsfall für Excptions. Exceptions wirft man dann, wenn dieser Fall im Normalbetrieb nicht auftaucht; zumindest habe ich das mal so gelernt.

      Rolf

      --
      sumpsi - posui - clusi
      1. Tach!

        Wenn man den konkreten Fall kennt, kommt man durchaus zu einer anderen Antwort auf die Frage nach der sinnvollen Lösung, als wenn man irgendwelche Pauschalitäten beurteilen soll. Darin sind wir uns sicher einig, und deswegen hatte ich auch erstmal nur nach dem zu lösenden Problem gefragt, und als Nebenschauplatz ein paar technische Argumente zu if-else-Konstrukten mit Abbruch/Ende im if geliefert. Ob sowas überhaupt in der Lösung zum konkreten Problem auftaucht, steht auf einem anderen Blatt.

        dedlfix.

      2. Hallo dedlfix,

        eine Funktion, die im Fehlerfall eine Exception wirft und ansonsten nur true zurückgibt, braucht gar nichts zurückzugeben.

        Ich halte eine solche Funktion aber für überflüssig.

        Hier geht es ums Prinzip!

        MFG

      3. moin,

        eine Funktion, die im Fehlerfall eine Exception wirft und ansonsten nur true zurückgibt, braucht gar nichts zurückzugeben.

        siehe hier

        Ich halte eine solche Funktion aber für überflüssig. [...]

        Ein Einwand. Jedoch ist diese Funktion aus der Luft gegriffen. ich hätte auch Metasyntaxtische Variablen fubar schreiben können, aber man hat mir gesagt - ich glaube @@Gunnar Bittersmann, @@dedlfix oder du - das es besser ist im Konkreten Fall ein exemplarisches Beispiel zu geben.

        lgmb

    2. moin,

      Es gibt jedenfalls kein Gesetz, dass if immer ein else benötigt.

      durch aus nicht

      Der else-Zweig ist hier überflüssig, das return true kann einfach so notiert werden.

      stimmt, aber du schreibst ja auch am ende eines Attribut-Wertes des letzten Attributes eines CSS Selektors ein Semikolon obwohl da keins hin muss.

      small {
          display: none;
      }
      

      lgmb

    3. moin,

      function checkCustomerID ( int $id ) : bool {
          if ( Database::fetch ( $id ) === null ) {
              throw new Exception( 'can\'t fint Customer ID' );
          } else { // der struktur IF-ELSE zu liebe
              return true;
          }
          // so on
      }
      

      Es gibt jedenfalls kein Gesetz, dass if immer ein else benötigt. Der else-Zweig ist hier überflüssig[…]

      Ich bin wohl einwenig zu fest in der Einhaltung gefahren und ich sehe es ein. Sorry dafür. Ich weis das es keinen unterschied mach, weil ja genau das selbe heraus kommt.

      lgmb

  4. problematische Seite

    hi MB,

    ich habe Dir hier mal aufgeschrieben wie ich meine Fehlerbehandlung mache und Du kannst das da auch ausprobieren, ein Link macht einen Fehler anschaulich.

    Und natürlich lege ich auch den Code für die als statische Route an den URL gebundene Klasse offen. Wie Du sehen kannst, Fehlerbehandlung und OOP kann auch etwas Einfaches sein und das ist ja auch das Ziel einer überschaubaren Programmierung.

    Und es muss nicht immer eine Exception sein die im Fehlerfall geworfen wird. Bei mir wird z.B. nur die Zeilennummer ausgegeben, was völlig ausreicht, weil der Fehler faktisch nur in einer Datei, die dem Entwickler namentlich bekannt ist, auftreten kann. Also brauchen wir gar keinen Backtrace mit Dateinamen. Das ist der Vorteil einer flachen Klassenhierarchie.

    MFG

    1. problematische Seite

      Moin Hotti,

      Und natürlich lege ich auch den Code für die als statische Route an den URL gebundene Klasse offen.

      Hast du dazu einen Git-Link o.ä.?

      Und es muss nicht immer eine Exception sein die im Fehlerfall geworfen wird. Bei mir wird z.B. nur die Zeilennummer ausgegeben, was völlig ausreicht

      Und um einen Fehler zu analysieren musst du dir dann jeweils die Version auschecken, die zu dem genauen Zeitpunkt des Fehlers live war? Stelle ich mir recht aufwendig vor, wenn man, wie doch eigentlich üblich heutzutage, noch einen CI dazwischen hat..

      Cheers, markk

  5. Spezifischere Exceptions lassen sich besser verarbeiten.
    Aus ausgelagertem ÜberprüfungsCode ist es schwer solche zu schmeißen.

    Beispiel:

    Bei mir hat jede Domäne ihre eigene Throwable-Familie.

    Vorteile:

    • im catch wahlweise Eltern und Kinder fangen.
    • der ClassName ersetzt die Message bzw. dient als Key für die Übersetzung.
    • Reflektionen
    • previous Exception nutzen
    • ...
    <?php
    namespace my_app;
    
    class throwableException extends \Exception
    {
    }
    
    <?php
    namespace my_app\Customer;
    
    final class EntityNotFoundException extends \my_app\throwableException
    {
    }
    
    <?php
    namespace my_app\Product;
    
    final class EntityNotFoundException extends \my_app\throwableException
    {
    }
    
    1. moin,

      Das tut doch nichts zur Sache oder vertuhe ich mich da? Sry, dafür.

      z.B.:

      1. irgend eine Business Klasse von Business Klassen arbeitet etwas ab.
      2. Vor der abarbeitung, übereicht eine Klassenmethode den zu behandelte Sachverhalt an einen Validator der für diese spezielle Situation zuständig ist.
      3. Ist dieser zu behandelnde Sachverhalt validiert, arbeitet die Klassenmethode der Business Klasse einfach weiter, wenn nicht, wirft der Validator eine Exception oder Error (z.B. new FoobarError(), new FoobarException() usw.) die man geschrieben hat.

      Bist du mit diesem Thread im Bilde uns hast die Lösung die du präsentierst? Dann fürchte ich habe ich den zusammenhang nicht im Blick, sorry 😕!

      lgmb

      1. Tach!

        1. Ist dieser zu behandelnde Sachverhalt validiert, arbeitet die Klassenmethode der Business Klasse einfach weiter, wenn nicht, wirft der Validator eine Exception oder Error (z.B. new FoobarError(), new FoobarException() usw.) die man geschrieben hat.

        Die Validatorklasse kann nur eine allgemeine ValidationFailed werfen, aber keine Exception, die für den Business Case spezifisch ist. Dazu müsste man dort die allgemeine Exception abfangen und umpacken. Das kann man sich sparen, wenn man den Aufrufer selbst die Exception werfen lässt, die er für richtig hält. Außerdem kann eine fehlende Validierung immer nur als so schwerwiegend eingestuft werden, dass eine Exception nötig ist, selbst wenn dem Aufrufer eine einfache Information reichen würde.

        Der Validator sollte seine Aufgabe des Validierens erfüllen und nicht in den Control Flow eingreifen, wenn dazu keine zwingende Notwendigkeit besteht. Wenn der Aufrufer immer eine Exception abzufangen hat, macht das die Behandlung nur unnötig komplex, auch an Stellen, wo das nicht nötig ist.

        dedlfix.

        1. Hallo dedlfix,

          ich glaube, ihr seit euch über die Spezifität der Validatoren uneins.

          Mir scheint, dass MB sehr spezifische Validatoren bauen will, die nur an einer einzigen Ecke der Businesslogik verwendet werden. Die können dann auch spezifische Fehler erzeugen.

          Den Nutzen des Routings der Prüfung über eine Klassenmethode (= static function?) habe ich allerdings auch nicht verstanden.

          Und das Auslagern von Code in eigene Klassen, um Klassen zu haben, scheint mir auch nicht die beste Idee. Ich weiß nicht wie gut PHP das unter Fast CGI oder mit anderen Code-Caching Verfahren optimieren kann, aber grundsätzlich ist eine Klasse erstmal eine Struktur, die PHP zur Laufzeit aufbaut. Jede zusätzliche Klasse ist zusätzliche Arbeit, die pro Request vom Server geleistet werden muss.

          Es gibt verschiedene Anlässe, technische Klassen zu bilden (ich rede nicht von Businessklassen!):

          • rein statisch mit statischen Methoden. Sowas macht man bspw. in Java oder C#, um einen Namespace für eine Funktionensammlung zu haben. In PHP bieten sich da eher Funktionen und Namespaces an
          • ich baue eine Methode, und sie wird gigantisch. Es ist viel zu tun, und ich brauche eine Menge Arbeitsvariablen. Es wird gruselig, diese Methode zu testen, und es wird unüberschaubar, das Monster über Testargumente in allen Facetten zu validieren. Statt nun Code aus der Methode in private Methoden der Klasse auszulagern, die dann entweder viele Parameter haben oder die Klasse mit Instanzvariablen zumüllen, lagere ich das in eine Worker-Klasse aus, die ich dann auch separat testen kann.
          • ich möchte in Test und Produktion unterschiedlichen Code laufen lassen. Insbesondere im Bereich Logging, oder auch Fehlerprüfungen. Im Test prüfe ich vielleicht deutlich aufwändiger, und logge mehr. Man kann das so lösen, dass man den Code in zwei Klassen unterbringt, eine für Test, eine für Produktion, die beide das gleiche Interface haben. Und ich stelle an geeigneter Stelle eine Instanz dieser Klasse zur Nutzung durch den Rest des Codes zur Verfügung. Dann muss ich genau 1x testen, ob ich in Test oder Prod bin, den Rest erledigt die Polymorphie.

          Sicherlich gibt's noch mehr Anlässe.

          Deine Validatoren könnten durch Anlass 3 gerechtfertigt sein.

          Rolf

          --
          sumpsi - posui - clusi
          1. moin,

            Den Nutzen des Routings der Prüfung über eine Klassenmethode (= static function?) habe ich allerdings auch nicht verstanden.

            Ups, sorry!!! ich meinte Instanzmethoden. Sry 😟

            lgmb

            1. Hallo MB,

              dann verstehe ich das 😀. Die kannst Du im Zweifel sogar von einer abstrakten Klasse erben oder als Trait einmischen.

              Rolf

              --
              sumpsi - posui - clusi
              1. moin,

                dann verstehe ich das 😀. Die kannst Du im Zweifel sogar von einer abstrakten Klasse erben oder als Trait einmischen.

                Habe ich bei Template Construktor gemacht.

                // create
                $this->foobarTable = new Table();
                
                // built
                $this->foobarTable
                    ->setCaption()
                    ->setSettings()
                    ->setHead()
                    ->setBody()
                    ->foot();
                

                Tut mir wirklich leid. Ich „musste“ in der Vergangenheit mit CSS arbeite was ich garnicht mag. Da habe ich PHP aus den augen verloren 😟.

                lgmb

              2. moin,

                $ta = new TableAttributs( 'id', 'class', 1, 1 );
                $tcol = new TableColum( 'id', 'class', '1' );
                $tset = new TableSettings( 'id', 'class', null, [ $tcol, $tcol, $tcol, $tcol ] );
                $tc = new TableCell( 'foobar', $ta );
                $tr = new TableRow( [ $tc, $tc, $tc, $tc ] );
                $ts = new TableSection( [ $tr, $tr ], $ta );
                $t = new TableConstructor( 'Foo Bar', $tset, $ts, $ts, $ts );
                echo $t;
                

                mit Template

                <!-- TABLE -->
                <table
                    <?php if( isset( $attributs->id ) and !empty( $attributs->id ) ) : ?>
                    id="<?php echo $attributs->id ?>"
                    <?php endif; ?>
                    <?php if( isset( $attributs->class ) and !empty( $attributs->class ) ) : ?>
                    class="<?php echo $attributs->class ?>"
                    <?php endif; ?>>
                    
                    <!-- COLUMN GROUPS -->
                    <?php if( isset( $settings ) and !empty( $settings ) ) : ?>
                    <colgroup
                        <?php if( isset( $settings->id ) and !empty( $settings->id ) ) : ?>
                        id="<?php echo $settings->id ?>"
                        <?php endif; ?>
                        <?php if( isset( $settings->class ) and !empty( $settings->class ) ) : ?>
                        class="<?php echo $settings->class ?>"
                        <?php endif; ?>
                        <?php if( isset( $settings->span ) and !empty( $settings->span ) ) : ?>
                        span="<?php echo $settings->span ?>"
                        <?php endif; ?>
                        >
                        <?php if( isset( $settings->columns ) and !empty( $settings->columns ) ) : ?>
                        <?php for( $i = 0; $i < count( $settings->columns ); $i++ ) : ?>
                        <col
                            <?php if( isset( $settings->columns[ $i ]->id ) and !empty( $settings->columns[ $i ]->id ) ) : ?>
                            id="<?php echo $settings->columns[ $i ]->id ?>"
                            <?php endif; ?>
                            <?php if( isset( $settings->columns[ $i ]->class ) and !empty( $settings->columns[ $i ]->class ) ) : ?>
                            class="<?php echo $settings->columns[ $i ]->class ?>"
                            <?php endif; ?>
                            <?php if( isset( $settings->columns[ $i ]->span ) and !empty( $settings->columns[ $i ]->span ) ) : ?>
                            span="<?php echo $settings->columns[ $i ]->span ?>"
                            <?php endif; ?>>
                        <?php endfor ?>
                        <?php endif ?>
                    </colgroup>
                    <?php endif ?>
                    
                    <!-- CAPTION -->
                    <?php if( isset( $caption ) and !empty( $caption ) ) : ?>
                    <caption><?php echo $caption ?></caption>
                    <?php endif; ?>
                    
                    <!-- MATRIX -->
                    <?php foreach( $sections as $key => $section ) : ?>
                    <?php if( $section === null ) continue; // if one of section is not defined like '<tfoot>' ?>
                    <!-- SECTION -->
                    <t<?php echo $key; ?>
                        <?php if( isset( $section->attributs->id ) and !empty( $section->attributs->id ) ) : ?>
                        id="<?php echo $section->attributs->id ?>"
                        <?php endif; ?>
                        <?php if( isset( $section->attributs->class ) and !empty( $section->attributs->class ) ) : ?>
                        class="<?php echo $section->attributs->class ?>"
                        <?php endif; ?>>
                        <?php for( $i = 0; $i < count( $section->row ); $i++ ) : ?>
                        <tr
                            <?php if( isset( $section->row[ $i ]->attributs->id ) and !empty( $section->row[ $i ]->attributs->id ) ) : ?>
                            id="<?php echo $section->row[ $i ]->attributs->id ?>"
                            <?php endif; ?>
                            <?php if( isset( $section->row[ $i ]->attributs->class ) and !empty( $section->row[ $i ]->attributs->class ) ) : ?>
                            class="<?php echo $section->row[ $i ]->attributs->class ?>"
                            <?php endif; ?>>
                            <?php for( $j = 0; $j < count( $section->row[ $i ]->cell ); $j++ ) : ?>
                            <<?php if( $key === 'head' or $j === 0 and $key !== 'foot' ) :?>th<?php else: ?>td<?php endif; ?>
                                <?php if( isset( $section->row[ $i ]->cell[ $j ]->attributs->id ) and !empty( $section->row[ $i ]->cell[ $j ]->attributs->id ) ) : ?>
                                id="<?php echo $section->row[ $i ]->cell[ $j ]->attributs->id ?>"
                                <?php endif; ?>
                                <?php if( isset( $section->row[ $i ]->cell[ $j ]->attributs->class ) and !empty( $section->row[ $i ]->cell[ $j ]->attributs->class ) ) : ?>
                                class="<?php echo $section->row[ $i ]->cell[ $j ]->attributs->class ?>"
                                <?php endif; ?>
                                <?php if( isset( $section->row[ $i ]->cell[ $j ]->attributs->row ) and !empty( $section->row[ $i ]->cell[ $j ]->attributs->row ) ) : ?>
                                rowspan="<?php echo $section->row[ $i ]->cell[ $j ]->attributs->row ?>"
                                <?php endif; ?>
                                <?php if( isset( $section->row[ $i ]->cell[ $j ]->attributs->column ) and !empty( $section->row[ $i ]->cell[ $j ]->attributs->column ) ) : ?>
                                colspan="<?php echo $section->row[ $i ]->cell[ $j ]->attributs->column ?>"
                                <?php endif; ?>>
                                <?php echo $section->row[ $i ]->cell[ $j ]->content ?>
                            </<?php if( $key === 'head' or $j === 0 and $key !== 'foot' ) :?>th<?php else: ?>td<?php endif; ?>>
                            <?php endfor; ?>
                        </tr>
                        <?php endfor; ?>
                    </t<?php echo $key; ?>>
                    <?php endforeach; ?>
                </table>
                

                heraus kam

                <table>
                    <!-- COLUMN GROUPS -->
                    <colgroup id="id" class="class">
                        <col id="id" class="class" span="1">
                        <!-- soon -->
                    </colgroup>
                        
                    <!-- CAPTION -->
                    <caption>Foo Bar</caption>
                        
                    <!-- MATRIX -->
                    <!-- SECTION -->
                    <thead id="id" class="class">
                        <tr>
                            <th id="id" class="class" rowspan="1" colspan="1">
                                foobar
                            </th>
                            <!-- soon -->
                        </tr>
                        <!-- soon -->
                    </thead>
                    <tbody>
                        <!-- soon -->
                    </tbody>
                    <tfoot>
                        <!-- soon -->
                    </tfoot>
                </table>
                

                Ich habs mit abstrakten Eltern Klassen gemacht und Interfaces verwendet

                lgmb

                1. Hallo MB,

                  Whoa, was ist denn das für ein Template? Wieso <?php ... ?> auf jeder einzelnen Zeile? Und man darf in einem Template, wenn es mit PHP geschrieben ist, durchaus programmieren. Also Helper-Funktionen und -Variablen verwenden.

                  Unter anderem auch htmlspecialchars(), das fehlt bei Dir noch ganz - es sei denn das hast Du schon anderweit gemacht. Ich baue es hier deshalb bewusst nicht ein, aber ich denke, sowas gehört eigentlich ins Template, nicht ins Viewmodel.

                  Zum Beispiel könntest Du diesen Helper bauen:

                  function renderAttribute($attrName, $value) {
                     if (!empty($value))
                        echo " $attrName='$value'";
                  }
                  

                  isset brauchst Du nicht, empty() ist ein Sprachkonstrukt, keine Funktion und prüft isset implizit mit ab. Im Handbuch steht:

                  A variable is considered empty if it does not exist or if its value equals FALSE

                  (Beachte: equals FALSE meint == FALSE, nicht === FALSE).

                  Damit schreibst Du ins Template:

                  <table<?=renderAttribute("id", $attributs->id)?><?=renderAttribute("class", $attributs->class)?>>
                  

                  oder verlässt PHP für den Moment gar nicht (sollte unterm Strich aber egal sein):

                  <table<?=renderAttribute("id", $attributs->id) . renderAttribute("class", $attributs->class)?>>
                  

                  Es geht aber noch deutlich besser. Du könntest deine Klasse TableAttributs ertüchtigen (und vermutlich könnte sie sogar ElementAttributes heißen, weil sie nicht nur für Tabellen funktioniert):

                  class ElementAttributes {
                     public function render(...$attributes) {
                        foreach ($attributes as $attr) {
                           if (!empty($this->$attrName))
                              echo " $attrName='{$this->$attrName}'";
                        }
                     }
                  }
                  

                  Ich habe das ganze Attribut mit echo ausgegeben, statt dauernd zwischen PHP und HTML zu wechseln.

                  Damit hättest Du dann statt 7 Zeilen nur noch diese (und es ist auch kompakter, <?= ... ?> statt <? echo ...; ?> zu schreiben. Das predige ich schon länger 😉

                  <table <?=$attributs->render("id", "class")?> >;
                  

                  Es wäre dann auch sinnvoll, hinter den beiden for-Schleifen jeweils eine Variable mit dem aktuellen Schleifenwert zu setzen. Oder gleich die foreach-Schleife zu verwenden - was aber nur sinnvoll ist wenn Du $i oder $j nicht brauchst...

                  <?php foreach ($section->row as $rowData) :
                     <tr <?=$rowData->attributs->render("id", "class")?> >
                        <?php for( $j = 0; $j < count( $rowData->cell ); $j++ ) :
                           $cellData = $rowData->cell[$j];
                           $cellTag = ( $key === 'head' or $j === 0 and $key !== 'foot' ) ? "th" : "td"; ?>
                           <<?=$cellTag . $cellData->attributs->render("id", "class", "rowSpan", "colSpan")?>>
                              <?=$cellData->content?>
                           </<?=$cellTag?>>
                        <?php endfor; ?>
                     </tr>
                  <?php endforeach; ?>
                  

                  Das ist signifikant kleiner als dein Code. Ich find's auch besser lesbar.

                  Ob es sinnvoll ist, im Schleifenkern den PHP-Modus zu verlassen, weiß ich nicht. Vermutlich nicht. Außer < und > kommt ja eh alles aus PHP.

                  Schneller laufen wird's auch nicht, durch die Methodenaufrufe gehen einige Mikrosekunden drauf. Ob das jetzt sofort funktioniert weiß ich nicht, das ist teilweise in der Sandbox probiert, aber teilweise auch vom Hirn ins Forum gehauen.

                  Rolf

                  --
                  sumpsi - posui - clusi
                  1. moin,

                    Whoa, was ist denn das für ein Template? Wieso <?php ... ?> auf jeder einzelnen Zeile?

                    Kontext wechsel 😕

                    Und man darf in einem Template, wenn es mit PHP geschrieben ist, durchaus programmieren. Also Helper-Funktionen und -Variablen verwenden.

                    function renderAttribute($attrName, $value) {
                       if (!empty($value))
                          echo " $attrName='$value'";
                    }
                    

                    mir ist es deutlich lieber wenn ich BuiltIn-Funktionen nehme

                    Ich zerhacke immer alles und plaziere immer alles schön seeehr weit weg 😕. Hab da gute und weniger gute Erfahrungen gemacht.

                    Unter anderem auch htmlspecialchars(), das fehlt bei Dir noch ganz - es sei denn das hast Du schon anderweit gemacht.

                    hätte ich is aber nur n Test 😉

                    Ich baue es hier deshalb bewusst nicht ein, aber ich denke, sowas gehört eigentlich ins Template, nicht ins Viewmodel.

                    ich denke nicht, weil es ja nur ein Template ist. Aber ich bin im gegensatz zu dir (einen Profil) n echtes greenhorn 😏.

                    isset brauchst Du nicht, empty() ist ein Sprachkonstrukt, keine Funktion und prüft isset implizit mit ab. Im Handbuch steht:

                    …ja ich erinner mich da an was. Danke für die Auffrischung

                    Damit hättest Du dann statt 7 Zeilen nur noch diese (und es ist auch kompakter, <?= ... ?> statt <? echo ...; ?> zu schreiben. Das predige ich schon länger 😉

                    ja aber php.ini schaltet das per Default aus und es gibt da glaube ich Probleme mit DB bezügl. SQL-Injection oder so. Lasst mich erst in der PHP-Welt ankommen 😀.

                    Irgend wann baue ich mal einen simplen MarkDown-PlugIn.

                    Es wäre dann auch sinnvoll, hinter den beiden for-Schleifen jeweils eine Variable mit dem aktuellen Schleifenwert zu setzen. Oder gleich die foreach-Schleife zu verwenden - was aber nur sinnvoll ist wenn Du $i oder $j nicht brauchst...

                    da habe ich auch meine Problemchen wie ich das lösen kann. Dank für den Rat.

                    […] Das ist signifikant kleiner als dein Code. Ich find's auch besser lesbar.

                    das ist es… ich werde mich apäter damit beschäftigen, weil ichs weniger wichtig finde, Wichtig! aber weniger.

                    Ob es sinnvoll ist, im Schleifenkern den PHP-Modus zu verlassen, weiß ich nicht. Vermutlich nicht. […]

                    Kontext wechsel. Wenn ich im HTML Template bin dann notiere ich z.B. <?php echo $foobar; ?> und <?php if ( true ) : ?>.

                    lgmb

                    1. Tach!

                      Damit hättest Du dann statt 7 Zeilen nur noch diese (und es ist auch kompakter, <?= ... ?> statt <? echo ...; ?> zu schreiben. Das predige ich schon länger 😉

                      ja aber php.ini schaltet das per Default aus und es gibt da glaube ich Probleme mit DB bezügl. SQL-Injection oder so.

                      Nein, <?= steht seit Version 5.4 unabschaltbar zur Verfügung. Die Versionen, in denen das abschaltbar war sind ausgestorben (lies: unsupportet). Ein Zusammenhang zu DB-Handling und SQL-Injection oder jeglicher Art anderer Injection besteht auch nicht. Wenn ein Kontextwechsel vorhanden ist, muss der in beiden Schreibweisen in exakt gleicher Art und Weise berücksichtigt werden.

                      Ob es sinnvoll ist, im Schleifenkern den PHP-Modus zu verlassen, weiß ich nicht. Vermutlich nicht. […]

                      Jedenfalls ist es nicht sonderlich sinnvoll, zwischen ?> und <?php nur Whitespace stehen zu haben, der für die Ausgabe irrelevant ist. Da kann man gleich im PHP-Modus bleiben. Der erzeugte HTML-Code ist nur bedingt lesbar, weil ein dem ?> nachfolgender Zeilenumbruch nicht in der Ausgabe erscheint, aber die Einrückungs-Whitespace-Zeichen. Da entsteht also ohne zusätzliche Zeilenschaltung nur eine lange Zeilenwurst. Wichtig ist in erster Linie die syntaktische Korrektheit, nicht die menschliche Lesbarkeit. Syntaktische Korrektheit kann auch ein Automat prüfen, dem der Whitespace egal ist. Inhaltliche Probleme geht man hingegen meist in der DOM-Ansicht an, die der Browser anhand der logischen Struktur des Dokuments präsentiert.

                      dedlfix.

                      1. moin,

                        ja aber php.ini schaltet das per Default aus und es gibt da glaube ich Probleme mit DB bezügl. SQL-Injection oder so.

                        Nein, <?= steht seit Version 5.4 unabschaltbar zur Verfügung. […]

                        wuste ich nicht. Danke für di Info!!!

                        Der erzeugte HTML-Code ist nur bedingt lesbar, weil ein dem ?> nachfolgender Zeilenumbruch nicht in der Ausgabe erscheint, aber die Einrückungs-Whitespace-Zeichen.

                        habe ich mir gedacht!!! Bedankt

                        lgmb

                    2. Hallo MB,

                      mir ist es deutlich lieber wenn ich BuiltIn-Funktionen nehme

                      Du musst natürlich so programmieren, wie Du es am besten lesen und verstehen kannst.

                      Deswegen aber auf Helper-Funktionen zu verzichten und Megabytes an Boilerplate-Code zu tippen - ist das besser? Je mehr Code da steht, desto mehr musst Du lesen. Wenn der relevante Code aus ein paar Stecknadeln in Bergen von repetitivem Basiszeugs besteht, erschwert das die Fehlersuche ungemein. Sind nur meine 2ct dazu, die Entscheidung bleibt bei Dir. Ich werde dir - weil ich keine Akten über meine Gesprächspartner führe und sowas hier schnell wieder vergesse - vermutlich trotzdem regelmäßig dazu was erzählen wenn Du Codebeispiele bringst. Nimm's mir dann nicht übel 😉

                      Rolf

                      --
                      sumpsi - posui - clusi
                      1. moin,

                        mir ist es deutlich lieber wenn ich BuiltIn-Funktionen nehme

                        Du musst natürlich so programmieren, wie Du es am besten lesen und verstehen kannst.

                        da hast du natürlich recht. Es läst sich wohl nicht vermeiden aber da wo ich es vermeiden kann, werde ich nichtt drauf zurück greifen, esd sein den es ist im Templates üblich. Mit wachsendem Code wird es auf jeden Fall der Fall sein 😕.

                        Ich werde dir […] vermutlich trotzdem regelmäßig dazu was erzählen wenn Du Codebeispiele bringst. Nimm's mir dann nicht übel 😉

                        Ach iwo. Immer her damit.

                        lgmb

                2. Nun, wie wärs denn damit:

                  $t = new TableConstruktor( $tableprops );
                  $t->tr( $rowprops );
                  $t->tbody($options);
                  $t->th($opts);
                  $t->td($opts);
                  

                  Da hast Du alles in einer Klasse und ruft nur noch die Methoden auf! Wobei die übergebenen Options auch die Zelleninhalte und Attribute enthalten.

                  MFG

                3. Hier mal ein Beispiel was Du auch auf Deine Tabellenklasse anwenden kannst:

                  # Klasse zum Erstellen einer sitemap.xml /urlset
                  class Sitemap{
                      function __construct($opts){
                          $opts = array_merge( array(
                              'proto'    => '', 
                              'domain'   => '',
                              'version'  => '1.0',
                              'encoding' => 'UTF-8',
                              'xmlns'    => 'http://www.sitemaps.org/schemas/sitemap/0.9'
                          ), $opts);
                          $this->OPTS = $opts;
                          $this->URLSET = array();
                          $this->BUFFER = '';
                      }
                  
                      function add($url){
                          $this->URLSET[] = $url;
                      }
                  
                      function out(){
                          $this->BUFFER = sprintf("<?xml version='%s' encoding='%s'>\n", $this->OPTS['version'], $this->OPTS['encoding']);
                          $this->BUFFER .= sprintf("\t<urlset xmlns='%s'>\n", $this->OPTS['xmlns']);
                          $this->urlset();
                          $this->BUFFER .= "\t</urlset>";
                          return $this->BUFFER;
                      }
                      
                      function urlset(){
                          foreach( $this->URLSET as $url ){
                              $this->BUFFER .= sprintf("\t\t<url><loc>%s</loc></url>\n", $url);
                          }
                      }
                  }
                  
                  
                  
                  $s = new Sitemap( array( 
                      'proto' => 'https', 
                      'domain' => 'example.org' 
                  ));
                  
                  $s->add('/index.html');
                  $s->add('/foo.html');
                  $s->add('/find');
                  echo $s->out();
                  

                  Damit ist es einfach, weitere XML-Elemente einzubauen. OOP lebt von der Vielgestaltigkeit und umgekehrt.

                  MFG

                  1. Hallo pl,

                    Polymorphie? Wo? Ich sehe keine Klassenvererbung und keine Interfaces. Eins von beiden bräuchtest Du dafür schon.

                    Zum Code:

                    Du solltest deine Properties deklarieren.

                    URLSET, urlset und OPTS sollten private sein.

                    Nicht jede Variable muss ein Property sein. BUFFER sollte statt dessen lokale Variable sein und als Wert von urlset zurückgegeben werden. Das Ding ist temporär.

                    Die Namen einer öffentlichen Schnittstelle sollen so sprechend wie möglich sein. Sprechende Namen sind besser als jede Doku. Also - beispielsweise - toXmlString statt out, addUrl statt add, getUrlSetAsXmlFragment statt urlset und urlList statt BUFFER.

                    Wo findet der Kontextwechsel zu XML für die Zeichen &, ', ", < und > statt? Zumindest das & kann gern mal Teil einer URL sein, bei den vier anderen mag es weniger wichtig sein.

                    Die Parametrierbarbeit von UTF-8 und XML Version ist sinnlos. Sitemaps müssen UTF-8 codiert sein, und einen Sinn in einer XML 1.1 Sitemap sehe ich nicht, da kein Freitext enthalten sein darf und eine URL immer in XML 1.0 darstellbar ist.

                    Dass die Sitemap-Elemente lastmod, changefreq und priority nicht aufscheinen, ist sicherlich der Kürze des Beispiels geschuldet, oder?

                    Rolf

                    --
                    sumpsi - posui - clusi
                    1. Dass die Sitemap-Elemente lastmod, changefreq und priority nicht aufscheinen, ist sicherlich der Kürze des Beispiels geschuldet, oder?

                      Nein. Warum das fehlt hab ich doch geschrieben. MFG

                      1. Hallo pl,

                        mag sein. Aber nicht in diesem Thread.

                        Rolf

                        --
                        sumpsi - posui - clusi