Ankündigung

Einklappen
Keine Ankündigung bisher.

[Erledigt] PHP5: "Private Lazy Initialization" mit

Einklappen

Neue Werbung 2019

Einklappen
X
  • Filter
  • Zeit
  • Anzeigen
Alles löschen
neue Beiträge

  • [Erledigt] PHP5: "Private Lazy Initialization" mit

    Guten Tag!

    Folgender Sachverhalt:

    Zu einem bestimmten Problem habe ich mehrere Klassen und eine sogenannte Managerklasse fuer diese entwickelt. Die Managerklasse handhabt und "wrappt" den Zugriff auf die Objekte der anderen Klassen, die als private Member initalisiert werden.

    Extremes Beispiel zur Verdeutlichung:

    PHP-Code:
    <?php
    require 'AnotherClass.php';

    class 
    Manager
    {
      private 
    $InstanceOfAnotherClass;
      public function 
    __construct()
      {
        
    $this->InstanceOfAnotherClass = new AnotherClass();
      }
      
    // Eine in diesem Fall sinnlose Wrapperfunktion
      
    public function MakeThis()
      {
        return 
    $this->InstanceOfAnotherClass->foo();
      }
    }

    $bar = new Manager();
    print 
    $bar->MakeThis(); // Wrapper
    print $bar->InstanceOfAnotherClass->foo(); // resultiert, wie gewuenscht, in einem Fehler
    ?>
    Stellt euch nun vor, ich braeuchte nur einige dieser Klassen immer und die anderen eher selten. Diese anderen Klassen wuerden den Speicher (bzw. den Server) bei einer Nichtverwendung sinnlos belasten, was aber definitiv nicht mein Ziel ist.
    Dafuer habe ich die magische Funktion __get() in der Klasse Manager implementiert. Soweit, so gut. Jetzt aber das eigentliche Problem:
    Auf diese Art und Weise (die mir einzig bekannte) vergisst PHP5 das private-Attribut.

    (Diese Verfahrensweise nennt man "Lazy Initialization")

    PHP-Code:
    <?php

    class Manager
    {
      
    // Hier darf die Membervariable $InstanceOfAnotherClass noch nicht definiert sein
      
    public function __construct()
      {
        
    // Hier wird ersteinmal nichts gemacht
      
    }
      
      
    // Auch mit private Klappt es nicht
      
    private function __get($member)
      {
        switch (
    $member):
          case 
    'InstanceOfAnotherClass':
            
    // Erst beim Gebrauch laden und instanziieren
            
    include 'AnotherClass.php';
            
    $this->InstanceOfAnotherClass = new AnotherClass();
            return 
    $this->InstanceOfAnotherClass;
            break;
          
    // Den Rest lassen wir jetzt einmal aussen vor
        
    endswitch;
      }

      
    // Eine in diesem Fall sinnlose Wrapperfunktion
      
    public function MakeThis()
      {
        
    // Erwuenscht: Initalisierung erst beim Aufruf durch eine Wrapperfunktion, die Classmember ist
        
    return $this->InstanceOfAnotherClass->foo();
      }
    }

    $bar = new Manager();
    // erwuenscht, da Wrapper
    print $bar->MakeThis();

    // unerwuenscht, aber leider moeglich: $InstanceOfAnotherClass sollte private sein
    print $bar->InstanceOfAnotherClass->foo();
    ?>
    Man kann trotz des private-Attributes noch von aussen zugreifen. Eine Definition "private $InstanceOfAnotherClass" würde keinen Sinn ergeben, da PHP die Funktion __get() aus den Methoden dieser Klasse nicht aufrufen würde, denn fuer diese gilt ja der Zugriff auf private Member.
    Hat jemand dieses Problem schon mal gehabt und eventuell sogar eine Loesung parat?? Gibt es irgendwelche Internetseiten, die fuer dieses Problem eine Loesung bieten? Meine Suche ueber Google blieb erfolglos.

    Vielen Dank im Voraus.

    PS. Es gibt anscheinend Probleme mit der Codierung eurer Webinhalte! (anstatt z.B. eines "ö" kommt bei mir "?", Mozilla Firefox 1.0.7)

  • #2
    Ich habe mir das jetzt nicht näher angeschaut, aber wenn das wirklich so ist solltest Du Dich besser an die PHP Entwickler wenden und sie bitten das zu beheben, oder sie zumindest fragen was sie sich dabei gedacht haben. Meiner Meinung nach sind __get() und __set() nur fürs Prototyping, oder "Quick and Dirty Scripts" zu gebrauchen weil sie eine Dokumentationslücke hinterlassen. Jemand der sich aus deinem Code eine Doku generiert bekommt keine Information über Deine "InstanceOfAnotherClass".
    Lazy Initialization würde ich so machen:

    PHP-Code:
    <?php
    class Manager
    {
        private 
    $instanceOfAnotherClass NULL;

        private function 
    getInstanceOf AnotherClass()
        {
            
    //erst beim ersten Aufruf wird das Objekt initialisiert
            
    if (is_null($this->instanceOfAnotherClass)) {
                include_once(
    'AnotherClass.php');
                
    $this->instanceOfAnotherClass = new AnotherClass();
            }
            return 
    $this->instanceOfAnotherClass;
        }
    }
    ?>

    Kommentar


    • #3
      Um das ans laufen zu bringen, müsste man wohl an __get() etc. übergeben, von wo aus sie aufgerufen werden, damit man innerhalb der Methode regeln kann, wer sie aufrufen darf. Da __get() immer für alle abgefangenen Variablen zuständig ist, kann man das nicht von aussen Regeln.
      Und da die ZendEngine das wohl auf absehbare Zeit nicht tun wird (Patch einschicken hilft, ausser er wird abgelehnt), kann man sich wohl hässlicherweise an debug_backtrace() vergreifen... oder die Konstruktion über den Haufen werfen.

      Ach, mit __get __set und __call kann man schöne Decorator bauen. Und Doku kommt nicht nur aus dem PHPDocumentor.
      mod = master of disaster

      Kommentar


      • #4
        Zitat von Waq
        Ach, mit __get __set und __call kann man schöne Decorator bauen.
        Hört sich interessant an, könntest Du das näher erläutern?

        Kommentar


        • #5
          Wie es aussieht muss noch ein wenig an den Objekt-Überladungsfähigkeiten von PHP5 gearbeitet werden.
          Ich habe im Laufe der Woche fast alle möglichen Varianten probiert, aber keine läuft so, wie man es theoretisch erwartet. Wenigstens für mein Problem habe ich eine Lösung gefunden, die aber wiederum mehr Schreibarbeit benötigt (es ist schon ohne dies ein großes Projekt), und somit die Wartbarkeit und die Skalierbarkeit des PHP-Codes beeinträchtigt, was doch eigentlich nicht das Ziel dieser neuen Sprachfeatures von PHP 5 war... naja.

          Ich werde immer mal gucken ob und wie sich dieses Verhalten geändert hat und dann hier im Forum Bescheid sagen.

          Und vielen Dank für eure Hilfe, hier werde ich wahrscheinlich öfters vorbeischauen!

          Grüße

          Martin

          Kommentar


          • #6
            Zitat von mmorgenstern
            keine läuft so, wie man es theoretisch erwartet.
            Doch durchaus.
            Ob __get() private ist oder nicht darf keine Auswirkungen haben, da __get() für alle abgefangenen Variablen zuständig ist.
            Und wenn die Variable als private definiert ist, ist sie definiert und __get() wird nicht mehr aufgerufen.

            Privates __get() ist im Moment einfach nicht vorgesehen, es verhält sich aber alles so, wie es soll.
            mod = master of disaster

            Kommentar


            • #7
              Erstaunlich?? Nein, kein Wunder. Ich teste mit PHP5.1RC1 auf meinem Apache. Und debuggen aber im ZendStudio, welches PHP 5.0.4 verwendet.

              Folgender Code gibt unterschiedliche Ergebnisse:

              PHP-Code:
              <?php

              class foo
              {
                  private 
              $bar 32;
                  public function 
              __get($member)
                  {
                      if (
              $member == 'bar')
                          return 
              132;
                  }
              }

              $foo = new foo;
              print 
              $foo->bar;


              ?>
              PHP 5.0.4:
              Fatal error: Cannot access private property foo::$bar in C:\PHP5Dev\tests\overloading\test2.php on line 14

              PHP 5.1RC1:
              132

              Kommentar


              • #8
                In PHP 5.1 wird der Zugriff von aussen auf eine private Variable als "findet nix" interpretiert, weswegen __get() aufgerufen wird.
                In PHP 5.0 wird die private Variable gefunden, was im fatal error endet.

                Ein Zugriff von innen wird nie __get() aufrufen, da $bar ja schon definiert ist.
                mod = master of disaster

                Kommentar


                • #9
                  Ja okay, die Frage ist nur welche Variante nun ein Bug ist oder nicht. Das Verhalten von PHP5.1 wäre richtiger als bei 5.0, da private und protected nicht sichtbar sind und somit die Funktionen __get, __call und __set greifen, egal ob klassenintern die member schon definiert wurden oder nicht. Sonst wären diese Funktionen doch sinnlos. Bei public ist das Verhalten auch okay, dort wird kein __get verwendet wenn die Eigenschaft existiert.

                  Wenn man von innen (also von der Klasse aus) auf __get zurückgreifen will, darf man die Eigenschaft noch nicht definieren (wie im 2. Beispiel ganz oben). Eigentlich auch ein logisches Verhalten. Aber alles irgendwie nicht richtig dokumentiert...

                  Kommentar


                  • #10
                    Ein Bug ist keins, die Theorie hat sich von 5.0 zu 5.1 halt verändert.
                    Im Endeffekt ist diese Abfangerei nicht auf die Feinheiten von Sichtbarkeitsmodellen ausgelegt, sondern eher auf ein gibbet oder gibbet nich.
                    Nützlich um schnell nen Decorator zu schreiben, oder zur Lazy-Initialisierung ohne private-Spirenzchen.
                    mod = master of disaster

                    Kommentar


                    • #11
                      Das hab mich mir halt auch gerade gedacht. Eigentlich schade.

                      Kommentar

                      Lädt...
                      X