MB: per Interace Datentyp definieren möglich?

moin community,

ist es möglich in PHP eigene Datentypen zu definieren? Ich kenns mit Klassen:

interface Bar {
  public function baz() : void;
}

class Foo implements IBar {
  publich function baz() : void {}
}

aber gibt es das man Datentypen definiert? z.B. IBar?

interface IBar {
  string baz;
}

class Foo {
  public function __construct( IBar bar ) : void {}
}

wenn ja, wie mache ich das bekannt?

Bei übergaben ist es für mich schon extrem hilfreich da ich von Natur aus sehr unachtsam bin.

vlg MB

  1. Tach!

    ist es möglich in PHP eigene Datentypen zu definieren?

    Abseits von Klassen, nein. Interfaces können nur Methoden und Konstanten, aber letztere nützen dir nichts.

    dedlfix.

    1. achja 😕, auf jeden Fall danke für den Hinweis. Wann wird es denn soweit sein, das PHP-Datentypen ein Interface verpasst bekommen? In NodeJS haben sie es ja super hin gekrigt: TypeScript! Interfaces, Generics, Ducktypes, etc. Wunderbar!!!!! Obwohl es ja Microsoft Ursprung hat. Und nochals danke Dir für die damaligen Erklärungen.

      vlg MB

      1. Tach!

        Wann wird es denn soweit sein, das PHP-Datentypen ein Interface verpasst bekommen?

        Ich würde nicht damit rechnen. PHP ist nun mal keine stark typisierte Sprache. Dass es überhaupt Type Hinting gibt, ist sozusagen schon ein Wunder.

        In NodeJS haben sie es ja super hin gekrigt: TypeScript!

        NodeJS ist reines Javascript und hat mit TypeScript nichts weiter zu tun (und mit Microsoft gar nichts). Verwechsel das nicht mit dem NPM-Repository, in dem einie Pakete stecken, die in TypeScript geschrieben sind oder TypeScript-Definitionsdateien haben, um die TypeScript-Vorteile nutzen zu können.

        Interfaces, Generics, Ducktypes, etc. Wunderbar!!!!! Obwohl es ja Microsoft Ursprung hat.

        Das ist nur für Außenstehende verwunderlich. Microsoft hat seit je her die Programmierer hofiert und ihnen das Leben leichter zu machen versucht. Die sind es schließlich, die durch ihre Programme die Notwendigkeit schaffen, dass man Windows als Grundlage braucht. Deshalb, dem Trend der Zeit hin SPAs folgens, war is nut logisch, dass sie etwas C#-ähnliches fürs Web unters Volk werfen.

        dedlfix.

    2. Nachtrag:

      Wie haben sich die PHP Framework Entwickler es denn anders überlegt um dennoch an eine Art Datentyp zu kommen? Ich erstell mit dieser Frage einen neuen thread.

      vlg MB

      1. Tach!

        Wie haben sich die PHP Framework Entwickler es denn anders überlegt um dennoch an eine Art Datentyp zu kommen?

        Du kannst ja nur Methoden im Interface definieren, also bleiben dir da nur Getter und Setter. Ich würde das aber nicht tun wollen. Ein Setter, der lediglich eine Variable schreibt und ein Getter, der nur den Variableninhalt ausliest bringt keine Vorteile. Im Gegenteil, da ist bei jedem Zugriff ein unnötiger Methodenaufruf abzuarbeiten.

        Nun wird bestimmt gleich jemand um die Ecke kommen und Kapselung und danach Konsistenz rufen. Da kann ich entgegnen, ein Getter-Setter-Paar, das lediglich 1:1 liest und schreibt, kapselt effektiv gar nichts. Eine private Eigenschaft ist damit genauso manipulierbar, wie wenn sie öffentlich zugänglich wäre. Die Sachlage wird jedoch dann anders, wenn der Lese- oder der Schreibzugriff durch das Nichtvorhandensen eines der beiden Getter/Setter verhindert wird, oder wenn Getter/Setter weitergehenden Code enthalten. Die Konsistenz-Fanatiker sagen dann, dass man den Zugriff gern einheitlich über Getter/Setter gesehen haben möchte, und nehmen dann stillschweigend eine schlechtere Performance in Kauf. Allerdings sollte man sich fragen, warum da überhaupt von anderen Objekten her ein Zugriff auf Eigenschaften notwendig ist, solange es sich nicht um reine Datenobjekte handelt (POPO, POCO, POJO, ...). Vielleicht sollte man lieber an der Architektur ansetzen, statt Ungünstiges mit noch mehr Ungünstigem anzureichern, nur damit es am Ende hübsch aussieht.

        dedlfix.

        1. moin dedlfix,

          Ok ich schreibs doch hier. Ich hab mal ein pseudo Datentyp Interface erstellt

          class DTDatabase {
            public $driver, $name, $host, $user, $password, $options;
            public function __construct( string $driver, string $name, string $host, string $user, string $password, array $options ) {
              $this->driver   = $driver;
              $this->name     = $name;
              $this->host     = $host;
              $this->user     = $user;
              $this->password = $password;
              $this->options  = $options;
            }
          }
          
          class Database {
            private $dns, $user, $password, $options;
            public function __construct( DTDatabase $db ) {
              $this->dns      = "{$db->driver}:dbname={$db->dbname};host={$db->host}";
              $this->user     = $db->user;
              $this->password = $db->password;
              $this->options  = $db->options;
          }
          
          /* Datatype Database */
          $tdb = new DTDatabase( 'mysql', 'cortex', 'localhost', 'root', '', [] );
          $db = new Database( $tdb );
          

          Dieses Konstrukt bringt meines erachtens Vorteile eines Datentyp Interface.

          • die parameter sind obligat
          • Kapselung

          kommt die Art auch in Frameworka Anwendung?

          vlg MB

          1. kommt die Art auch in Frameworka Anwendung?

            Nein in meinem nicht. Da rufe ich einfach eine Methode aus der Factory auf $this->dbh('myweb') und bekomme damit ein DataBaseHandle für die Datenbank mit dem Namen 'myweb'.

            Die hierzu benötigten Zugangsdaten (host, Port, user, pass) liegen in einer Konfigurationsdatei, die bekommt $this gar nicht erst zu Gesicht.

            Jedoch: Deine Idee, diese Konfiguration für Debugging-Zwecke mitzuschleifen ist gar nicht mal so schlecht @MB

            1. Hi pl,

              kommt die Art auch in Frameworka Anwendung?

              Nein in meinem nicht.

              Ok, ich will aber wissen ob diese Art von pseudo Datentypen sich anderen PHP-Frameworks bedienen.

              Jedoch: Deine Idee, diese Konfiguration für Debugging-Zwecke mitzuschleifen ist gar nicht mal so schlecht @MB

              Dankeschön fürs Lob pl :).

              vlg MB

              1. Ok, ich will aber wissen ob diese Art von pseudo Datentypen sich anderen PHP-Frameworks bedienen.

                Im Grunde genommen ist ja jede Klasseninstanz ein abstrakter Datentyp. Die Geschichte die dahinter steckt, wurde in Perl geschrieben: "Eine Klasseninstanz ist nichts weiter als eine Referenz die weiß zu welcher Klasse sie gehört weil sie mit dem Namen einer Klasse gesegnet wurde."

                PHP Entwickler kennen zwar keine bless()-Funktion aber der Dumper print_r() zeigt alle Klassennamen an, auch die der aggregierten Objekte:

                class Hlp{
                    protected $h;
                }
                
                
                class Fubar {
                  protected $foo;
                  protected $bar;
                
                    function __construct(){
                        $this->HLP = new Hlp;
                    }
                
                }
                
                
                $fb = new Fubar;
                
                print_r($fb);
                
                
                /*
                
                Fubar Object
                (
                    [foo:protected] => 
                    [bar:protected] => 
                    [HLP] => Hlp Object
                        (
                            [h:protected] => 
                        )
                
                )
                */
                
                1. Tach!

                  Im Grunde genommen ist ja jede Klasseninstanz ein abstrakter Datentyp. Die Geschichte die dahinter steckt, wurde in Perl geschrieben: "Eine Klasseninstanz ist nichts weiter als eine Referenz die weiß zu welcher Klasse sie gehört weil sie mit dem Namen einer Klasse gesegnet wurde."

                  Dass Perl hier Geschichte geschrieben haben soll, kann ich nicht nachvollziehen. Von einer Klasse abgeleitet zu sein, ist ein Merkmal objektorientierter Programmierung und in deutlich älteren Sprachen als Perl zu finden. Zudem geht Perl mit dem Blessing (nach der Instantiierung ein Paket an das Objekt binden) einen Weg, den man in von vorn herein objektorientiert angelegten Sprachen auch nicht findet.

                  dedlfix.

                  1. Zudem geht Perl mit dem Blessing (nach der Instantiierung ein Paket an das Objekt binden) einen Weg, den man in von vorn herein objektorientiert angelegten Sprachen auch nicht findet.

                    Perl war von Anfang an (also von vorn herein) objektorientiert. Es ist nur so, daß man als Anwender von Perl erst ab MR 5 (~1998) diese Objektorientierung kreativ nutzen konnte. Geblieben ist jedoch die Doppeldeutigkeit des Objekt-Begriffes, so gibt es Objekte als

                    1. Instanzen von Klassen
                    2. Abstrakte Datentypen die keine Instanzen sind

                    D.h., in Perl ist ein Objekt nicht zwangsläufig auch eine Instanz einer Klasse. In JavaScript übrigens auch nicht. MfG

          2. Tach!

            Ich hab mal ein pseudo Datentyp Interface erstellt

            Lass mal den Begriff Interface zum Beschreiben von Datenstrukturen weg (zumindest wenn es um PHP geht). Du kannst komplexe Daten auch als Klasse definieren, so wie du es hier getan hast. Ein Interface ist dafür nicht erforderlich. Mit einer Klasse entsteht auch ein Typ, wenn man so will.

            Jedoch könntest du ein Interface definieren mit getDSN() als Methode, die die Zugangsdatenklassen implementieren könnten. Die DSN-Strings sind DBMS-spezifisch und die Zusammensetzung könnte man Spezialisten überlassen, statt sie in eine allgemeine DBMS-Klasse zu tun.

            Dieses Konstrukt bringt meines erachtens Vorteile eines Datentyp Interface.

            Keine weiteren, als Klassen bereits haben.

            kommt die Art auch in Frameworka Anwendung?

            Klassen, um Daten zu beschreiben, kommen natürlich vor - auch ohne Framework.

            dedlfix.

  2. Hallo,

    was für einen Sinn hätte es denn, Attribute auf einem Interface zu definieren? Der Sinn von einem Interface ist es doch, dass es unterschiedliche Ausprägungen/Implementierungen des Interfaces geben kann. Wenn du aber reine Attribute definierst, dann macht eine zweite Implementierung gar keinen Sinn.

    Deswegen ist es m.E. korrekt, dass Interfaces primär Methoden anbieten. Wie dedlfix sagte, kannst du immernoch Attribute über Setter anbieten. Der Vorteil ist aber nun, dass du die eigentliche Implementierung des Setters austauschen kannst - z.B. eine Implementierung, bei der der Wert erst während des Aufrufs aus einer externen Datenquelle (File, Schnittstelle, Datenbank, ...) berechnet wird - das könntest du mit reinen Attributen einfach nicht.

    Ich habe mal einen "Mischbetrieb" (teils Attribute, teils Methoden) ignoriert.

    tl;dr: nutze Getter in echten Klassen für solche DTOs. Nutze Interfaces, wenn du unterschiedliche Implementierung anbieten willst. Diese sind i.d.R. für DTOs uninteressant.

    Viele Grüße Matti

    1. Tach!

      was für einen Sinn hätte es denn, Attribute auf einem Interface zu definieren? Der Sinn von einem Interface ist es doch, dass es unterschiedliche Ausprägungen/Implementierungen des Interfaces geben kann. Wenn du aber reine Attribute definierst, dann macht eine zweite Implementierung gar keinen Sinn.

      Achja, das vergesse ich auch immer wieder, weil C# es ermöglicht Propertys in Interfaces zu definieren. Aber genau genommen sind das ja auch nur Methodenaufrufe, die sich hinter dem Syntactic Sugar eines einfachen Variablenzugriffs verstecken.

      Kurz gefasst: Interfaces definieren Verhalten - eigentlich.

      Zu allem Überfluss kann man in TypeScript Interfaces auch mit allen Typen bestücken, nicht nur mit Funktionen. Und das ist gar nicht mal schlecht, ansonsten hätte man zum Definieren von komplexen Typen ein weiteres sprachliches Konstrukt nehmen müssen, das sich außer in diesem Punkt nicht weiter von Interfaces unterscheidet.

      dedlfix.

  3. Ausnahmsweise zitiere ich mal Wikipedia: Ein Abstrakter Datentyp (ADT) ist ein Verbund von Daten zusammen mit der Definition aller zulässigen Operationen, die auf sie zugreifen.

    Nun, Innerhalb einer Klasse sind es ja gerade die Getter und Setter womit man Operationen mit den Daten, die als Verbund in der Instanz vorliegen, tätigt. Jetzt nehmen wir einmal an, es gibt eine Klasse deren Instanz fürs Ausliefern einer Response von Type text/html zuständig ist.

    So könnten wir, in Anlehnung an https://forum.selfhtml.org/self/2017/aug/24/komplexen-view-rendern/1702332#m1702332 Methoden eines Interfaces definierten die nacheinander aufgerufen werden und verschiedene Template-Bereiche zum Leben erwecken:

    1. this->start_html() schreibt <doctype><head>..</head> in den Ausgabepuffer
    2. this->menu() erzeugt das Menu für die Seite
    3. this->bodybuild() rendert das restliche Template für den Body
    4. this->end_html() schließt die Seite und baut ggf. einen Fußmenu noch untendran

    Warum unsere Responseklasse ein Interface implementiert ist klar: Der Ablauf, siehe obenstehend ist für jede Response die als txt/html ausgeliefert wird, immer derselbe.

    Sind diese Interface-Methoden nun Getter? Ja, sie sind es! Denn sie greifen ja auf die Interna der KlassenInstanz. Somit implementiert eine Klassenerweiterung auch ein Interface -- nurmalso als praktisches Beispiel mit Erhalt der Kapselung.

    MfG schönen Sonntag.

    1. Tach!

      1. this->start_html() schreibt <doctype><head>..</head> in den Ausgabepuffer
      2. this->menu() erzeugt das Menu für die Seite
      3. this->bodybuild() rendert das restliche Template für den Body
      4. this->end_html() schließt die Seite und baut ggf. einen Fußmenu noch untendran

      Warum unsere Responseklasse ein Interface implementiert ist klar: Der Ablauf, siehe obenstehend ist für jede Response die als txt/html ausgeliefert wird, immer derselbe.

      Warum gibts dann nicht nur ein render(), das diesen Ablauf ausführt, wenn er immer wieder derselbe ist?

      Sind diese Interface-Methoden nun Getter? Ja, sie sind es! Denn sie greifen ja auf die Interna der KlassenInstanz.

      Nicht alles ist ein Getter, was ein Ergebnis liefert. Die Begriffe Getter und Setter verwendet man für Methoden, die den Zugriff auf eine einzelne Variable steuern. Die Beispiele von oben werden auf eine Vielzahl interner Variablen zugreifen und mehr oder weniger kompelxe Berechnungen anstellen und fallen damit nicht mehr unter die übliche Definition von Gettern.

      dedlfix.

      1. Nicht alles ist ein Getter, was ein Ergebnis liefert.

        So isses. Wenn nämlich das Request-Objekt feststellt, dass Parameter im Request sind

        if( $this->param() ){ 
           # Benutzereingabe, Controller wird aufgerufen
           # control() ist eine Interface Methode
           $this->control();
        }
        else{ 
           # Darstellung der Antwortseite 
           # wenn keine Parameter im Request sind
           $this->browse(); 
        }
        

        ergibt sich eine Verzweigung und somit ist control() ein Setter der das Ergebnis einer Eingabe in eine ganz bestimmte Datenstruktur (abstrakter Datentype) setzt. browse() kann übrigens auch ein setter sein, der könnte bspw. eine DB-Afrage in den STASH (Datenversteck) setzen. $this->STASH ist ein abstrakter Datentyp der so beschaffen ist, daß er unmittelbar an die render()-Methode der TemplatingEngine übergeben werden kann. Und letztere rendert dann z.B. eine mehrzeilige wie mehrspaltige Tabelle oder eine Fehlermeldung ins Ausgabe-Template.

        MfG

        1. Tach!

          Nicht alles ist ein Getter, was ein Ergebnis liefert.

          So isses. Wenn nämlich das Request-Objekt feststellt, dass Parameter im Request sind [...] ergibt sich eine Verzweigung und somit ist control() ein Setter der das Ergebnis einer Eingabe in eine ganz bestimmte Datenstruktur (abstrakter Datentype) setzt.

          Das entspricht nur nicht dem, wofür sich der Begriff Setter etabliert hat.

          dedlfix.

          1. Das entspricht nur nicht dem, wofür sich der Begriff Setter etabliert hat.

            Vergiß Wikipedia. Hier gehts ja auch nicht explizit um Getter/Setter sondern um die Frage ob es möglich ist mit Interface-Methoden auf abstrakte Datentypen zuzugreifen.

            Und das ist eine sehr sehr praktische Frage die @MB da in den SELF-Raum gestellt hat!

        2. Nicht alles ist ein Getter, was ein Ergebnis liefert.

          So isses. Wenn nämlich das Request-Objekt feststellt, dass Parameter im Request sind

          if( $this->param() ){ 
             # Benutzereingabe, Controller wird aufgerufen
             # control() ist eine Interface Methode
             $this->control();
          }
          else{ 
             # Darstellung der Antwortseite 
             # wenn keine Parameter im Request sind
             $this->browse(); 
          }
          

          ergibt sich eine Verzweigung und somit ist control() ein Setter der das Ergebnis einer Eingabe in eine ganz bestimmte Datenstruktur (abstrakter Datentype) setzt. browse() kann übrigens auch ein setter sein, der könnte bspw. eine DB-Afrage in den STASH (Datenversteck) setzen. $this->STASH ist ein abstrakter Datentyp der so beschaffen ist, daß er unmittelbar an die render()-Methode der TemplatingEngine übergeben werden kann. Und letztere rendert dann z.B. eine mehrzeilige wie mehrspaltige Tabelle oder eine Fehlermeldung ins Ausgabe-Template.

          MfG

          PS:

          Die Beispiele von oben werden auf eine Vielzahl interner Variablen zugreifen und mehr oder weniger kompelxe Berechnungen anstellen und fallen damit nicht mehr unter die übliche Definition von Gettern.

          Es gibt sogar Setter die verändern sämtliche Eigenschaften einer Instanz, weil die Veränderung einer einzigen Eigenschaft keinen Sinn ergibt. Z.B. wäre es in einem Datum-Objekt unsinnig, nur den Julianischen Tag zu ändern OHNE sämtliche anderen Eigenschaften (Tag, Monat, Jahr) neu zu berechnen, denn das muß ja am Ende zusammenpassen.

          Die Wikipedia Definition von Getter/Setter finde ich daher eher armselig, weil ein praktischer Nutzen nun doch ein bischen mehr voraussetzt als das Was Wikipedia schreibt.

          1. Hallo pl,

            die englische Wikipedia formuliert es etwas direkter:

            In computer science, a mutator method is a method used to control changes to a variable. They are also widely known as setter methods.

            Nicht jede Methode, die einen Parameter bekommt und daraufhin was am Zustand des Objekts verändert, ist ein Setter.

            $this->control() ist schon mal gar keiner, weil dieser Methode ein wesentliches Element eines Setters fehlt: der Parameter mit dem Wert, der in einer Eigenschaft des Objekts zu speichern ist.

            Schlüsselbegriff ist: "control changes". Kontrolle bedeutet: Validierung von Werten, und Benachrichtigung interessierter Beobachter. Von meiner Erwartung her ist auch der Singular ein wesentliches Element der Definition: "a variable", nicht "variables of an object".

            Dass der Aufruf eines Setters Auswirkungen auf andere Properties haben kann, spricht da nicht gegen. Dein Beispiel eines Date-Objekts ist ideal dafür. De facto speichert dieses Objekt einen Zeitpunkt und Properties wie Tag, Wochentag oder julianisches Datum sind Sichten auf diesen Zeitpunkt. Ob das Objekt die Werte dieser Sichten intern cached oder den Wert bei jedem Aufruf berechnet, ist ein Implementierungsdetail. Fachlich repräsentiert das Date-Objekt genau einen Wert: ein bestimmtes Datum (oder Timestamp, je nach Implementierung). Wenn diese Sichten-Properties Setter haben, wird nicht das Property geändert, sondern der gespeicherte Zeitpunkt. Und es gibt einige interessierte Beobachter, z.B. wenn ich das julianische Datum schreibe, sind die Properties Tag, Monat und Jahr brennend an diesem Update interessiert.

            $this->control() und $this->browse() sind demnach ganz normale Methode, die dem Objekt den Auftrag geben, die Benutzereingabe zu übernehmen oder die Eingabeseite aufzubereiten. Das werden sie mit Hilfe eines Schwarms von Helper-Objekten tun, und es geht über einen Setter weit hinaus.

            Eine Methode $this->acceptFormData($formData), die einen Satz Formulareingaben entgegennimmt und im Objekt speichert, würde ich demnach auch nicht als setter verstehen. Es ist ein Helper, der einen Datensatz ins Objekt übernimmt. Im Sinne von SRP wird dieser Helper nicht mehr tun, als diesen Ablauf zu orchestrieren, und dabei etliche Setter aufrufen, die nicht mehr tun, als die Werte zu speichern und ggf. ein ModelChanged-Event zu feuern.

            Rolf

            --
            Dosen sind silbern
            1. $this->control() und $this->browse() sind demnach ganz normale Methode, die dem Objekt den Auftrag geben, die Benutzereingabe zu übernehmen oder die Eingabeseite aufzubereiten. Das werden sie mit Hilfe eines Schwarms von Helper-Objekten tun, und es geht über einen Setter weit hinaus.

              In meinem FW sind das Methoden eines Interface die über die Framework-Instanz aufgerufen werden. Diese Instanz aggregiert z.B. eine Instanz der Klasse xCGI und delegiert die param()-Methode der Klasse xCGI in eine eigene Methode die denselben Namen hat. Das befähigt die FW-Instanz zum Lesen sämtlicher Parameter welche der Request mit sich bringt.

              Mitnichten also ein "Schwarm von Helper Objekten".

              Interessant zu dieser Helper-Geschichte übrigens ist die Fabrikmethode insbesondere wenn Helper zur Laufzeit nachgelagert aggregiert werden. Das könnte z.B. beim ersten Aufruf von $this->param() erfolgen ist aber nicht wirklich sinnvoll, weil $this->param() ohnehin bei jedem Request aufgerufen wird.

              $this->control() ist schon mal gar keiner, weil dieser Methode ein wesentliches Element eines Setters fehlt: der Parameter mit dem Wert, der in einer Eigenschaft des Objekts zu speichern ist.

              Die Parameter kommen aus dem Request und wie weiter oben dargelegt, ist ja $this in der Lage Request-Parameter zu parsen bzw. entgegenzunehmen. Z.B. Benutzername und ein Passwort und wenn das valide ist werden Name der Gruppe, Benutzername und Zeitpunkt der Anmeldung in der Eigenschaft $this->SESSION gespeichert. Ein Helper hierzu ist $this->DAL der jedoch erst zur Laufzeit aggregiert wird (Data Access Layer, Benutzername, Gruppe, Passwort müssen ja persistent hinterlegt sein).

              Eine dedizierte Methode um Daten in das assoziative Array $this->SESSION einzutragen ist nicht notwendig, das findet innerhalb der control()-Methode statt. Und das ist auch völlig legitim, denn innerhalb dieser Methode ist $this (die FW Instanz) mit sämtlichen Eigenschaften verfügbar.

              Und überhaupt empfangen meine Interface-Methoden keine Parameter über den Methodenaufruf. Sie werden ganz und gar erst aufgerufen wenn die existieren, also z.B. so $this->execute('control') (ein wrapper die die Existenz einer Methode feststellt) da ist es gar nicht möglich, weitere Argumente zu übergeben.

              Und wie ich schrieb, die Begriffe Getter/Setter sind für mich keine dogmatische sondern eine praktisch latente Angelegenheit. Ich persönlich finde es auch übertrieben, bei jedem instanzverändernden Vorgang "den Setter" definieren zu wollen. So ist der Aufruf $this->dd() ein Dump&Die Getter und wäre mit

              $this->dd( nobin => 1 ) formal ein Setter weil mit diesem Schalter der Dump der gesamten Konfiguration unterdrückt wird. Diese liegt in $this->BIN und mit dem Schalter nobin => 1 wird die ganz einfach gelöscht weil sie nach dem Aufruf von Dump&Die sowieso nicht mehr gebraucht wird ($this ist danach tot).

              Man könnte ja auch die Instanz für eine eigene Implementierung eines Dumpers rekursiv duchlaufen wobei die Instanz nicht verändert sondern nur die Daten kopiert werden -- man kanns auch übertreiben 😉

              MfG

            2. Eine Methode $this->acceptFormData($formData), die einen Satz Formulareingaben entgegennimmt und im Objekt speichert, würde ich demnach auch nicht als setter verstehen. Es ist ein Helper, der einen Datensatz ins Objekt übernimmt. Im Sinne von SRP wird dieser Helper nicht mehr tun, als diesen Ablauf zu orchestrieren, und dabei etliche Setter aufrufen, die nicht mehr tun, als die Werte zu speichern und ggf. ein ModelChanged-Event zu feuern.

              Characteristisch für SRP (Single-Responsibility-Prinzip) ist, die Methoden der Helper zu Methoden der eigenen Instanz zu machen. Wie ich weiter oben schon darlegte, ist $this->CGI

              1. ein Helper
              2. ein aggregiertes Objekt
              3. eine Instanz einer anderen, nicht verwandten Klasse

              und das bedeutet, daß diese Instanz mit Methoden seiner, also einer anderen Klasse daherkommt. Diese Methoden werden zu eigenen Methoden indem sie delegiert werden.

              Analog verfährt SRP mit den Eigenschaften. Der Aufruf einer delegierten Methode $this->param() hat zur Folge, das Formulareingaben zunächst im aggregierten Objekt $this->CGI gespeichert werden. Natürlich muß, um diese Daten, SRP folgend, in die eigene Eigenschaft $this->STASH zu bekommen ein weiterer Transfer stattfinden der ganz einfach nur die Methoden oder allg. die dafür zuständige Schnittstelle nutzt die das aggregierte Objekt hierzu bereitstellt.

              Idealerweise sind die Namen der in $this->STASH für die TE bereitgestellten Platzhalter dieselben wie sie als name= Attribut im Formular vergeben wurden. In PHP würde es vollkommen ausreichen, das GET- oder POST-Array in eine Eigenschaft der eigenen Instanz zu kopieren um SRP Genüge zu tun. MfG

            3. Dein Beispiel eines Date-Objekts ist ideal dafür. De facto speichert dieses Objekt einen Zeitpunkt und Properties wie Tag, Wochentag oder julianisches Datum sind Sichten auf diesen Zeitpunkt. Ob das Objekt die Werte dieser Sichten intern cached oder den Wert bei jedem Aufruf berechnet, ist ein Implementierungsdetail.

              Anders als bei einem Objekt, was bspw. eine Person (Name, Vorname, Alter usw.) beschreibt, sind sämtliche Eigenschaften eines Datum-Objekts voneinander abhängig. Da ist es schon im Sinne konsistenter Daten, unmittelbar nach Änderung einer einzelnen Eigenschaft, z.B. nach einer Inkrementoperation $date++ auch alle anderen Eigenschaften sofort neu zu berechnen.

              Also für mich ist das keine Frage eines Implementierungsdetails sondern eine Frage der Zweckmäßigkeit.

              MfG

      1. this->start_html() schreibt <doctype><head>..</head> in den Ausgabepuffer
      2. this->menu() erzeugt das Menu für die Seite
      3. this->bodybuild() rendert das restliche Template für den Body
      4. this->end_html() schließt die Seite und baut ggf. einen Fußmenu noch untendran

      Finde ich vom Ansatz schon eher schräg. Warum sollte man Programmroutinen am zu erzeugenden Markup orientieren?! Wie dedlix schon schrieb: "render" und gut ists. Welche Variablen an welcher Stelle mit welchem Markup ausgegeben werden sollte das Template bestimmen.

      1. Warum sollte man Programmroutinen am zu erzeugenden Markup orientieren?!

        Das tun meine Methoden nicht. Sie laden höchstens ein bestimmtes Template und welches das ist, bestimmt entweder die Konfiguration oder die Klasse. Vielmehr definieren die Methoden den Zugriff auf die in das Template zu rendernden Daten (die in einem abstrakten Datentyp vorliegen). Auch die Konfiguration ist ein abstrakter Datentyp.

        Wie dedlix schon schrieb: "render" und gut ists.

        Nein. Code wird aufgeteilt, schon allein der Übersicht wegen. Beispielsweise greift menu() auf eine Array-Referenz, eine einzelne Eigenschaft der Instanz. start_html() hingegen kann den Inhalt auch von einer per Konfiguration festgelegten anderen Methode laden.

        Jede Methode gibt seinen Return-String sukzessive in den Ausgabepuffer. So kann jede Methode auch recht einfach mit einem '' beendet werden, falls das so konfiguriert ist.

        Insbesondere beim multi-Domain-Betrieb kann es ja auch erhebliche Abweichungen im Layout geben was ebenfalls für mehr Code-Strukturierung spricht. Und Template-Bereiche bestimmten Methoden zuzuordnen habe ich auch in anderen Framewoks gesehen z.B. bei Magento.

        Welche Variablen an welcher Stelle mit welchem Markup ausgegeben werden sollte das Template bestimmen.

        Ja natürlich was denn sonst.

  4. Moin,

    es gibt, in PHP wie in Perl eine Möglichkeit, Variablen (abstrakte Datentypen) an eine Klasse zu binden. Eine solche Bindung bewirkt, dass jeder Zugriff auf eine solche Variable zum Aufruf einer Funktion führt.

    Beispielsweise hast Du ein assoziatives Array, das ist anfangs leer. Nun fragt jemand die Daten ab, die zum Schlüssel '/foo.html' gehören. Die dazugehörige Funktion wird aufgerufen, beschafft die zum Schlüssel '/foo.html' gehörigen Daten und liefert diese zurück.

    Ebenso ist dem schreibenden Zugriff eine bestimmte Funktion zugeordnet, die bspw. die Daten persistent macht. Soll ein bestimmter Schlüssel gelöscht werden, wird wiederum eine andere Funktion aufgerufen.

    Und schließlich gibt es auf Funktionen bzw. Methoden mit denen über das Array iteriert werden kann. Aus alldem ergeben sich vielfältige Anwendungsmöglichkeiten, guck Dir das einfach mal an.

    MfG

    1. Tach!

      es gibt, in PHP wie in Perl eine Möglichkeit, Variablen (abstrakte Datentypen) an eine Klasse zu binden. Eine solche Bindung bewirkt, dass jeder Zugriff auf eine solche Variable zum Aufruf einer Funktion führt.

      Darf ich um ein Code-Beispiel bitten oder wenigstens um die Nennung der PHP-Namen der dazu notwendigen Dinge.

      [Der Rest vom Posting]

      ... hört sich nach einem Gebilde an, dass wie ein assoziatives Array funktioniert, also Werte unter beliebigen Schlüsseln ablegen kann, aber einige Magic Methods von PHP sowie Predefined Interfaces nutzt, um auf die tatsächlich irgendwo anders abgelegten Daten zuzugreifen. Das ist ja alles schön und gut, aber inwiefern definiert das einen Datentyp im Sinne der Fragestellung?

      Bei übergaben ist es für mich schon extrem hilfreich da ich von Natur aus sehr unachtsam bin.

      Das deutet darauf hin, dass die Eigenschaftennamen vom Autor festgelegt werden sollen und nicht beliebig vom Verwender benannt sein dürfen. Selbst ein einfaches assoziatives Array stünde dem entgegen. Welche Vorteile und Nachteile gegenüber einer einfache Klasse mit vordefinierten Eigenschaften siehst du bei deinem Vorschlag?

      dedlfix.

      1. Welche Vorteile und Nachteile gegenüber einer einfache Klasse mit vordefinierten Eigenschaften siehst du bei deinem Vorschlag?

        Die Anwendung greift direkt auf den abstrakten Datentyp während dieser, für den Anwender unsichtbar, Methoden eines Interfaces aufruft.

        Anstatt also in der Anwendung bspw. eine Methode einer vorher erstellten Instanz aufzurufen die ein Insert Statement ausführt, greift die Anwendung ohne diesem Umweg direkt in ein Array: Aus der Sicht der Anwendung werden Daten in ein Array eingefügt, die an das Array gebundene Klasse jedoch fügt Daten in eine Datenbank ein.

        Schichtenmodelle lassen sich auf diese Art und Weise recht einfach implementieren und auch anwenden. Das Array $_SESSION was jeder PHP Entwickler kennen dürfte ist ein Beispiel. Die Anwendung greift ins Array, die Persistierung jedoch ist komplett raus aus der Anwendung, d.h. um den Dateizugriff, Serialisierung usw. muss sich die Anwendung gar nicht kümmern. Praktisch implementiert $_SESSION ein Random Access File.

        Etwas abgedreht war ein FTP Client den ich in Perl mal entwickelt habe. Da hast Du in der Anwendung nur noch ein $ftpclient = $file aufrufen müssen um die Datei auf den Server hochzuladen und ein $localfile = $ftpclient zum Download, gleichzeitig wurden Mails verschickt an einen konfigurierbaren Verteiler. Über die an $ftpclient gebundene Klasse war die diesbezügliche Konfiguration einschließlich Access Control komplett von der Anwendung getrennt.

        Eine einfache Zuweisungsoperation kann also sehr komplexe Vorgänge auslösen deren diesbezüglicher Code aus der eigenen Anwendung raus ist und beispielsweise den Aufruf mehrerer Methoden innerhalb der Anwendung erspart.

        MfG

      2. Darf ich um ein Code-Beispiel bitten oder wenigstens um die Nennung der PHP-Namen der dazu notwendigen Dinge.

        ArrayAccess, ArrayObject, Iterator.. nachgereicht 😉