Ankündigung

Einklappen
Keine Ankündigung bisher.

10110: Spieglein, Spieglein an der Wand…

Einklappen

Neue Werbung 2019

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

  • 10110: Spieglein, Spieglein an der Wand…

    10110:
    Unter Reflexion oder auch Introspektion versteht man in der Programmierung die Möglichkeit, dass ein Programm zur Laufzeit die eigene Struktur untersuchen und gegebenenfalls auch modifizieren kann. Reflexion wird von den meisten Programmiersprachen, die Objektorientierung bieten, unterstützt, so auch von PHP. Zunächst stellt sich aber die Frage, wozu man Reflexion überhaupt benötigt. Nun ja, es gibt mehrere Gründe. Der eine ist, dass das Programm stark modular gehalten ist und deshalb einzelne Strukturteile variieren können. Ein sehr einfaches Beispiel von Reflexion hat sicherlich jeder schon einmal angewendet:
    PHP-Code:
    if (class_exists('MyClass')) {
        print 
    'Klasse existiert';

    Ein anderes Beispiel, bei dem es mehr um den Inhalt eines Objekts denn um dessen Vorhandensein geht, ist dieses:
    PHP-Code:
    $vars get_object_vars($myObject); 
    Ich denke, nun ist etwas klarer, worum es bei Introspektion geht.

    PHP bietet aber seit der Version 5.0.0 mit Einführung der Reflection-API eine weitaus mächtigere Möglichkeit, einzelne Klassen, Objekte oder Funktionen zu analysieren.
    Um diese Möglichkeiten zu demonstrieren, erstellen wir ersteinmal eine simple Klasse. Hier wurde ein stupides nutzloses Singleton gewählt, da man dort gleich auf Anhieb schön viele Methoden und auch eine Eigenschaft verwenden kann. Das Ganze ist natürlich auch pflichtbewusst dokumentiert.
    PHP-Code:
    <?php
    /**
     * Just a simple test class
    */
    class TestClass
    {
        
    /**
         * Contains the instance.
         * 
         * @var TestClass
        */
        
    private static $__instance null;
        
        
        
    /**
         * Returns the instance. Use this method instead of the constructor!
         * 
         * @return TestClass
        */
        
    public static function getInstance()
        {
            if (
    null === self::$__instance) {
                
    self::$__instance = new self;
            }
            
            return 
    self::$__instance;
        }
        
        
        
    /**
         * Protect access to constructor.
        */
        
    protected function __construct()
        {
        }
        
        
        
    /**
         * Disallow cloning.
        */
        
    private function __clone()
        {
        }
        
        
        
    /**
         * Return a random number.
         * Chosen by fair dice roll, guaranteed to be random.
         * Inspired by xkcd <http://xkcd.com/221/> :-)
         * 
         * @return integer
        */
        
    public function getRandomNumber()
        {
            return 
    4;
        }
    }
    Jetzt soll die Klasse analysiert werden. Um dies zu tun, wird die Klasse ReflectionClass herangezogen. Von dieser wird eine neue Instanz abgeleitet, wobei dem Konstruktor der Name der zu untersuchenden Klasse übergeben wird:
    PHP-Code:
    $testClassRefl = new ReflectionClass('TestClass'); 
    Soweit so schön. Um nun die Informationen über die reflektierte Klasse zu erhalten, muss man nicht viel herumprogrammieren, weil die Klasse die Methode __toString() implementiert und so ein simples echo oder print seinen Dienst tut:
    PHP-Code:
    print '<pre>' $testClassRefl '</pre>'
    Die Methode __toString() greift übrigens auf die statische Methode export() zu, weshalb man das auch anders machen kann:
    PHP-Code:
    print '<pre>' ReflectionClass::export(TestClass::getInstance(), true) . '</pre>'
    Doch das nur so am Rande…

    Das Ergebnis ist schonmal recht üppig:
    Code:
    /**
     * Just a simple test class
    */
    Class [  class TestClass ] {
      @@ C:\Programme\xampp\htdocs\test.php 5-57
    
      - Constants [0] {
      }
    
      - Static properties [1] {
        Property [ private static $__instance ]
      }
    
      - Static methods [1] {
        /**
         * Returns the instance. Use this method instead of the constructor!
         * 
         * @return TestClass
        */
        Method [  static public method getInstance ] {
          @@ C:\Programme\xampp\htdocs\test.php 20 - 27
        }
      }
    
      - Properties [0] {
      }
    
      - Methods [3] {
        /**
         * Protect access to constructor.
        */
        Method [  protected method __construct ] {
          @@ C:\Programme\xampp\htdocs\test.php 33 - 35
        }
    
        /**
         * Disallow cloning.
        */
        Method [  private method __clone ] {
          @@ C:\Programme\xampp\htdocs\test.php 41 - 43
        }
    
        /**
         * Return a random number.
         * Chosen by fair dice roll, guaranteed to be random.
         * Inspired by xkcd <http://xkcd.com/221/> :-)
         * 
         * @return integer
        */
        Method [  public method getRandomNumber ] {
          @@ C:\Programme\xampp\htdocs\test.php 53 - 56
        }
      }
    }
    Man sieht: die gesamte Struktur der Klasse wurde in einem Pseudocode wiedergegeben und sogar die DocBlocks wurden (zumindest bei der Klasse selbst und den Methoden) beachtet. So lassen sich z.B. auch Dokumentationen generieren.
    Diese Ausgabe ist jedoch etwas unpraktisch und dient mehr der Übersicht oder zu Debugging-Zwecken. Die Analyse lässt sich auch etwas verfeinern, indem man nur einzelne Elemente betrachtet. Hierfür bietet die Klasse ReflectionClass viele, viele weitere Methoden, von denen eine kleine Auswahl hier vorgestellt wird:
    PHP-Code:
    $testClassRefl = new ReflectionClass('TestClass');
    print 
    'Name: '          $testClassRefl->getName()       . '<br>';
    print 
    'File: '          $testClassRefl->getFileName()   . '<br>';
    print 
    'Lines: '         $testClassRefl->getStartLine()  . ' unto ' $testClassRefl->getEndLine() . '<br>';
    print 
    'DocBlock: <pre>' $testClassRefl->getDocComment() . '</pre>'
    Das Ergebnis:
    Code:
    Name: TestClass
    File: C:\Programme\xampp\htdocs\test.php
    Lines: 5 unto 57
    DocBlock: 
    /**
     * Just a simple test class
    */
    Damit ist schonmal die Struktur der Klasse selbst erfragt. Kommen wir zu den Methoden. Um Methoden zu erfragen, existiert die Methode getMethods(), welche ein Array mit Objekten für die einzelnen Methoden zurückgibt.
    PHP-Code:
    $testClassRefl = new ReflectionClass('TestClass');
    print 
    '<pre>' print_r($testClassRefl->getMethods(), true) . '</pre>'
    Code:
    Array
    (
        [0] => ReflectionMethod Object
            (
                [name] => getInstance
                [class] => TestClass
            )
    
        [1] => ReflectionMethod Object
            (
                [name] => __construct
                [class] => TestClass
            )
    
        [2] => ReflectionMethod Object
            (
                [name] => __clone
                [class] => TestClass
            )
    
        [3] => ReflectionMethod Object
            (
                [name] => getRandomNumber
                [class] => TestClass
            )
    
    )
    Das Ergebnis zeigt, dass jede Methode vom Typ ReflectionMethod ist. Diese Klasse bietet ähnliche Methoden wie die Klasse ReflectionClass, die wir schon behandelt haben.
    Nicht immer will man aber alle Methoden abfragen, weshalb es auch möglich ist, eine bestimmte Methode direkt anzusprechen:
    PHP-Code:
    $testClassRefl = new ReflectionClass('TestClass');
    print 
    '<pre>' $testClassRefl->getMethod('getRandomNumber') . '</pre>'
    Das Ergebnis ist dem ersten Ergebnis recht ähnlich:
    Code:
    /**
         * Return a random number.
         * Chosen by fair dice roll, guaranteed to be random.
         * Inspired by xkcd <http://xkcd.com/221/> :-)
         * 
         * @return integer
        */
    Method [  public method getRandomNumber ] {
      @@ C:\Programme\xampp\htdocs\test.php 53 - 56
    }
    Es ist jedoch nicht nur möglich, Methoden zu analysieren, man kann sie sogar ausführen und auch ihr Ergebnis verwerten. Da Methoden jedoch in aller Regel im Kontext eines Objekts aufgerufen werden, muss zunächst eine Instanz der zu analysierenden Klasse erstellt werden. Um Methoden dann schließlich auszuführen, kann die Methode invoke() benutzt werden, die eine variable Anzahl an Parametern erlaubt. Der erste ist dabei Pflicht und fordert das Objekt der Klasse, die restlichen sind optional und sind die Parameter, die an die auszuführende Methode übergeben werden. In unserem Falle haben wir aber keine Parameter.
    PHP-Code:
    $testObj TestClass::getInstance();
    $testClassRefl = new ReflectionClass('TestClass');
    print 
    $testClassRefl->getMethod('getRandomNumber')->invoke($testObj); 
    Das Ergebnis ist (wie zu erwarten war) 4.
    Analog zu getMethods()/getMethod() existieren natürlich auch getProperties()/getProperty(). Da diese jedoch sehr ähnlich funktionieren, wird an dieser Stelle nicht näher darauf eingegangen.

    Neben der ReflectionClass gibt es auch noch eine Reihe anderer Klassen, von denen ich mir jetzt aber nur eine heraussuche und zwar ReflectionObject. Diese dient dazu, ein Objekt zu inspizieren und sorgt dabei gerne für Verwirrung. Der Name kann nämlich leicht in die Irre führen, da hierbei nicht wirklich das Objekt selbst, sondern die Klasse dieses Objekts untersucht wird. Dies kann man auch leicht daran sehen, dass ReflectionObject eine Kindklasse von ReflectionClass ist. Der größte Unterschied besteht lediglich darin, dass dem Konstruktor nicht der Name der Klasse sondern ein Objekt übergeben wird:
    PHP-Code:
    $testObj TestClass::getInstance();
    $testObjRefl = new ReflectionObject($testObj);
    print 
    '<pre>' $testObjRefl '</pre>'
    Das Ergebnis ist fast identisch, lediglich die Beschriftung der Klasse hat sich geändert. Außerdem ist die Sektion Dynamic properties hinzugekommen, welche Eigenschaften beinhaltet, die erst zur Laufzeit hinzugefügt wurden.
    Es geht hier also wirklich nur um die Struktur; für eine Analyse des Status eines Objekts zur Laufzeit muss daher auf get_object_vars() zurückgegriffen werden.
    Code:
    /**
     * Just a simple test class
    */
    Object of class [  class TestClass ] {
      @@ C:\Programme\xampp\htdocs\test.php 5-57
    
      - Constants [0] {
      }
    
      - Static properties [1] {
        Property [ private static $__instance ]
      }
    
      - Static methods [1] {
        /**
         * Returns the instance. Use this method instead of the constructor!
         * 
         * @return TestClass
        */
        Method [  static public method getInstance ] {
          @@ C:\Programme\xampp\htdocs\test.php 20 - 27
        }
      }
    
      - Properties [0] {
      }
    
      - Dynamic properties [0] {
      }
    
      - Methods [3] {
        /**
         * Protect access to constructor.
        */
        Method [  protected method __construct ] {
          @@ C:\Programme\xampp\htdocs\test.php 33 - 35
        }
    
        /**
         * Disallow cloning.
        */
        Method [  private method __clone ] {
          @@ C:\Programme\xampp\htdocs\test.php 41 - 43
        }
    
        /**
         * Return a random number.
         * Chosen by fair dice roll, guaranteed to be random.
         * Inspired by xkcd  :-)
         * 
         * @return integer
        */
        Method [  public method getRandomNumber ] {
          @@ C:\Programme\xampp\htdocs\test.php 53 - 56
        }
      }
    }
    Die Klasse selbst funktioniert sonst aber genauso wie ReflectionClass auch und bedarf deshalb keiner genaueren Beschreibung.

    Ihr merkt: Reflexion ist ein umfangreiches Thema, sogar so umfangreich, dass hier nur ein kleiner Einblick gegeben werden kann, doch ist meist gerade dieser Einblick besonders wichtig, da Die Reflection-API zu Anfang oft ein wenig schwer zu verstehen ist und das PHP-Manual die Verwirrung meist nur noch größer macht. Hat man jedoch einmal einen Einblick bekommen, sollte der Rest klar sein. Deshalb hier auch der Link zu offiziellen Dokumentation:


  • #2
    Schön. Hatten wir MODs auch noch ne kleine Überraschung. Danke.
    --

    „Emoticons machen einen Beitrag etwas freundlicher. Deine wirken zwar fachlich richtig sein, aber meist ziemlich uninteressant.
    Wenn man nur Text sieht, haben viele junge Entwickler keine interesse, diese stumpfen Texte zu lesen.“


    --

    Kommentar


    • #3
      interessant ist auch ReflectionExtension>
      PHP-Code:
      // Create an instance of the ReflectionExtension class
      $ext = new ReflectionExtension('standard');

      // Print out basic information
      printf(
          
      "Name        : %s\n" .
          
      "Version     : %s\n" .
          
      "Functions   : [%d] %s\n" .
          
      "Constants   : [%d] %s\n" .
          
      "INI entries : [%d] %s\n" .
          
      "Classes     : [%d] %s\n",
              
      $ext->getName(),
              
      $ext->getVersion() ? $ext->getVersion() : 'NO_VERSION',
              
      sizeof($ext->getFunctions()),
              
      var_export($ext->getFunctions(), 1),

              
      sizeof($ext->getConstants()),
              
      var_export($ext->getConstants(), 1),

              
      sizeof($ext->getINIEntries()),
              
      var_export($ext->getINIEntries(), 1),

              
      sizeof($ext->getClassNames()),
              
      var_export($ext->getClassNames(), 1)
      ); 
      PHP4?!?>>>Aktuelle PHP Version: 5.2.11 || 5.3.0
      Suse 11.2 *vorfreude*

      Kommentar

      Lädt...
      X