Gerhard: Methoden in Klasse inkludieren

Hallo,

ich mache die ersten Gehversuche OOP unter PHP. Um zu testen, ob ich für dieselbe Klasse Objekte mit unterschiedlichen Methoden generieren kann, habe ich ein kleines Beispiel geschrieben. Offensichtlich geht es aber nicht, da die Funktionen, die als Methoden eingebunden werden sollen, als Funktionen im globalen Kontext eingebunden werden.

----

kunde.php

<?php

class kunde
{
    var $name;

function kunde($name, $geschlecht)
    {
        if ($geschlecht == "m")
        {
            $include="kunde_m.php";
        }
        if ($geschlecht == "w")
        {
            $include="kunde_w.php";
        }
        $this->name = $name;
        include_once($include);
    }

}

$max = new kunde("Max", "m");
test_m();  // globale Einbindung, deswegen funktioniert es
$max->print_kunde();  // globale Einbindung, deswegen im Objekt unbekannt

$sandra = new kunde("Sandra", "w");
test_w();
$sandra->print_kunde();

?>

----

kunde_m.php

<?php

function print_kunde()
{
    echo "Ihr Name ist " . $this->name . ". Sie sind männlich.<br>";
}

function test_m()
{echo "Global eingebunden! => m";}

?>

----

kunde_w.php

<?php

function print_kunde()
{
    echo "Ihr Name ist " . $this->name . ". Sie sind weiblich.<br>";
}

function test_m()
{echo "Global eingebunden! => w";}

?>

----

Das führt zur Ausgabe

Global eingebunden! => m
Fatal error: Call to undefined method kunde::print_kunde() ...

Ich habe eine Lösung gefunden, mit einem innerhalb einer Superklasse erzeugten DAO das Problem zu umgehen. Im Beispiel würden demnach die Methoden der Module als eigene Klassen kunde_m und kunde_w definiert, und der Konstruktor von kunde würde je nach Aufruf diese Klassen als Objekt zurückgeben. Erzeugt würden die Objekte mit

$max = kunde::dao('Max', 'm');
$sandra = kunde::dao('Sandra', 'w');

Dieses Vorgehen ist zwar besser, als alle Methoden in einer Klasse Kunde einzubinden, weil jeweils für ein Objekt nur die Eigenschaften und Methoden der selektierten Unterklasse eingebunden werden, aber zumindest einmal muß der Code aller Unterklassen als Klassenschablone in den Speicher geladen werden. Würde das gepostete Muster mit include funktionieren, wäre der Speicherbedarf niedriger, weil nur die wirklich benötigten Methoden geladen würden.

Gibt es eine Möglichkeit, das mit PHP zu erreichen? Vererbung mittels "extends" entfällt per se, weil es 1. nicht gewünscht ist und 2. zum selben Overhead wie der Weg mit dem DAO führt.

  1. Also, dein Ansatz erscheint mir irgendwie extrem seltsam zu sein...

    Ich kenne keine Sprache in der so was gehen würde, würde ich es nicht machen...

    Und was deine Begründung mit dem Overhead angeht: Die paar Kilobyte, die du damit sparst... dafür würde ich lieber eine vernünftige, nachvollziehbare Struktur mit Vererbungen bauen...

    Du schreibst ja wahrscheinlich nicht für Embedded Systems, sondern für "normale" Webserver...

    Gruß

    Stareagle

    1. Also, dein Ansatz erscheint mir irgendwie extrem seltsam zu sein...

      Hallo!

      Der Ansatz ist eine Factory-Klasse.

      Ich kenne keine Sprache in der so was gehen würde, würde ich es nicht machen...

      Und was deine Begründung mit dem Overhead angeht: Die paar Kilobyte, die du damit sparst... dafür würde ich lieber eine vernünftige, nachvollziehbare Struktur mit Vererbungen bauen...

      Als ich eben noch mal darüber nachgedacht habe, habe ich die Lösung gefunden. Das ist so simpel, daß ich es einfach nicht gesehen habe. Die Module enthalten nun vollständige Klassen. In der Klasse kunde gibt es eine dao-Funktion, die statisch über

      $max = kunde::dao("Max", "m");
      $max->print_kunde();

      $sandra = kunde::dao("Sandra", "w");
      $sandra->print_kunde();

      aufgerufen wird. In der dao-Funktion wird NUR die benötigte Klasse inkludiert, ein neues Objekt aus ihr generiert mit Zuweisung des Namens an dessen Konstruktor. Anschließend übergibt die dao-Funktion mit return das Objekt an $max bzw. $sandra.

      Das funktioniert jetzt einwandfrei.

  2. Was hindert dich, eine Funktion zu definieren, der die Unterscheidung männlich weiblich mitgeteilt wird?

    Was du machst ist folgendes: Du fügst eine Datei ein, in der allgemeine Funktionen definiert sind. Diese sind nun zwar innerhalb der Klasse nutzbar, aber nicht als Methode sonder als Funktion innerhalb einer Methode. Du könntest höchstens versuchen die Funktionen als Methoden dieser Klassen zu kloenen, aber das ist in meinen Augen partou unsinnig.
    Die Bezeichnung: $this-> innerhalb dieser allgemeinen Methoden ist daher fehlerhaft.

    Was du trotzdem machen könntest, wenn es unbedingt sein muss:
    Du definierst eine Methode:

    function my_function()
    {
    include_once($this->include);
    echo print_kunde($this->name);
    }

    und definierst die Funktion, die nicht auf Klassenvariablen zugreifen kann so um, dass sie den Parameter schluckt.

    Aber wie gesagt: In meinen Augen sehr schlechter Stil.

    1. Was hindert dich, eine Funktion zu definieren, der die Unterscheidung männlich weiblich mitgeteilt wird?

      Hallo!

      Der Code war ein Beispiel. Bei so einem Beispiel ginge das natürlich einfacher. Eine Eigenschaft mit dem Geschlecht, abgeleitete Klassen, all das ist denkbar. So soll es aber nicht sein. In Beispielen versuche ich, soweit wie möglich zu abstrahieren, und mache eine Vorgabe, wie es sein soll. Scheinbar verlieren manche Leute dadurch die Vorstellung, um was es geht.

      Was du machst ist folgendes: Du fügst eine Datei ein, in der allgemeine Funktionen definiert sind. Diese sind nun zwar innerhalb der Klasse nutzbar, aber nicht als Methode sonder als Funktion innerhalb einer Methode.

      Wirklich? Oder sind sie global eingebunden?

      Du könntest höchstens versuchen die Funktionen als Methoden dieser Klassen zu kloenen, aber das ist in meinen Augen partou unsinnig.

      Die Lösung ist meinem anderen Beitrag.

      Die Bezeichnung: $this-> innerhalb dieser allgemeinen Methoden ist daher fehlerhaft.

      Das ist schon klar. Aber es SOLL gehen, und das tut es jetzt auch nach Umprogrammierung.

      Aber wie gesagt: In meinen Augen sehr schlechter Stil.

      1. Moin!

        Der Code war ein Beispiel. Bei so einem Beispiel ginge das natürlich einfacher.

        Von jemandem, der selbst schreibt, der mache gerade seine ersten Gehversuche mit OOP, muß man nicht erwarten, dass seine Codebeispiele nicht der eigentliche Code sind, sondern abstrahierte Beispiele.

        So soll es aber nicht sein. In Beispielen versuche ich, soweit wie möglich zu abstrahieren, und mache eine Vorgabe, wie es sein soll. Scheinbar verlieren manche Leute dadurch die Vorstellung, um was es geht.

        Das Problem ist: Abstrakte Problemstellungen lösen sich sehr gerne von der Realität und lassen sich nur noch schwer wieder mit ihr verbinden. Konkrete, reale Probleme eröffnen oft viel bessere Betrachtungsweisen.

        Was du machst ist folgendes: Du fügst eine Datei ein, in der allgemeine Funktionen definiert sind. Diese sind nun zwar innerhalb der Klasse nutzbar, aber nicht als Methode sonder als Funktion innerhalb einer Methode.

        Wirklich? Oder sind sie global eingebunden?

        Versuch einfach mal, mit deinem abstrakten Beispielcode zwei männliche Objekte anzulegen. Das dürfte voraussichtlich scheitern, weil du eine Funktion doppelt definieren willst.

        Die Lösung ist meinem anderen Beitrag.

        Ja klar. Das Factory Pattern ist die übliche Lösung, wenn man über ein einziges Interface gleichartige, aber dennoch intern unterschiedliche Objekte herstellen will.

        - Sven Rautenberg

        --
        "Love your nation - respect the others."
        1. Hallo!

          Der Code war ein Beispiel. Bei so einem Beispiel ginge das natürlich einfacher.

          Von jemandem, der selbst schreibt, der mache gerade seine ersten Gehversuche mit OOP, muß man nicht erwarten, dass seine Codebeispiele nicht der eigentliche Code sind, sondern abstrahierte Beispiele.

          Ich schrieb, meine ersten Gehversuche in OOP UNTER PHP.

          So soll es aber nicht sein. In Beispielen versuche ich, soweit wie möglich zu abstrahieren, und mache eine Vorgabe, wie es sein soll. Scheinbar verlieren manche Leute dadurch die Vorstellung, um was es geht.

          Das Problem ist: Abstrakte Problemstellungen lösen sich sehr gerne von der Realität und lassen sich nur noch schwer wieder mit ihr verbinden. Konkrete, reale Probleme eröffnen oft viel bessere Betrachtungsweisen.

          Bisher ging ich davon aus, je weiter ich abstrahiere und unnütze Nebensächlichkeiten entferne, desto einfacher ist es nachzuvollziehen. Ich sehe aber, die Abstraktion wird jenseits einer Grenze selbst wieder erklärungsbedürftig.

          Was du machst ist folgendes: Du fügst eine Datei ein, in der allgemeine Funktionen definiert sind. Diese sind nun zwar innerhalb der Klasse nutzbar, aber nicht als Methode sonder als Funktion innerhalb einer Methode.

          Wirklich? Oder sind sie global eingebunden?

          Versuch einfach mal, mit deinem abstrakten Beispielcode zwei männliche Objekte anzulegen. Das dürfte voraussichtlich scheitern, weil du eine Funktion doppelt definieren willst.

          Das ist schon klar. Aber das passiert auch, wenn sie global eingebunden wird. In der neuen Factory-Klasse ist das kein Problem mehr, weil nun Klassen statt alleinstehender Funktionen eingebunden werden.

          test_m(); funktionierte ja aus dem globalen Kontext. Kann man eine in eine Funktion eingebundene Funktion

          function erste_ebene()
          {
          function test_m()
          {}
          }

          ebenfalls aus dem globalen Kontext mit test_m() aufrufen?

          Die Lösung ist meinem anderen Beitrag.

          Ja klar. Das Factory Pattern ist die übliche Lösung, wenn man über ein einziges Interface gleichartige, aber dennoch intern unterschiedliche Objekte herstellen will.

          Ja, ich wollte im Prinzip ja auch nur den Weg wissen, wie ich wirklich nur die benötigten Inhalte inkludieren kann. Ich hatte zunächst den Denkfehler, die Unterklassen müßten in PHP schon im Hauptspeicher vorhanden sein, um aus ihnen das konkrete Objekt zu erzeugen. Deshalb versuchte ich den Weg, keine Unterklassen einzubinden, sondern mit einer Klasse auszukommen und in diese dynamisch die nötigen Methoden einzubinden. Aber es ist nicht nötig, ein include der wirklich benötigten Unterklasse funktioniert, wie ich es will.

          1. Moin!

            test_m(); funktionierte ja aus dem globalen Kontext. Kann man eine in eine Funktion eingebundene Funktion

            function erste_ebene()
            {
            function test_m()
            {}
            }

            ebenfalls aus dem globalen Kontext mit test_m() aufrufen?

            Nein, nicht so, wie du denkst.

            Der erste Aufruf der Funktion erste_ebene() definiert die Funktion test_m(). Erst danach steht diese zur Verfügung. Jeder weitere Aufruf der Funktion erste_ebene() führt zum Versuch, die Funktion doppelt zu definieren, was mit einer Fehlermeldung quittiert wird.

            Du kannst Funktionen nicht ineinander schachteln, um deren Scope zu verändern.

            - Sven Rautenberg

            --
            "Love your nation - respect the others."
            1. echo $begrüßung;

              Du kannst Funktionen nicht ineinander schachteln, um deren Scope zu verändern.

              Oder etwas deutlicher formuliert: Es gibt unter PHP keinen anderen Kontext als den globalen für nicht klassengebundene Funktionen, egal an welcher Stelle sie deklariert werden.

              echo "$verabschiedung $name";

              1. Du kannst Funktionen nicht ineinander schachteln, um deren Scope zu verändern.

                Oder etwas deutlicher formuliert: Es gibt unter PHP keinen anderen Kontext als den globalen für nicht klassengebundene Funktionen, egal an welcher Stelle sie deklariert werden.

                Ok, das hat, denke ich, meine zuletzt gestellte Frage hinreichend beantwortet (bis auf ein sinnvolles Beispiel).

                1. echo $begrüßung;

                  Du kannst Funktionen nicht ineinander schachteln, um deren Scope zu verändern.
                  Oder etwas deutlicher formuliert: Es gibt unter PHP keinen anderen Kontext als den globalen für nicht klassengebundene Funktionen, egal an welcher Stelle sie deklariert werden.
                  Ok, das hat, denke ich, meine zuletzt gestellte Frage hinreichend beantwortet (bis auf ein sinnvolles Beispiel).

                  Ein Beispiel hast du mit deiner Factory-Lösung eigentlich schon, da inkludierte Dateien im Grunde nichts anderes sind als an dieser Stelle stehender Code*). Innerhalb einer Funktion kannst du beliebigen Code inkludieren, also nicht nur solchen, der Klassen deklariert sondern auch einfache Funktionen oder Konstanten definiert und Variablen erzeugt. Nur Variablen landen im lokalen Scope, alles andere wird global angelegt.

                  *) Es gibt da schon noch Unterschiede, aber die interessieren in diesem Zusammenhang nicht weiter.

                  echo "$verabschiedung $name";

                  1. Moin!

                    Ein Beispiel hast du mit deiner Factory-Lösung eigentlich schon, da inkludierte Dateien im Grunde nichts anderes sind als an dieser Stelle stehender Code*).

                    Wobei include() nicht an jeder beliebigen Code-Position erlaubt ist - beispielsweise nicht innerhalb von class{}, wohl aber innerhalb darin befindlicher function{}s.

                    - Sven Rautenberg

                    --
                    "Love your nation - respect the others."
                    1. Wobei include() nicht an jeder beliebigen Code-Position erlaubt ist - beispielsweise nicht innerhalb von class{}, wohl aber innerhalb darin befindlicher function{}s.

                      Und das ist eine sinnvolle Ergänzung.

                  2. Innerhalb einer Funktion kannst du beliebigen Code inkludieren, also nicht nur solchen, der Klassen deklariert sondern auch einfache Funktionen oder Konstanten definiert und Variablen erzeugt.

                    !!!

                    Nur Variablen landen im lokalen Scope, alles andere wird global angelegt.

                    !!!

                    Vielen Dank, das ist eine klare Aussage.

            2. Nein, nicht so, wie du denkst.

              Der erste Aufruf der Funktion erste_ebene() definiert die Funktion test_m(). Erst danach steht diese zur Verfügung. Jeder weitere Aufruf der Funktion erste_ebene() führt zum Versuch, die Funktion doppelt zu definieren, was mit einer Fehlermeldung quittiert wird.

              Das ist mir klar. Aber kann ich nun aus dem Hauptprogramm mit test_m() die Funktion aufrufen, oder ist sie nur der Funktion erste_ebene() bekannt? Mit anderen Worten, gibt es gar keine Ebenen, und liegt die Funktion test_m() nach dem Aufruf von erste_ebene() auf einer Ebene mit erste_ebene()?

              Du kannst Funktionen nicht ineinander schachteln, um deren Scope zu verändern.

              Also wahrscheinlich letzteres.

              Davon unabhängig. Kennst Du ein sinnvolles Beispiel, wo so eingebundene Funktionen sinnvoll sind? Wahrscheinlich auch nur, wenn es unterschiedliche Funktionen mit gleichem Namen gibt, von denen nur eine eingebunden werden soll/ darf.

  3. echo $begrüßung;

    Noch ein anderer Tipp:

    ich mache die ersten Gehversuche OOP unter PHP.
    class kunde {
        var $name;
        function kunde($name, $geschlecht) {

    Du solltest dich von der alten PHP-4-Syntax verabschieden und für neue Projekte die PHP5-OOP-Syntax verwenden. PHP5 ist seit Jahren verfügbar und PHP4s Endedatum verkündet.

    var ist zwar mittlerweile ein Synonym für public, trotzdem ist es nur nebenbei erwähnt und sollte zugunsten der mit PHP5 eingeführten Zugriffsmodifikatoren nicht verwendet werden.

    Der Konstruktor bekommt nicht mehr den Namen der Klasse sondern heißt __construct.

    echo "$verabschiedung $name";

    1. echo $begrüßung;

      Noch ein anderer Tipp:

      ich mache die ersten Gehversuche OOP unter PHP.
      class kunde {
          var $name;
          function kunde($name, $geschlecht) {

      Du solltest dich von der alten PHP-4-Syntax verabschieden und für neue Projekte die PHP5-OOP-Syntax verwenden. PHP5 ist seit Jahren verfügbar und PHP4s Endedatum verkündet.

      Danke für den Tip. Das weiß ich. Aber da vorläufig PHP 4 noch weit bei den Massenhostern verbreitet ist, möchte ich vorerst auf Befehle von PHP 5 verzichten bzw. abwärtskompatibel so programmieren, daß es auch ohne die neuen Befehle geht. Soweit ich weiß, kann man in PHP 5 Kostruktoren mit __construct und dem Klassenname gleichzeitig anlegen. Der Parser nimmt dann __construct, und unter PHP 4 die Methode mit dem Klassennamen. Das müßte ich mir aber noch mal genau ansehen.

      1. echo $begrüßung;

        Danke für den Tip. Das weiß ich. Aber da vorläufig PHP 4 noch weit bei den Massenhostern verbreitet ist,

        Die Verfügbarkeit von PHP5 bei den Massenhostern sollte mittlerweile gleich der von PHP4 sein. Wenn ein Provider immer noch kein PHP5 anbietet, ist es Zeit diesen zu verlassen, denn PHP4 bekommt ab 31.12.2007 keine neuen Versionen und spätestens ab 8.8.8 *) keine sicherheitskritischen Patches mehr.

        *) Ich hab mir dieses Datum nicht ausgesucht. Es steht im PHP 4 end of life announcement (News vom 13.7.2007)

        echo "$verabschiedung $name";