Take: OOP (in PHP) - 3 Fragen

Moin allerseits,

ich entdecke gerade anhand dieses sehr guten Tutorials von Peter Kropff die tolle Welt von OOP in PHP. Das Problem was ich jetzt habe ist, dass in dem Text zwar alles super erklärt wird, aber wie man jetzt konkrete Probleme mit OOP umsetzt, steht eben nicht drin. Wie ihr vielleicht aus meinem letzten Thread wisst, bastle ich (immernoch...) an einem mehr oder weniger umfangreichen, CMS-artigen Dingens. Jetzt zu meinen 3 Fragen:

  • Ich habe eine Klasse "page", die die Verwaltung der Seiten übernimmt. Diese hat verschiedene Methoden, unter anderem "load", mit der man eine bestimmte Seite in den Speicher lädt. Wo aber gehört denn die Funktion (ich meine nicht "Subroutine") zum Auslesen der Seite aus den GET-Parametern hin? In die Klasse? Oder einfach außerhalb in das Script?

  • Und wo packt man die Umleitung auf Fehlerseiten hin? Sollte die Klasse selbstständig bei Fehlern statt der gewünschten die Fehlerseite laden, oder sollte sie nur einen Fehler zurückgeben und das Hauptprogramm lässt dann die Klasse die Fehlerseite laden?

  • Die letzte Frage, die eigentlich aus den anderen beiden hervorgeht ist, ob man sowas wie Session-Initialisierung, erstellen der page-Instanz, usw. in die Klasse CMS* als Methode packen sollte, die dann im Hauptprogramm aufgerufen wird, oder ob das einfach im Hauptprogramm stehen sollte.

Hoffe ihr versteht mich und könnt mich aufklären!

Danke schonmal und Gruß,
Take

* Diese Klasse stellt ein paar statische Methoden zur Verfügung, die die eigentliche Funktionalität des CMS ausmachen: Dateien mit gleichzeitigem Locking laden und dann das enthaltene JSON parsen und zurückgeben, etc. etc.

  1. Moin,

    und was mir jetzt schon wieder auffällt, und was ich schon immer wieder fragen wollte: Wie kann man eigentlich hier im Forum Threads per _E-Mail_ abonnieren? Die Links für RSS und Atom stehen ja oben drüber, aber nichts zu E-Mail und in der Hilfe steht nur, dass es geht, aber nicht wo...

    Gruß,
    Take

  2. hi,

    • Ich habe eine Klasse "page", die die Verwaltung der Seiten übernimmt. Diese hat verschiedene Methoden, unter anderem "load", mit der man eine bestimmte Seite in den Speicher lädt. Wo aber gehört denn die Funktion (ich meine nicht "Subroutine") zum Auslesen der Seite aus den GET-Parametern hin? In die Klasse? Oder einfach außerhalb in das Script?

    Vorschlag:
    load.php ist ein eigenständiges Script, sozusagen der Handler für jeden Request und auf den wird alles per rewrite umgeschossen.

    Du sprichst von einer "Verwaltung der Seiten", das passt, gute Idee! Geht dann wie folgt weiter: load.php kriegt den Request, ermittelt aus REQUEST_URI den PATH und fragt in der Verwaltung nach, ob es den gibt. Wenn ja, wird ein Response-Object (Instanz Deiner Klasse) erstellt, wenn nein, ein 404 geworfen.

    Im Ja-Zweig gehts dann so weiter: Das Response-Object ruft nun die Methoden auf, welche die Seite erzeugen. Eine weitere Kontrolle prüft, ob Parameter anliegen. Somit können nun alle möglichen Content-Types ausgegeben oder ein Template bestückt und ausgegeben werden.

    • Und wo packt man die Umleitung auf Fehlerseiten hin?

    Keine Umleitung. Entweder wird eine fehlerhafte Eingabe über einen festen Platzhalter im Template geführt oder der Body wird komplett gegen eine Fehlerseite ausgetauscht. Umleitungen sind Mist, das verwirrt den Besucher und erschwert Deine eigene Fehlersuche.

    dann die Klasse die Fehlerseite laden?

    Mach es nicht so kompliziert. Jede Fehlerbehandlung kann über das Response-Object (Instanz Deiner Klasse) abgewickelt werden.

    • Die letzte Frage, die eigentlich aus den anderen beiden hervorgeht ist, ob man sowas wie Session-Initialisierung,

    Wenn Du eine Verwaltung hast, haben die Response-Objecte auch Attribute. Eins davon legt fest, ob Session oder nicht Session.

    * Diese Klasse stellt ein paar statische Methoden zur Verfügung, die die eigentliche Funktionalität des CMS ausmachen: Dateien mit gleichzeitigem Locking laden und dann das enthaltene JSON parsen und zurückgeben, etc. etc.

    Ich mache das mit Templates. Beispiel:
     /foo.html
    ist ein Formular mit dem Platzhalter %result fürs Ergebnis. Der Controler (Methode Deiner Klasse CMS) prüft den Request, der kann vom Submit (S) kommen oder es ist Ajax (A).

    Submit) das Template wird neu geladen %result mit dem Ergebnis bestückt
    Ajax) es wird nur das Ergebnis als JSON gesendet und per DOM das %result beschrieben

    Hotti

    1. Moin,

      Vorschlag:
      load.php ist ein eigenständiges Script, sozusagen der Handler für jeden Request und auf den wird alles per rewrite umgeschossen.

      Ja klar, so läuft das doch: Es gibt eine index.php und die kriegt den GET-Parameter page der angibt, welche Seite geladen werden soll. Per mod_rewrite täusche ich dann eine normale Dateistruktur vor.

      Du sprichst von einer "Verwaltung der Seiten", das passt, gute Idee! Geht dann wie folgt weiter: load.php kriegt den Request, ermittelt aus REQUEST_URI den PATH und fragt in der Verwaltung nach, ob es den gibt. Wenn ja, wird ein Response-Object (Instanz Deiner Klasse) erstellt, wenn nein, ein 404 geworfen.

      Das ist ja meine Frage 2: Wo geschieht das alles? Innerhalb einer Klasse? Klatsche ich das jetzt einfach als Code in eine .php?

      Im Ja-Zweig gehts dann so weiter: Das Response-Object ruft nun die Methoden auf, welche die Seite erzeugen. Eine weitere Kontrolle prüft, ob Parameter anliegen. Somit können nun alle möglichen Content-Types ausgegeben oder ein Template bestückt und ausgegeben werden.

      Alles kein Problem.

      Keine Umleitung. Entweder wird eine fehlerhafte Eingabe über einen festen Platzhalter im Template geführt oder der Body wird komplett gegen eine Fehlerseite ausgetauscht. Umleitungen sind Mist, das verwirrt den Besucher und erschwert Deine eigene Fehlersuche.

      Das war glaub ich ein bisschen unglücklich ausgedrückt. Was ich meinte war: Die Klasse "page" versucht die angeforderte Seite zu laden (Überprüft ob die Datei existiert). Was tut sie, wenn die Datei nicht existiert? Automatisch die 404-Seite laden? Oder einfach nur false zurückgeben und irgendwo anders (wo?) wird dann ermittelt, dass die 404-Seite im Falle einer nicht gefundenen Seite geladen werden muss und der Klasse gesagt, sie solle jetzt die 404-Seite laden.

      dann die Klasse die Fehlerseite laden?

      Mach es nicht so kompliziert. Jede Fehlerbehandlung kann über das Response-Object (Instanz Deiner Klasse) abgewickelt werden.

      Ich machs nicht kompliziert, ich hab nur gefragt, weil ich mir nicht sicher war: Lädt die Klasse "page" automatisch eine Fehlerseite, oder wird ihr das von außen gesagt?

      • Die letzte Frage, die eigentlich aus den anderen beiden hervorgeht ist, ob man sowas wie Session-Initialisierung,

      Wenn Du eine Verwaltung hast, haben die Response-Objecte auch Attribute. Eins davon legt fest, ob Session oder nicht Session.

      Was meinst du mit "Verwaltung"?

      * Diese Klasse stellt ein paar statische Methoden zur Verfügung, die die eigentliche Funktionalität des CMS ausmachen: Dateien mit gleichzeitigem Locking laden und dann das enthaltene JSON parsen und zurückgeben, etc. etc.

      Ich mache das mit Templates. Beispiel:
      /foo.html
      ist ein Formular mit dem Platzhalter %result fürs Ergebnis. Der Controler (Methode Deiner Klasse CMS) prüft den Request, der kann vom Submit (S) kommen oder es ist Ajax (A).

      Submit) das Template wird neu geladen %result mit dem Ergebnis bestückt
      Ajax) es wird nur das Ergebnis als JSON gesendet und per DOM das %result beschrieben

      Das beantwortet jetzt glaub ich indirekt meine Frage: Es gibt einen Controller, der eben alles "kontrolliert". Der wird einfach am Anfang aufgerufen und macht dann den Rest. Im Hauptscript steht also nichts, außer dem Controlleraufruf, oder?
      Das mit den Templates hab ich mir auch schon so vorgestellt, wie du es beschreibst. Aber ist "submit" hier nicht der falsche Begriff? Ich würd es eher als Request bezeichnen, wobei Ajax ja auch ein Request ist... Aber müsste dieses Überprüfen ob Ajax oder "normaler" Request, nicht eher Teil einer Extra-Klasse "template" sein, die das laden und bestücken der Templates übernimmt?
      Insgesamt, merke ich jetzt, ist das Thema echt komplex. Man könnte jetzt seine alten nicht-OOP-Scripte einfach so hinbiegen, aber dann hat man das OOP ja nur um des OOP Willen und nutzt die Vorteile gar nicht (bzw. kann es nicht). Viel zu lernen... Gibts da evtl. Literaturempfehlungen (Deutsch wäre schön :D)?

      Gruß,
      Take

      1. Hi!

        Du sprichst von einer "Verwaltung der Seiten", das passt, gute Idee! Geht dann wie folgt weiter: load.php kriegt den Request, ermittelt aus REQUEST_URI den PATH und fragt in der Verwaltung nach, ob es den gibt. Wenn ja, wird ein Response-Object (Instanz Deiner Klasse) erstellt, wenn nein, ein 404 geworfen.

        Das ist ja meine Frage 2: Wo geschieht das alles? Innerhalb einer Klasse? Klatsche ich das jetzt einfach als Code in eine .php?

        Besser nicht. Damit ignorierst du schon das simple Trennungsmuster nach dem EVA-Prinzip und erzeugst dir eine komplexe Riesenklasse, die sich mehr oder weniger nur mit Fallunterscheidungen zum Ende hin durchschlängelt. Dann hast du von den Vorteilen der OOP nur ganz wenige genutzt und stattdessen irgendwas zwischen "unstrukturierter Ablaufsteuerung" und "strukturierter und funktionsbasierter Steuerung" im OOP-Gewande erstellt.

        Im Ja-Zweig gehts dann so weiter: Das Response-Object ruft nun die Methoden auf, welche die Seite erzeugen. Eine weitere Kontrolle prüft, ob Parameter anliegen. Somit können nun alle möglichen Content-Types ausgegeben oder ein Template bestückt und ausgegeben werden.
        Alles kein Problem.

        Das wird ein Problem, weil das Response/Page-Objekt so etwas wie ein fettes switch oder ein schlankes mit einer Menge spezialisierter Methoden braucht, um die jeweiligen Daten zu ermitteln. Zumindest ist das so, wenn es nur eine Response/Page-Klasse gibt und nicht für jeden unterschiedlichen Request eine eigene.

        Die Klasse "page" versucht die angeforderte Seite zu laden (Überprüft ob die Datei existiert). Was tut sie, wenn die Datei nicht existiert? Automatisch die 404-Seite laden? Oder einfach nur false zurückgeben und irgendwo anders (wo?) wird dann ermittelt, dass die 404-Seite im Falle einer nicht gefundenen Seite geladen werden muss und der Klasse gesagt, sie solle jetzt die 404-Seite laden.

        Schon der Front-Controller findet heraus, ob er die Anfrage zum Abarbeiten weiterleiten kann oder nicht. Wenn nicht, gibt er die 404er-Antwort direkt zurück. Dazu kann er sich ruhig eines 404er Templates bedienen. Aber Umleiten ist definitiv Mist - schon aus HTTP-Sicht. Eine Umleitung ist Status-Code 30x und die Fehlerseite ist nicht 404 sondern 200. Anderenfalls weiß der Client nicht, ob das eigentliche Ziel nicht vorhanden ist oder das Umleitungsziel. Und bei 200 geht er sowieso von "alles ok" aus.

        Ich machs nicht kompliziert, ich hab nur gefragt, weil ich mir nicht sicher war: Lädt die Klasse "page" automatisch eine Fehlerseite, oder wird ihr das von außen gesagt?

        Siehe meine Antwort. Meiner Meinung nach solltest du die Aufgaben deutlicher trennen und die Zuständigkeiten der einzelnen Klassen überschaubar klein halten.

        Das beantwortet jetzt glaub ich indirekt meine Frage: Es gibt einen Controller, der eben alles "kontrolliert". Der wird einfach am Anfang aufgerufen und macht dann den Rest. Im Hauptscript steht also nichts, außer dem Controlleraufruf, oder?

        Ja, aber der macht nicht den Rest, der delegiert den Rest an die jeweiligen Spezialisten weiter.

        Aber müsste dieses Überprüfen ob Ajax oder "normaler" Request, nicht eher Teil einer Extra-Klasse "template" sein, die das laden und bestücken der Templates übernimmt?

        Bei Frameworks nach dem MVC-Muster gibt es Action-Controller, die für ein bestimmtes Aufgabengebiet zuständig sind. Die einzelnen Requests dieses Aufgabengebiets landen vom Front-Controller und seinen Vasallen (Router) ermittelt bei einer der Action-Methoden des Controllers. Ajax-Requests und herkömmliche Request sind unterschiedlich abzuarbeiten und landen auf eigenen Action-Methoden. Sie können zwar die gleichen Verarbeitungen erledigen / die gleichen Daten holen, das machen sie aber, indem sie sich desselben Models bedienen. Die Antwort ist jedoch spezifisch und wird der einen oder der anderen View zum Formatieren übergeben. Am Ende der Action-Methode ist allerdings in guten Framworks nur bekannt, welche View gewählt wurde und welche Daten übergeben werden sollen, die View aber hat noch nicht gearbeitet. Wenn erst der Front-Controller das Rendern anstößt hat auch noch ein Vor-der-View-Plugin die Chance den Rendering-Prozess zu beeinflussen. Vielleicht ist das in deinem Fall YAGNI, doch wenn dein Framework universal einsetzbar sein soll, sollten auch solche Anwendungsfälle berücksichtigt werden können.

        Viel zu lernen... Gibts da evtl. Literaturempfehlungen (Deutsch wäre schön :D)?

        Garantiert gibt es Bücher, die ein bestimmtes Framework beschreiben, vielleicht auch allgemeine zum Thema. Und die Muttersprache liest sich im Allgemeinen auch flüssiger. Doch früher oder später wirst du an Englisch nur vorbeikommen, wenn du dir ein anderes Hobby suchst. Also nutze lieber die Gelegenheiten mit der Originaldokumentation auch deine Lesefähigkeiten zu verbessern.

        Lo!

      2. Moin,

        Du sprichst von einer "Verwaltung der Seiten", das passt, gute Idee! Geht dann wie folgt weiter: load.php kriegt den Request, ermittelt aus REQUEST_URI den PATH und fragt in der Verwaltung nach, ob es den gibt. Wenn ja, wird ein Response-Object (Instanz Deiner Klasse) erstellt, wenn nein, ein 404 geworfen.

        Das ist ja meine Frage 2: Wo geschieht das alles? Innerhalb einer Klasse? Klatsche ich das jetzt einfach als Code in eine .php?

        In Deiner Klasse (Code). In der Verwaltung (Konfiguration). Das sind zwei unterschiedliche Dinge und klar voneinander getrennt.

        Das war glaub ich ein bisschen unglücklich ausgedrückt. Was ich meinte war: Die Klasse "page" versucht die angeforderte Seite zu laden (Überprüft ob die Datei existiert). Was tut sie, wenn die Datei nicht existiert? Automatisch die 404-Seite laden? Oder einfach nur false zurückgeben und irgendwo anders (wo?) wird dann ermittelt, dass die 404-Seite im Falle einer nicht gefundenen Seite geladen werden muss und der Klasse gesagt, sie solle jetzt die 404-Seite laden.

        Ganze einfach in der Webserverkonfig:

        ErrorDocument 404 => ResponseHandler (index.php)
        Alle Requestse    => ResponseHandler (index.php)

        Der Trick, damit Beides funktioniert: Der ResponseHandler (index.php) steht NICHT in der Verwaltung.

        Was meinst du mit "Verwaltung"?

        Das ist die Konfiguration, siehe weiter oben: Eine Tabelle mit ALLEN URLs, welche Deine Website beinhaltet.

        Das mit den Templates hab ich mir auch schon so vorgestellt, wie du es beschreibst. Aber ist "submit" hier nicht der falsche Begriff? Ich würd es eher als Request bezeichnen, wobei Ajax ja auch ein Request ist... Aber müsste dieses Überprüfen ob Ajax oder "normaler" Request, nicht eher Teil einer Extra-Klasse "template" sein, die das laden und bestücken der Templates übernimmt?

        Es muss dafür gesorgt sein, dass der Controler unterscheiden kann, ob ein Submit oder ein Ajaxrequest vorliegt. Das kann über einen speziellen Header erfolgen oder anhand eines Parameters.

        Beispiel

        Insgesamt, merke ich jetzt, ist das Thema echt komplex. Man könnte jetzt seine alten nicht-OOP-Scripte einfach so hinbiegen, aber dann hat man das OOP ja nur um des OOP Willen und nutzt die Vorteile gar nicht

        Siehs mal so: Deine Verwaltung, die Konfiguration ist eine Sammlung von Objekten. Das ist die Grundlage dafür, OOP zur dynamischen Erstellung von Webcontent nutzen zu können.

        Hotti

        1. Das mit den Templates hab ich mir auch schon so vorgestellt, wie du es beschreibst. Aber ist "submit" hier nicht der falsche Begriff? Ich würd es eher als Request bezeichnen, wobei Ajax ja auch ein Request ist... Aber müsste dieses Überprüfen ob Ajax oder "normaler" Request, nicht eher Teil einer Extra-Klasse "template" sein, die das laden und bestücken der Templates übernimmt?

          Es muss dafür gesorgt sein, dass der Controler unterscheiden kann, ob ein Submit oder ein Ajaxrequest vorliegt. Das kann über einen speziellen Header erfolgen oder anhand eines Parameters.

          Submit ist der falsche Begriff. Aber mir ist kein Anderer eingefallen, der einen Nicht-Ajax-Request kennzeichnet ;)

        2. Moin,

          Das war glaub ich ein bisschen unglücklich ausgedrückt. Was ich meinte war: Die Klasse "page" versucht die angeforderte Seite zu laden (Überprüft ob die Datei existiert). Was tut sie, wenn die Datei nicht existiert? Automatisch die 404-Seite laden? Oder einfach nur false zurückgeben und irgendwo anders (wo?) wird dann ermittelt, dass die 404-Seite im Falle einer nicht gefundenen Seite geladen werden muss und der Klasse gesagt, sie solle jetzt die 404-Seite laden.

          Ganze einfach in der Webserverkonfig:

          ErrorDocument 404 => ResponseHandler (index.php)
          Alle Requestse    => ResponseHandler (index.php)

          Der Trick, damit Beides funktioniert: Der ResponseHandler (index.php) steht NICHT in der Verwaltung.

          Äh, was? Ich habe eine index.php Bei einem Request wird erstmal die Seite per GET-Parameter übergeben: index.php?page=index oder index.php?page=test/index Und wie soll da der Webserver einen 404 schmeißen, wenns die Datei test/index nicht gibt?

          Was meinst du mit "Verwaltung"?

          Das ist die Konfiguration, siehe weiter oben: Eine Tabelle mit ALLEN URLs, welche Deine Website beinhaltet.

          Wozu brauch ich denn die? Nehmen wir an, ich arbeite ganz stumpf mit Flatfiles, dann prüfe ich einfach per file_exists(), ob eine Datei (=Seite) existiert.

          Das mit den Templates hab ich mir auch schon so vorgestellt, wie du es beschreibst. Aber ist "submit" hier nicht der falsche Begriff? Ich würd es eher als Request bezeichnen, wobei Ajax ja auch ein Request ist... Aber müsste dieses Überprüfen ob Ajax oder "normaler" Request, nicht eher Teil einer Extra-Klasse "template" sein, die das laden und bestücken der Templates übernimmt?

          Es muss dafür gesorgt sein, dass der Controler unterscheiden kann, ob ein Submit oder ein Ajaxrequest vorliegt. Das kann über einen speziellen Header erfolgen oder anhand eines Parameters.

          Ist mir klar. Aber _wer_ unterscheidet das? Der Controler oder eine Extra-Klasse?

          Insgesamt, merke ich jetzt, ist das Thema echt komplex. Man könnte jetzt seine alten nicht-OOP-Scripte einfach so hinbiegen, aber dann hat man das OOP ja nur um des OOP Willen und nutzt die Vorteile gar nicht

          Siehs mal so: Deine Verwaltung, die Konfiguration ist eine Sammlung von Objekten. Das ist die Grundlage dafür, OOP zur dynamischen Erstellung von Webcontent nutzen zu können.

          Das heißt, ich speichere für jede Seite, jedes Template, etc. einfach eine Serialisierte Form des Objektes (wohin ist erstmal egal)? Ich hatte mir das eher so vorgstellt, das die "page" Klasse eine Methode "load" hat, die dann eine bestimmte Seite lädt, sodass dieses Objekt jetzt diese Seite wiederspiegelt.

          Gruß,
          Take

          1. hi Take,

            Das war glaub ich ein bisschen unglücklich ausgedrückt. Was ich meinte war: Die Klasse "page" versucht die angeforderte Seite zu laden (Überprüft ob die Datei existiert). Was tut sie, wenn die Datei nicht existiert? Automatisch die 404-Seite laden? Oder einfach nur false zurückgeben und irgendwo anders (wo?) wird dann ermittelt, dass die 404-Seite im Falle einer nicht gefundenen Seite geladen werden muss und der Klasse gesagt, sie solle jetzt die 404-Seite laden.

            Ganze einfach in der Webserverkonfig:

            ErrorDocument 404 => ResponseHandler (index.php)
            Alle Requestse    => ResponseHandler (index.php)

            Der Trick, damit Beides funktioniert: Der ResponseHandler (index.php) steht NICHT in der Verwaltung.

            Äh, was? Ich habe eine index.php Bei einem Request wird erstmal die Seite per GET-Parameter übergeben: index.php?page=index oder index.php?page=test/index Und wie soll da der Webserver einen 404 schmeißen, wenns die Datei test/index nicht gibt?

            In diesem Fall wirft natürlich NICHT der Webserver den 404. index.php wird jedoch einen Status: 404 Not Found an den Webserver senden, damit dieser den richtigen Header ausgibt und index.php wird dem Bescucher eine schöne Seite zeigen mit hilfreichen Links.

            Der Webserver wirft nur dann selbst einen 404, wenn der Request an index.php vorbei auf eine nicht vorhandende Datei greift. Du musst dazu eine passende Rewrite-Condition UND eine passende R-Rule haben, ohne dem gehts natürlich nicht ;)

            Es gibt zwei Fälle, die auch von Deiner Cond/Rule abhängen:

            1. der Webserver findet zum Request die lokale Ressource nicht ( außerhalb der RewriteRule)
            2. die Rule greift, aber das Response-Object kann nicht erstellt werden, weil es nicht in der Verwaltung steht

            Hinweis, Vorsicht, die Zeile
            ErrorDocument 404 /index.php

            Baue erst zum Schluss ein, Fehler in der Cond/Rule sind sonst sehr schwer zu finden (den Fall hatte ich gestern abend).

            Was meinst du mit "Verwaltung"?

            Das ist die Konfiguration, siehe weiter oben: Eine Tabelle mit ALLEN URLs, welche Deine Website beinhaltet.

            Wozu brauch ich denn die? Nehmen wir an, ich arbeite ganz stumpf mit Flatfiles, dann prüfe ich einfach per file_exists(), ob eine Datei (=Seite) existiert.

            Deine Website hat URLs like /foo.html /bar.html /downloads/mp3/otto.mp3 usw. Alles nur virtuell, die liegen nicht als Dateien im FS. Diese URLs werden in einer Tabelle verwaltet, das sind Objecte mit Eigenschaften. Eine solche Verwaltung gibt das Dateisystem nicht her, das musst Du schon selber bauen. Wo die Tabelle liegt, ist egal, das kann MySQL sein oder ein Flatfile, oder eine ini oder sonstwas: Performant muss es sein!

            Es muss dafür gesorgt sein, dass der Controler unterscheiden kann, ob ein Submit oder ein Ajaxrequest vorliegt. Das kann über einen speziellen Header erfolgen oder anhand eines Parameters.

            Ist mir klar. Aber _wer_ unterscheidet das? Der Controler oder eine Extra-Klasse?

            Der Controler kann in einer extra Klasse liegen, muss aber nicht. In meiner Praxis ist der Controler eine Methode des Response-Objects. Diese Methode wird dann gerufen, wenn es GET oder POST Parameter gibt UND wenn zu dem requesteten URL überhaupt Parameter erlaubt sind UND wenn die Berechtigung stimmt (Session, Anmeldung liegt vor). Auch hieran erkennst Du den Vorteil der OOP, nämlich: Alles was jetzt gebraucht wird, sind Attribute des Response-Objects. Und die stehen in der Verwaltung.

            Beispiel, Verwaltung in einer ini-Datei

            [/users/login] # das ist die Objekt-ID, der URL zu einem Login-Formular
                title=Login für den Kundenbereich
                descr=Lass dir was einfallen...
                type=text/html
                css=/cust.css
                js=/base.js
                iswas=template # Name der Methode, welche den Body erstellt
                control=login  # so heißt die Methode, die beim Eintippen der Benutzerdaten aufgerufen wird. sub login{} ist der Controler, eine Klassenmethode der Singleton-Class

            Das heißt, ich speichere für jede Seite, jedes Template, etc. einfach eine Serialisierte Form des Objektes (wohin ist erstmal egal)? Ich hatte mir das eher so vorgstellt, das die "page" Klasse eine Methode "load" hat, die dann eine bestimmte Seite lädt, sodass dieses Objekt jetzt diese Seite wiederspiegelt.

            Genau! Request geht auf /users/login, der Reponse-Handler prüft, ob der Request erlaubt ist, das ist der Fall, den URL gibts in der Verwaltung. Das Response-Object wird erstellt, es ruft die Methode template auf, welch den Body (Formular) erstellt und alles zsammen zum Browser schickt. Der gibt user/pass ein, was die Methode sub contol{} aufruft, da drinnen werden die Parameter geprüft, der Login prozessiert und das Ergebnis ausgegeben.

            Genauer: Es gibt noch eine Funktion, die Du mit 'load' meinst (vermute ich). Nenne die nicht load, sondern erzeuge_body, getbody, bodygen, bodybuild oder so ähnlich. Weil: Der Contoler (sub login{}) ruft letztendlich wieder die Methode template{} auf, wenn keine Fehler oder nur kleine Fehler aufgetreten sind (Benutzername falsch oder so...). Ein fataler Fehler wäre ein unerlaubter Parameter, da sollte nicht das Formular gezeigt werden, sondern eine richtig gute Fehlerseite. Die Methode bodybuild wird als Letzte in der Verarbeitungskette aufgerufen, die guckt in den Fehlerspeicher (ein Attribut des Response-Objects) und tauscht bei fatalen Fehlern den Body aus. Ansonsten kommmt das Template, woher auch immer (Datei, DB...).

            Wie gesagt: Alles nur ein Vorschlag. Hat sich aber gut bewährt ;)

            Ich habs in Perl umgesetzt, in PHP würde das ähnlich aussehen, evntl. ist die Templategeschichte damit schöner zu machen (mit dem Gedanken spiele ich seit ein paar Tagen)

            Schönes Wochenende,
            Hotti

            1. Moin,

              Genauer: Es gibt noch eine Funktion, die Du mit 'load' meinst (vermute ich). Nenne die nicht load, sondern erzeuge_body, getbody, bodygen, bodybuild oder so ähnlich. Weil: Der Contoler (sub login{}) ruft letztendlich wieder die Methode template{} auf, wenn keine Fehler oder nur kleine Fehler aufgetreten sind (Benutzername falsch oder so...). Ein fataler Fehler wäre ein unerlaubter Parameter, da sollte nicht das Formular gezeigt werden, sondern eine richtig gute Fehlerseite. Die Methode bodybuild wird als Letzte in der Verarbeitungskette aufgerufen, die guckt in den Fehlerspeicher (ein Attribut des Response-Objects) und tauscht bei fatalen Fehlern den Body aus. Ansonsten kommmt das Template, woher auch immer (Datei, DB...).

              Verstehe ich so. Aber mit "load" meinte ich eigentlich soviel wie "Lade die Daten, die du für die Seite brauchst (Seitenname, Inhalt, etc.) in den RAM".

              Wie gesagt: Alles nur ein Vorschlag. Hat sich aber gut bewährt ;)

              Mal sehen, was ich so gebrauchen kann, erstmal Vielen Dank!

              Schönes Wochenende,
              Hotti

              Dir auch,

              Gruß,
              Take

              1. Moin,

                Genauer: Es gibt noch eine Funktion, die Du mit 'load' meinst (vermute ich). Nenne die nicht load, sondern erzeuge_body, getbody, bodygen, bodybuild oder so ähnlich. Weil: Der Contoler (sub login{}) ruft letztendlich wieder die Methode template{} auf, wenn keine Fehler oder nur kleine Fehler aufgetreten sind (Benutzername falsch oder so...). Ein fataler Fehler wäre ein unerlaubter Parameter, da sollte nicht das Formular gezeigt werden, sondern eine richtig gute Fehlerseite. Die Methode bodybuild wird als Letzte in der Verarbeitungskette aufgerufen, die guckt in den Fehlerspeicher (ein Attribut des Response-Objects) und tauscht bei fatalen Fehlern den Body aus. Ansonsten kommmt das Template, woher auch immer (Datei, DB...).

                Verstehe ich so. Aber mit "load" meinte ich eigentlich soviel wie "Lade die Daten, die du für die Seite brauchst (Seitenname, Inhalt, etc.) in den RAM".

                Jes. In der Praxis wird es mehrere Methoden/Funktionen geben, die daran beteiligt sind. Ein Controler übernimmt die Kontrolle der Berechtigungen und prüft, ob eine Anmeldung vorliegt. Die Kontrolle, ob der Req.URL in der Verwaltung geführt wird, muss auch irgendwo gemacht werden und dann ist auch noch zu prüfen, ob der Request Parameter enthalten darf.

                Diese Controler sind nicht an irgendeinen URL gebunden, die werden bei  jedem Request aufgerufen.

                Der Controler gem. MVC-Patterns übernimmt bei mir nur die Parameterkontrolle, die Parameter wiederum sind an einen URL gebunden. URLs, die Parameter im Request haben dürfen, haben also jeweils eine eigene Control-Methode (der Name steht in der Konfiguration/Verwaltung).

                Singleton: Tja, alles in einer Klasse, es braucht tatsächlich nur eine Instanz (das Resonse-Object, RO). Das RO hat u.a. auch den Stash als Attribut, das ist die Eigenschaft, in der die Werte fürs Template liegen, Scalare, Hashes, Arrays... reine Daten, Ergebnismengen aus DB-Abfragen, völlig getrennt von der Darstellung.

                RO (mit Stash) ruft dann eine Methode der Template-Engine, erst hier werden die Daten aus dem Datenversteck ins Markup geschossen und fertisch ist der Lack ;)

                Hotti (zurück zur Glotze, gucke Papstbesuch im Eichsfeld, meine alte Heimat)

                1. hm,

                  RO (mit Stash) ruft dann eine Methode der Template-Engine, erst hier werden die Daten aus dem Datenversteck ins Markup geschossen und fertisch ist der Lack ;)

                  Quatsch. Die Template-Engine ist bei mir eine eigene Klasse. Das Template-Objekt wird erstellt und bekommt sowohl den Body als auch den Stash übergeben, das sind zwei Attribute des Response-Objects. Sorum, sorry ;)

  3. Hi!

    ich entdecke gerade anhand dieses sehr guten Tutorials von Peter Kropff die tolle Welt von OOP in PHP. Das Problem was ich jetzt habe ist, dass in dem Text zwar alles super erklärt wird, aber wie man jetzt konkrete Probleme mit OOP umsetzt, steht eben nicht drin.

    Er erklärt das anhand ziemlich abstrakter Beispiele. Das heißt, im wahren Leben sind seine Beispiele ziemlich konkret, aber beim Programmieren bildet man doch öfter mal Dinge ab, die man ohne die Programmierung gar nicht bräuchte oder erstellt Code zwar so, dass er das konkrete Problem erfüllt, aber an die Gegebenheiten der Computerprogrammierung angepasst ist.

    Ich hab nicht alles gelesen, ich hab erstmal aufgehört, als ich den ersten groben Schnitzer fand. "Briefzustellen" ist bei ihm eine Klasse, die sowohl den Brief als auch das Zustellen enthält. Im wahren Leben ist das aber nicht so. Genauso wie sich eine Kuh nicht selbst melkt, also nicht sie die Methode "Melken" bekommt sondern der Bauer, versendet sich ein Brief nicht selbst. Der Brief ist also ein Objekt und die Post, die die Briefe zustellt, ein anderes. Methoden eines Objektes sollten immer Tätigkeiten sein, bei denen es akrtiv ist. Wenn es passiv etwas mit sich geschehen lassen soll, wird es als Parameter an eine Methode des aktiven Objekts übergeben.

    Jedenfalls muss man als Programmierer in der Lage sein, die Hilfsmittel auch im abstrakten Zustand zu kennen und bei konkreten Aufgabenstellungen wissen, wie man diese am besten mit den gegebenen Mitteln umsetzt.

    • Ich habe eine Klasse "page", die die Verwaltung der Seiten übernimmt. Diese hat verschiedene Methoden, unter anderem "load", mit der man eine bestimmte Seite in den Speicher lädt. Wo aber gehört denn die Funktion (ich meine nicht "Subroutine") zum Auslesen der Seite aus den GET-Parametern hin? In die Klasse? Oder einfach außerhalb in das Script?

    Wenn du mit "Auslesen der Seite aus den GET-Parametern" meinst, wie dein Page-Objekt nun weiß, was der Client aufgerufen hat, dann ist das nicht Aufgabe des Page-Objekts, herauszufinden was es eigentlich sein soll. Stattdessen nimmt man einen Front-Controller, der die Eingabeparameter auswertet und entsprechend das passende Page-Objekt erstellt. Wenn du die Page-Klasse von den GET-Parametern abhängig machst, ist sie nicht mehr universell. Sie kann dann weder POST noch irgendwelche anderen Requests bearbeiten. Oder du baust das alles mit Fallunterscheidungen ein, wobei du wieder beim Programmierstil "Unstrukturierte Ablaufsteuerung" gelandet bist.

    Wenn du es ordentlich machen willst - also schön gekapselt und wiederverwendbar -, brauchst du also eine gewisse Menge Overhead, der bei der "unstrukturierten Ablaufsteuerung" nicht benötigt wird.

    • Und wo packt man die Umleitung auf Fehlerseiten hin?

    Die lässt man ganz weg. Weiterleititis ist eine vermeidbare Krankheit. (Es gibt sinnvolle Anwendungen für Weiterleitungen, aber Fehlermeldungsseiten gehören nicht dazu.) Man gibt einfach die geforderte Seite aus. Im Fehlerfall kommt nicht der eigentliche Inhalt sondern der Ersatzinhalt zur Ausgabe. Ersatzinhalt kann auch eine Fehlermeldung sein, auch begleitet von einem entsprechenden Statuscode. Ersatzinhalt kann auch etwas sein, das dem Anwender hilft, trotzdem ans Ziel zu kommen, ihm eine Alternative bietet, anstatt ihn ob der Fehlermeldung achselzuckend zur Konkurrenz zu verlieren.

    Sollte die Klasse selbstständig bei Fehlern statt der gewünschten die Fehlerseite laden, oder sollte sie nur einen Fehler zurückgeben und das Hauptprogramm lässt dann die Klasse die Fehlerseite laden?

    Vermeide eierlegene Wollmichsau-Objekte. Sie sehen einerseits aufgrund ihrer vielen Features sehr nützlich aus, bedeuten jedoch andererseits einen erhöhten Erstellungs- und später Pflegeaufwand. In eine Page-Klasse würde bei mir nur die Aufgabe haben, die beim EVA-Prinzip der A-Teil hätte, also alles was mit Ausgabe zu tun hat. Das Besorgen der Daten und dabei auftretende Fehler zu behandeln ist Aufgabe des V-Teils. Anhand dessen Ergebnisses kann man dann entscheiden, welchen Inhalt das Page-Objekt zwecks Ausgabe bekommt - beispielsweise welches Template zu laden ist.

    • Die letzte Frage, die eigentlich aus den anderen beiden hervorgeht ist, ob man sowas wie Session-Initialisierung, erstellen der page-Instanz, usw. in die Klasse CMS* als Methode packen sollte, die dann im Hauptprogramm aufgerufen wird, oder ob das einfach im Hauptprogramm stehen sollte.

    Session-Initialisierung wäre vielleicht eine Aufgabe des Front-Controllers, wenn zum Ermitteln, was verarbeitet werden soll auch Session-Daten benötigt werden. Vielleicht braucht es auch erst der Verarbeitungsteil. Und manchmal hat man mehrere Stellen, die auf Session-Daten angewiesen sind oder solche erzeugen. Dann sollte man die Session-Verwaltung als eigenen Programmteil erstellen.

    Wenn du was komplexes machen willst, kannst du ruhig erst einmal versuchen, etwas selbst zu erfinden, um dabei auch die Schwierigkeiten dieses Weges kennenzulernen. Und dann kannst dir mal vorhandene Frameworks ansehen, die zum Beispiel nach dem MVC-Muster aufgebaut sind. Üblicherweise hat man genug mit der Implementierung der eigenen Geschäftslogik zu tun, so dass es hilfreich ist, wenn für die ganzen kleinen Nebentätigkeiten bereits vorhandene Lösungswege genutzt werden können. Selbst wenn du lieber selbst etwas erstellen willst, lohnt sich der Blick auf ein Framework, um Inspirationen für den Aufbau des eigenen Projekts zu bekommen.

    Lo!

    1. Moin,

      Hach, mit deiner Antwort kann ich schonmal mehr anfangen, als mit der von hotti (nix gegen dich hotti, aber ich glaube, du kannst einfach nicht gut erklären :D). Dankeschön!

      Ich hab nicht alles gelesen, ich hab erstmal aufgehört, als ich den ersten groben Schnitzer fand. "Briefzustellen" ist bei ihm eine Klasse, die sowohl den Brief als auch das Zustellen enthält. Im wahren Leben ist das aber nicht so. Genauso wie sich eine Kuh nicht selbst melkt, also nicht sie die Methode "Melken" bekommt sondern der Bauer, versendet sich ein Brief nicht selbst. Der Brief ist also ein Objekt und die Post, die die Briefe zustellt, ein anderes. Methoden eines Objektes sollten immer Tätigkeiten sein, bei denen es akrtiv ist. Wenn es passiv etwas mit sich geschehen lassen soll, wird es als Parameter an eine Methode des aktiven Objekts übergeben.

      *freu* Genau das hab ich mich beim Lesen auch gefragt! Das zeigt auf jeden Fall, dass ich was verstanden habe.

      • Ich habe eine Klasse "page", die die Verwaltung der Seiten übernimmt. Diese hat verschiedene Methoden, unter anderem "load", mit der man eine bestimmte Seite in den Speicher lädt. Wo aber gehört denn die Funktion (ich meine nicht "Subroutine") zum Auslesen der Seite aus den GET-Parametern hin? In die Klasse? Oder einfach außerhalb in das Script?

      Wenn du mit "Auslesen der Seite aus den GET-Parametern" meinst, wie dein Page-Objekt nun weiß, was der Client aufgerufen hat, dann ist das nicht Aufgabe des Page-Objekts, herauszufinden was es eigentlich sein soll. Stattdessen nimmt man einen Front-Controller, der die Eingabeparameter auswertet und entsprechend das passende Page-Objekt erstellt. Wenn du die Page-Klasse von den GET-Parametern abhängig machst, ist sie nicht mehr universell. Sie kann dann weder POST noch irgendwelche anderen Requests bearbeiten. Oder du baust das alles mit Fallunterscheidungen ein, wobei du wieder beim Programmierstil "Unstrukturierte Ablaufsteuerung" gelandet bist.

      Das hab ich bei hotti glaub ich auch rausgelesen. Und dieser Front-Controller stellt dann wahrscheinlich eine statische Methode dar, die einmal aufgerufen wird und dann den Ablauf steuert, oder?

      Wenn du es ordentlich machen willst - also schön gekapselt und wiederverwendbar -, brauchst du also eine gewisse Menge Overhead, der bei der "unstrukturierten Ablaufsteuerung" nicht benötigt wird.

      • Und wo packt man die Umleitung auf Fehlerseiten hin?

      Die lässt man ganz weg. Weiterleititis ist eine vermeidbare Krankheit. (Es gibt sinnvolle Anwendungen für Weiterleitungen, aber Fehlermeldungsseiten gehören nicht dazu.) Man gibt einfach die geforderte Seite aus. Im Fehlerfall kommt nicht der eigentliche Inhalt sondern der Ersatzinhalt zur Ausgabe. Ersatzinhalt kann auch eine Fehlermeldung sein, auch begleitet von einem entsprechenden Statuscode. Ersatzinhalt kann auch etwas sein, das dem Anwender hilft, trotzdem ans Ziel zu kommen, ihm eine Alternative bietet, anstatt ihn ob der Fehlermeldung achselzuckend zur Konkurrenz zu verlieren.

      Ich merke: das Wort "Umleitung" ist doof gewählt, siehe meine Antwort auf hottis Post.

      Sollte die Klasse selbstständig bei Fehlern statt der gewünschten die Fehlerseite laden, oder sollte sie nur einen Fehler zurückgeben und das Hauptprogramm lässt dann die Klasse die Fehlerseite laden?

      Vermeide eierlegene Wollmichsau-Objekte. Sie sehen einerseits aufgrund ihrer vielen Features sehr nützlich aus, bedeuten jedoch andererseits einen erhöhten Erstellungs- und später Pflegeaufwand. In eine Page-Klasse würde bei mir nur die Aufgabe haben, die beim EVA-Prinzip der A-Teil hätte, also alles was mit Ausgabe zu tun hat. Das Besorgen der Daten und dabei auftretende Fehler zu behandeln ist Aufgabe des V-Teils. Anhand dessen Ergebnisses kann man dann entscheiden, welchen Inhalt das Page-Objekt zwecks Ausgabe bekommt - beispielsweise welches Template zu laden ist.

      Ok, hab mich mal kurz informiert, was das EVA-Prinzip überhaupt ist. Bei einem CMS wäre also die Eingabe die GET/POST-Parameter. Der Front-Controller (ich übernehme einfach mal deine Begriffe, hoffentlich richtig :D) entscheidet auf deren Basis, was zu tun ist und ruft entsprechend die Methoden der einzelnen Klassen (z.B. "page", "template", usw.) auf. Das Ergebnis dieses Prozesses (Seiteninhalt an Template übergeben, usw.) wird dann (vom Front-Controller) an den Browser gesendet. Soweit OK?

      Wenn du was komplexes machen willst, kannst du ruhig erst einmal versuchen, etwas selbst zu erfinden, um dabei auch die Schwierigkeiten dieses Weges kennenzulernen. Und dann kannst dir mal vorhandene Frameworks ansehen, die zum Beispiel nach dem MVC-Muster aufgebaut sind. Üblicherweise hat man genug mit der Implementierung der eigenen Geschäftslogik zu tun, so dass es hilfreich ist, wenn für die ganzen kleinen Nebentätigkeiten bereits vorhandene Lösungswege genutzt werden können. Selbst wenn du lieber selbst etwas erstellen willst, lohnt sich der Blick auf ein Framework, um Inspirationen für den Aufbau des eigenen Projekts zu bekommen.

      Puh, viel Arbeit. Da hab ich mir ja was vorgenommen :D Jetzt ist erstmal Gute Nacht :)

      Vielen Dank dedlfix, hast mir sehr geholfen!

      Gruß,
      Take

      1. Hi!

        Und dieser Front-Controller stellt dann wahrscheinlich eine statische Methode dar, die einmal aufgerufen wird und dann den Ablauf steuert, oder?

        Jein. Es kann auch sinnvoll oder je nach Implementierung des Front-Controllers notwendig sein, eine Instanz zu erzeugen und mehrere Methoden davon aufzurufen, oder schlichtweg vor dem run() ein paar Eigenschaften zu setzen.

        Ok, hab mich mal kurz informiert, was das EVA-Prinzip überhaupt ist.

        Im Prinzip eine einfache Aufgabenteilung in die drei Teile Eingabe, Verarbeitung, Ausgabe. Darauf sollte eigentlich jeder Anfänger stoßen, der aus dem Stadium der "unstrukturierten Ablaufsteuerung" in das der "geordnete Ablaufsteuerung" wechselt. EVA muss nicht nur ein Zwischenschritt sein, sondern kann bei kleinen Projekten auch als alleiniges Realisierungsmuster verwendet werden. Seine Grundprinzipien kann man jedoch auch in großen Frameworks wiederfinden.

        Bei einem CMS wäre also die Eingabe die GET/POST-Parameter. Der Front-Controller (ich übernehme einfach mal deine Begriffe, hoffentlich richtig :D) entscheidet auf deren Basis, was zu tun ist und ruft entsprechend die Methoden der einzelnen Klassen (z.B. "page", "template", usw.) auf. Das Ergebnis dieses Prozesses (Seiteninhalt an Template übergeben, usw.) wird dann (vom Front-Controller) an den Browser gesendet. Soweit OK?

        Im Großen und Ganzen ja. Aber tausch jetzt nicht nur dein Page durch den Begriff Front-Controller aus. Auch der sollte nicht die ELWMS (Eier...sau) werden. Jedenfalls nicht bei größeren Projekten.

        Lo!

        1. Moin,

          Und dieser Front-Controller stellt dann wahrscheinlich eine statische Methode dar, die einmal aufgerufen wird und dann den Ablauf steuert, oder?

          Jein. Es kann auch sinnvoll oder je nach Implementierung des Front-Controllers notwendig sein, eine Instanz zu erzeugen und mehrere Methoden davon aufzurufen, oder schlichtweg vor dem run() ein paar Eigenschaften zu setzen.

          OK, leuchtet ein.

          Bei einem CMS wäre also die Eingabe die GET/POST-Parameter. Der Front-Controller (ich übernehme einfach mal deine Begriffe, hoffentlich richtig :D) entscheidet auf deren Basis, was zu tun ist und ruft entsprechend die Methoden der einzelnen Klassen (z.B. "page", "template", usw.) auf. Das Ergebnis dieses Prozesses (Seiteninhalt an Template übergeben, usw.) wird dann (vom Front-Controller) an den Browser gesendet. Soweit OK?

          Im Großen und Ganzen ja. Aber tausch jetzt nicht nur dein Page durch den Begriff Front-Controller aus. Auch der sollte nicht die ELWMS (Eier...sau) werden. Jedenfalls nicht bei größeren Projekten.

          Das scheint ein wichtiges Merkmal zu sein, diese nicht-ELWMS-Objekte. Ich glaub ich setze mich jetzt nochmal ran und kucke mal, was ich so umsetzen kann. Und dann zeig ich irgendwann mal mein Konzept und frag noch mal nach :)

          Gruß,
          Take

          1. Hi!

            Das scheint ein wichtiges Merkmal zu sein, diese nicht-ELWMS-Objekte.

            Das ist kein Merkmal der OOP sondern eine aus Erfahrung entstandene selbst aufzuerlegende Einschränkung. Man kann mit jedem Programmierparadigma unübersichtlichen Code erzeugen. Das von Sven erläuterte SOLID-Prinzip kann man prinzipell auch auf Nicht-OOP anwenden, um wartbaren und überschaubaren Code zu erzeugen.

            Lo!

  4. Moin!

    ich entdecke gerade anhand dieses sehr guten Tutorials von Peter Kropff die tolle Welt von OOP in PHP.

    Naja, ein eher mittelmäßiges Tutorial.

    Die Beispiele sind allesamt vergleichbar mit "Auto extends Fahrzeug extends Metall". Das ist die klassische Methode, Vererbung zu erklären, geht aber nach meiner Meinung am Kern von OOP vorbei.

    Sehr kritisch sehe ich beispielsweise, dass das Tutorial die Verwendung von statischen Methoden und Eigenschaften relativ unkritisch propagiert. Damit handelt man sich jede Menge Probleme ein, die dummerweise sehr unoffensichtlich sein können.

    Das Problem was ich jetzt habe ist, dass in dem Text zwar alles super erklärt wird, aber wie man jetzt konkrete Probleme mit OOP umsetzt, steht eben nicht drin.

    Das Tutorial erklärt die grundlegenden Konzepte der OOP. Du weißt also hinterher Dinge wie Vererbung, Konstruktoren, Interfaces, Sichtbarkeit etc, und wie diese Dinge technisch zusammenspielen können. Was nicht drinsteht - und das kriegt man vermutlich auch erst hin, wenn man selbst aktiv wird und seine eigenen Erfahrungen sammelt - ist wie man diese Kenntnis letztendlich in funktionsfähigen und verständlichen Code umsetzt.

    Mal konkret formuliert: Folgender Tutorialcode wird dargeboten

    class Getraenk  
    {  
      private static $rechnung;  
      private static $getraenke = array();  
      private $promille;  
      
      public function bestellen($getraenk, $preis, $alkohol)  
      {  
        self::$rechnung   += $preis;  
        $this -> promille += $alkohol;  
        self::$getraenke[$getraenk] += 1;  
      }  
      public function getPromille()  
      {  
        return $this -> promille;  
      }  
      public static function getRechnung()  
      {  
        return self::$rechnung;  
      }  
      public static function getGetraenke()  
      {  
        return self::$getraenke;  
      }  
    }
    

    Wie oben schon kritisiert, werden hier statische Methoden und Eigenschaften verwendet.

    Das Anwendungsbeispiel (mal verkürzt):

    $dieter = new Getraenk;  
    $doerte = new Getraenk;  
    $dieter -> bestellen('Bier', 4.5, 0.3);  
    $doerte -> bestellen('Wein', 8.0, 0.3);  
      
    echo $dieter -> getPromille().'<br>';  
    echo $doerte -> getPromille().'<br>';  
    echo Getraenk::getRechnung().'<br>';  
    
    

    Man sieht, dass es ZWEI Objekte vom Typ "Getraenk" gibt, die sich allerdings gegenseitig beeinflussen, denn jede Bestellung hat Auswirkungen auf die Gesamtrechnung.

    Das Beispiel kann die Gefährlichkeit solcher Konzepte nicht verdeutlichen, weil sich die gesamte Anwendung der fragwürdigen Klasse ja auf das gewünschte Ziel hin entwickelt: Zwei Objekte sammeln Getränke, und am Ende steht die Gesamtrechnung zur Verfügung.

    Was nicht gezeigt wird: Was passiert, wenn an einer ganz anderen Stelle im Code, für eine vollkommen andere Sache ebenfalls eine Getränkerechnung benötigt wird, und deshalb dieselbe Klasse zum Einsatz kommt?

    Ich hoffe, du erkennst das Problem.

    • Ich habe eine Klasse "page", die die Verwaltung der Seiten übernimmt. Diese hat verschiedene Methoden, unter anderem "load", mit der man eine bestimmte Seite in den Speicher lädt. Wo aber gehört denn die Funktion (ich meine nicht "Subroutine") zum Auslesen der Seite aus den GET-Parametern hin? In die Klasse? Oder einfach außerhalb in das Script?

    Ausgehend von den technischen Möglichkeiten von OOP kann man ja beliebig programmieren, nur sind manche Ansätze besser als andere, weil das spätere erneute Verstehen, das Verändern oder das Wiederverwenden des Codes einfacher ist.

    Deshalb haben sich gewisse Grundsätze entwickelt, die man einhalten sollte, wenn man sich das Leben nicht unnötig schwer machen will: SOLID Dieses Akronym bedeutet:

    * Single Responsibility: Eine Klasse sollte nur EINEN Grund haben, dass man sie ändert.
    * Open/Closed Principle: Eine Klasse sollte offen für Erweiterung, aber geschlossen für Modifikation sein.
    * Liskov Substitution: Eine Kindklasse sollte ohne Anpassungsnotwendigkeit als 1:1-Ersatz an allen Stellen verwendbar sein, an denen die Elternklasse schon funktioniert.
    * Interface Segregation: Viele kleinere Interfaces für eine spezifische Aufgabe sind besser als ein großes Pauschal-Interface.
    * Dependency Inversion: Die von einer Klasse benutzten weiteren Klassen dürfen nicht innerhalb dieser Klasse erzeugt werden, sondern kommen von außen hinein.

    Ich gebe gern zu, dass diese Prinzipien sehr abstrakt klingen mögen für den Anfänger.

    Deshalb mal als Gedankenansatz: "eine Klasse "page", die die Verwaltung der Seiten übernimmt" bedeutet für dich was genau? Was heißt "Verwaltung"?

    In meiner Vorstellung enthält "Verwaltung" sehr viele verschiedene Aufgaben, die alle nicht gesammelt in eine einzige Klasse gehören. Beispielsweise wird es wohl einen Mechanismus geben, eine Seite aus dem persistenten Speicherbereich ins RAM zu laden. Die Seite wird also irgendeine Methode namens "load()" enthalten, die das macht. Aber was macht die Methode dann genau? Beachte: Ich sprach vom "persistenten Speicherbereich" - das ist erstmal alles mögliche, beispielsweise eine Datei auf Festplatte, oder eine Tabelle in einer Datenbank. Oder eine externe, per HTTP erreichbare Ressource von einem anderen Server.

    Du würdest für dein konkretes System natürlich erstmal nur eine einzige Methode zum Laden des Seiteninhalts benötigen - aber wenn du z.B. die Datenbankabfrage konkret in die load-Methode deiner Klasse "page" schreibst, dann hast du dich für diese Klasse eindeutig festgelegt, dass der persistente Speicher eben "Datenbank" ist. Das ist aber ein Verstoß gegen das erste SOLID-Prinzip: Single Responsibility - eine Klasse wird nur aus EINEM Grund geändert. Deine Klasse "page" hat den primären Grund zu existieren und geändert zu werden, weil sie in deinem System eine auszugebende Seite repräsentiert. Und wenn in dieser Klasse Datenbankcode steht, hat sie dadurch noch einen zweiten Grund, geändert werden zu müssen: Wenn sich die Datenbank verändert, oder der Datenspeicher ganz allgemein.

    Aus demselben Grund wird die Klasse "page" keinerlei Code zur Auswertung von Request-Parametern haben - das ist Aufgabe einer anderen Klasse, die nur zu diesem Zweck existieren sollte.

    • Und wo packt man die Umleitung auf Fehlerseiten hin?

    Das ist ebenfalls Aufgabe einer ganz anderen Klasse. Insgesamt gehört sowas in ein Konstrukt, welches man als MVC kennt. Und alles, was für das Auswerten des Request und das Generieren des zugehörigen Responses zuständig ist, ist in den diversen Frameworks, die weitverbreitet sind, schon recht umfassend und gut anpassbar realisiert.

    Hatte ich dir schon mal empfohlen, dir irgendeins dieser Frameworks anzusehen? Einfach nur mal, um die Konzepte, die man dort findet, überhaupt mal kennenzulernen und zu verstehen?

    Sollte die Klasse selbstständig bei Fehlern statt der gewünschten die Fehlerseite laden, oder sollte sie nur einen Fehler zurückgeben und das Hauptprogramm lässt dann die Klasse die Fehlerseite laden?

    Eine Klasse "page" wird garnicht angesprochen, wenn sie selbst von einem Request nicht angesprochen wird, weil der Request dann ja offenbar eine andere Seite meinte - und nichtexistente Seiten zeichnen sich dadurch aus, dass sie nicht existieren, deswegen auch keine zugehörige Klasse "page".

    Irgendein vorgeschalteter Mechanismus muss ja erstmal erkennen, ob die vom Request gemeinte Seite existiert.

    • Die letzte Frage, die eigentlich aus den anderen beiden hervorgeht ist, ob man sowas wie Session-Initialisierung, erstellen der page-Instanz, usw. in die Klasse CMS* als Methode packen sollte, die dann im Hauptprogramm aufgerufen wird, oder ob das einfach im Hauptprogramm stehen sollte.

    Es gibt keine Klasse "CMS". Siehe oben: Single Responsibility.

    Und ja, eine Session ist auch etwas, was man in eine Klasse verpacken muss.

    * Diese Klasse stellt ein paar statische Methoden zur Verfügung, die die eigentliche Funktionalität des CMS ausmachen: Dateien mit gleichzeitigem Locking laden und dann das enthaltene JSON parsen und zurückgeben, etc. etc.

    Um Gottes Willen!*

    - Sven Rautenberg

    [*] Kannst du natürlich erstmal so programmieren, um dann hoffentlich festzustellen, dass sowas extreme Probleme nach sich ziehen wird, wenn der Code irgendwann mal geändert werden muss.

    1. Sehr gut geschrieben!
      Daumen hoch.

      Gruß
      lobender
      T-Rex