{"id":3430,"date":"2018-04-11T09:10:24","date_gmt":"2018-04-11T09:10:24","guid":{"rendered":"https:\/\/anexia.com\/blog\/de\/?p=3430"},"modified":"2022-04-19T07:28:11","modified_gmt":"2022-04-19T05:28:11","slug":"reflection-in-php-projekten","status":"publish","type":"post","link":"https:\/\/anexia.com\/blog\/de\/reflection-in-php-projekten\/","title":{"rendered":"Reflection in PHP Projekten"},"content":{"rendered":"<p>Unter dem Begriff \u201eReflection\u201c versteht man in der <a href=\"https:\/\/anexia.com\/de\/softwareentwicklung\/\">Softwareentwicklung<\/a>, dass ein Programm zur Laufzeit seine eigene Struktur kennt und diese auch modifizieren kann. Diese F\u00e4higkeit wird unter anderem auch als die sogenannte \u201eIntrospection\u201c bezeichnet. Im PHP-Bereich findet Reflection Anwendung bei der Sicherstellung von Typsicherheit im Programmcode. Wie das konkret funktioniert, und warum sie ein sehr n\u00fctzliches Hilfsmittel ist, wird im Folgenden erkl\u00e4rt.<\/p>\n<h2>Typsicherheit in PHP<\/h2>\n<p><a href=\"https:\/\/anexia.com\/de\/softwareentwicklung\/webentwicklung\/php-entwicklung\/\">PHP<\/a> ist nicht gerade bekannt daf\u00fcr, dass es eine typisierte Sprache ist. Es l\u00e4sst sich herzlich dar\u00fcber streiten, ob man das nun positiv oder negativ betrachten soll \u2013 beide Seiten haben ihre Argumente.<\/p>\n<p>Grunds\u00e4tzlich unterscheidet man in PHP zwischen vier grundlegenden Datentypen: Skalar, Array, Objekt und Ressource.\u00a0In den Bereich der skalaren Datentypen fallen u.a. \u00a0\u201eBoolean\u201c, \u201eInteger\u201c, \u201eDouble\u201c und \u201eString\u201c.\u00a0Objekt-Klassen dagegen sind komplexe Datenstrukturen, die sich aus den oben angef\u00fchrten Typen individuell zusammensetzen.<\/p>\n<h2>Auswirkung der schwachen Typisierung<\/h2>\n<p>Im Gegensatz zu stark typisierten Sprachen, wie beispielsweise Java oder C, k\u00f6nnen sich in PHP die Typen, der im Speicher befindlichen Daten dynamisch \u00e4ndern. So wird beispielsweise versucht, von skalaren Typen auch stets einen Integer-Wert zu ermitteln. Auch m\u00fcssen in PHP Variablen nicht explizit deklariert werden. Das bedeutet, dass der Deklaration einer neuen Variable hier nicht unbedingt ein Datentyp angegeben werden muss.<\/p>\n<p>Dies hat einerseits den Vorteil, dass man flexibel in der Verwendung dieser Variable ist, da man deren Verwendung in Hinblick auf den Datentyp stets dynamisch modifizieren kann, ohne dass es zu Problemen mit dem Compiler kommt. Was nun f\u00fcr viele sehr verlockend klingt, birgt jedoch auch einen entscheidenden Nachteil: angenommen dem Entwickler unterl\u00e4uft ein kleines Missgeschick, und er beginnt mitten im Programmcode beispielsweise ein Array pl\u00f6tzlich als Objekt neu zu initialisieren. F\u00fcr den weiteren Verlauf k\u00f6nnte es problemlos klappen \u2013 was ist aber mit dem restlichen Code, welcher von dem urspr\u00fcnglichen Array ausgeht?<\/p>\n<p>Um das Verhalten von PHP zu demonstrieren, betrachten wir mal das folgende Beispiel:<\/p>\n<pre class=\"lang:php decode:true \">$foo = \"image\";\r\n\r\n$foo == \"image\"\t\t\/\/ true\r\n$foo == 1\t\t\t\/\/ false\r\n$foo == 0\t\t\t\/\/ true\r\n$foo === 0 \t\t\t\/\/ false\r\n<\/pre>\n<p>Wie man sieht, entspricht der String \u201eimage\u201c dem numerischen Wert \u201e0\u201c. Dies liegt daran, dass PHP versucht, den String als erstes in einen Integer zu konvertieren, was in diesem Fall auch klappt. W\u00fcrde man der <strong>intval()<\/strong>-Funktion diesen String \u00fcbergeben, so erh\u00e4lt man tats\u00e4chlich den Wert \u201e0\u201c \u2013 und ein Vergleich von \u201e0\u201c und \u201e0\u201c ist nun mal \u201etrue\u201c. Umgehen l\u00e4sst sich dies beispielsweise durch striktere typensichere Operatoren, wie \u201e===\u201c und \u201e!==\u201c.<\/p>\n<h2>Die Reflection-API in PHP<\/h2>\n<p>Wer viel mit Frameworks arbeitet, kann sofort best\u00e4tigen, dass diese sehr stark mit Annotations oder Enums arbeiteten. Zun\u00e4chst kann man diese Tatsache einfach mal akzeptieren. Wenn man nun aber bedenkt, dass PHP diese Sprach-Konstrukte eigentlich \u00fcberhaupt nicht unterst\u00fctzt, kommt schnell mal die Frage auf: Wozu dienen diese dann \u00fcberhaupt? Mit folgendem Beispiel, wollen wir den Zweck solcher Konstrukte demonstrieren, und die Magie in dem Ganzen verdeutlichen.<\/p>\n<h3>Ein Beispiel<\/h3>\n<p>Bereiten wir eine einfache Beispiel-Klasse vor:<\/p>\n<pre class=\"lang:php decode:true\">class Example {\r\n\r\n\tprivate $attribute1;\r\n\r\n\tprotected $attribute2;\r\n\r\n\tpublic $attribute3;\r\n\r\n\tconst PI = 3.1415;\r\n\r\n\tpublic function __construct() {\r\n\t\t$this-&gt;attribute1 = \"Lorem ipsum\";\r\n\t\t$this-&gt;attribute2 = 36;\r\n\t\t$this-&gt;attribute3 = 2 * self::PI;\r\n\t}\r\n\r\n\r\n\tpublic function getAttribute1() {\r\n\t\treturn $this-&gt;attribute1;\r\n\t}\r\n\t\r\n\tpublic function setAttribute1($attribute1) {\r\n\t\t$this-&gt;attribute1 = $attribute1;\r\n\t}\r\n\r\n\tprivate function getAttribute2() {\r\n\t\treturn $this-&gt;attribute2;\r\n\t}\r\n\t\r\n}\r\n<\/pre>\n<p>Wie man sieht, stellt diese Beispielklasse ein paar einfache Methoden und Attribute bereit. Sie wird uns in den n\u00e4chsten Beispielen als einfacher Ausgangspunkt dienen.<\/p>\n<h4>Objekt-Dump ausgeben<\/h4>\n<p>Mit Hilfe der Relection-API ist es m\u00f6glich, die Eigenschaften dieser Klasse von au\u00dfen zu analysieren. Um einen vollst\u00e4ndigen Export zu erhalten, stellt <strong>Reflection()<\/strong> die statische Methode <strong>export()<\/strong> zur Verf\u00fcgung.<\/p>\n<pre class=\"lang:ps decode:true \">$reflection_class = new ReflectionClass(\"Example\");\r\n\r\nforeach ($reflection_class-&gt;getMethods() as $method) {\r\n\techo $method-&gt;getName() . \"\\n\";\r\n}\r\n<\/pre>\n<p>Als Resultat erh\u00e4lt man dabei Folgendes:<\/p>\n<ul>\n<li>__construct<\/li>\n<li>getAttribute1<\/li>\n<li>setAttribute1<\/li>\n<li>getAttribute2<\/li>\n<\/ul>\n<p>Als Parameter erwartet die <strong>ReflectionClass()<\/strong> lediglich den vollen Klassennamen (inkl. Namespace, sofern vorhanden) der zu analysierenden Klasse. Um die Initialisierung noch ein wenig sch\u00f6ner zu gestalteten, kann man hier auch direkt die Klassennamen-Aufl\u00f6sung von PHP nutzen:<\/p>\n<pre class=\"lang:php decode:true \">$reflection_class = new ReflectionClass( Example::class );<\/pre>\n<p>Dies ist besonders dann sinnvoll, wenn viel mit Namespaces gearbeitet wird.<\/p>\n<p>Neben den Methoden-Namen lassen sich aber auch andere Informationen auslesen:<\/p>\n<pre class=\"lang:php decode:true \">$reflection_class = new ReflectionClass( Example::class );\r\n\r\nforeach ($reflection_class-&gt;getMethods() as $method) {\r\n\techo $method-&gt;getName() . \"\\n\";\r\n\t\r\n\techo \"Number of parameters: \" . $method-&gt;getNumberOfParameters() . \"\\n\";\r\n\techo \"Is private: \" . ($method-&gt;isPrivate() ? 'Yes' : 'No') . \"\\n\\n\";\r\n}<\/pre>\n<p>Als Resultat erh\u00e4lt man dabei Folgendes:<\/p>\n<ul>\n<li>__construct<br \/>\nNumber of parameters: 0<br \/>\nIs private: No<\/li>\n<li>getAttribute1<br \/>\nNumber of parameters: 0<br \/>\nIs private: No<\/li>\n<li>setAttribute1<br \/>\nNumber of parameters: 1<br \/>\nIs private: No<\/li>\n<li>getAttribute2<br \/>\nNumber of parameters: 0<br \/>\nIs private: Yes<\/li>\n<\/ul>\n<p>&nbsp;<\/p>\n<h4>Attribute auslesen<\/h4>\n<p>Das gleiche Verhalten l\u00e4sst sich \u00fcbrigens auch auf Attribute anwenden. Anstelle von <strong>getMethods()<\/strong> verwendet man hierf\u00fcr einfach <strong>getProperties()<\/strong>.<\/p>\n<pre class=\"lang:php decode:true \">$reflection_class = new ReflectionClass(Example::class);\r\n\r\nforeach ($reflection_class-&gt;getProperties() as $property) {\r\n    echo $property-&gt;getName() . \"\\n\";\r\n    \r\n    echo \"Is private: \" . ($property-&gt;isPrivate() ? 'Yes' : 'No') . \"\\n\\n\";\r\n}<\/pre>\n<p>Das Resultat ist hier vergleichbar mit jenem bei den Methoden zuvor:<\/p>\n<ul>\n<li>attribute1<br \/>\nIs private: Yes<\/li>\n<li>attribute2<br \/>\nIs private: No<\/li>\n<li>attribute3<br \/>\nIs private: No<\/li>\n<\/ul>\n<p>&nbsp;<\/p>\n<h4>Methoden mittels Reflection ausf\u00fchren<\/h4>\n<p>Die Reflection-API hat nebenbei \u00fcbrigens auch eine Besonderheit. So lassen sich mit Hilfe von ihr auch private Methoden direkt ausf\u00fchren, was im normalen Programmkontext gar nicht m\u00f6glich w\u00e4re.<\/p>\n<pre class=\"lang:php decode:true\">$object = new Example();\r\n\r\n$reflection_object = new ReflectionObject($object);\r\n$method = $reflection_object-&gt;getMethod(\"getAttribute1\");\r\n\r\ntry {\r\n\techo $method-&gt;invoke($object);\r\n\t\r\n} catch (ReflectionException $e) {\r\n\techo \"Forbidden!\";\r\n}\r\n<\/pre>\n<p>Als Ausgabe erh\u00e4lt man nun den String \u201eLorem ipsum\u201c. Das liegt daran, dass die aufgerufene Methode <strong>getAttribute1()<\/strong> das Attribut <strong>attribute1<\/strong> zur\u00fcckliefert, welches im Konstruktor der Klasse mit diesem String initialisiert wurde.<\/p>\n<p>Wie man sieht, hat der Aufruf dieser Methode wunderbar geklappt.<\/p>\n<p>Versuchen wir im n\u00e4chsten Schritt nun unsere private Methode <strong>getAttribute2()<\/strong> aufzurufen, indem wir in der <strong>getMethod()<\/strong> den gew\u00fcnschten Methodennamen angeben:<\/p>\n<pre class=\"lang:php decode:true \">$object = new Example();\r\n\r\n$reflection_object = new ReflectionObject($object);\r\n$method = $reflection_object-&gt;getMethod(\"getAttribute2\");\r\n\r\ntry {\r\n\techo $method-&gt;invoke($object);\r\n\t\r\n} catch (ReflectionException $e) {\r\n\techo \"Forbidden!\";\r\n}<\/pre>\n<p>Als Ausgabe erhalten wir nun \u201eForbidden!\u201c wie in unserem Exception-Handling definiert. Dies liegt \u2013 wie erwartet \u2013daran, dass man auf private Methoden au\u00dferhalb der instanziierten Klasse nicht direkt zugreifen kann.<\/p>\n<p>Genau f\u00fcr diesen Fall bietet die Reflection-API mit der <strong>setAccessible()<\/strong>-Methode jedoch ein n\u00fctzliches Hilfsmittel.<\/p>\n<pre class=\"lang:php decode:true \">$object = new Example();\r\n\r\n$reflection_object = new ReflectionObject($object);\r\n$method = $reflection_object-&gt;getMethod(\"getAttribute2\");\r\n\r\ntry {\r\n\t$method-&gt;setAccessible(true);\r\n\techo $method-&gt;invoke($object);\r\n\t\r\n} catch (ReflectionException $e) {\r\n\techo \"Forbidden!\";\r\n}<\/pre>\n<p>Nun erhalten wir als Ausgabe die Zahl \u201e36\u201c, wie in unserer Klasse deklariert.<\/p>\n<p>Mittels <strong>setAccessible()<\/strong> l\u00e4sst sich das private Verhalten der Methode f\u00fcr die weitere Ausf\u00fchrung von dieser deaktivieren.<\/p>\n<p>Allerdings ist dies f\u00fcr die Praxis nicht sehr empfehlenswert, da es meistens Gr\u00fcnde hat, warum manche Methoden als privat definiert wurden. Eine m\u00f6gliche Verwendung von solchen Techniken k\u00f6nnte beispielsweise f\u00fcr Unit-Tests interessant werden.<\/p>\n<h3>Weiteres Beispiel: Entit\u00e4ten-Objekt mit Auto-Fill<\/h3>\n<p>Mit diesem Beispiel m\u00f6chten wir euch nun die Anwendung der Reflection-API in der Praxis zeigen. Um das entsprechend zu veranschaulichen, werden wir anhand eines Entit\u00e4ten-Objekts demonstrieren, wie man es auch bereits von vielen Frameworks kennt.<\/p>\n<p>Dazu bauen wir uns zuerst ein Basis-Element, von welchem wir sp\u00e4ter unsere individuellen Entit\u00e4ten ableiten k\u00f6nnen. Wir gehen davon aus, dass jede Entit\u00e4t folgende Attribute besitzt (angelehnt an das Laravel-Framework):<\/p>\n<ul>\n<li>id<\/li>\n<li>createdAt<\/li>\n<li>updatedAt<\/li>\n<li>deletedAt<\/li>\n<\/ul>\n<pre class=\"lang:php decode:true\">\/**\r\n * Class BaseElement\r\n *\/\r\nabstract class BaseElement {\r\n\r\n\t\/**\r\n\t * @var int $id\r\n\t *\/\r\n\tprivate $id;\r\n\r\n\t\/**\r\n\t * @var \\DateTime $createdAt\r\n\t *\/\r\n\tprivate $createdAt;\r\n\r\n\t\/**\r\n\t * @var \\DateTime $updatedAt\r\n\t *\/\r\n\tprivate $updatedAt;\r\n\r\n\t\/**\r\n\t * @var \\DateTime $deletedAt\r\n\t *\/\r\n\tprivate $deletedAt;\r\n\r\n\r\n\t\/**\r\n\t * @var array\r\n\t *\/\r\n\tprivate $_hidden = [ 'deleted_at' ];\r\n\r\n\r\n\t\/**\r\n\t * BaseElement constructor.\r\n\t *\r\n\t * @param array $data\r\n\t *\/\r\n\tpublic function __construct( $data = null ) {\r\n\t\t\r\n\t}\r\n\r\n\r\n\r\n\t\/**\r\n\t * Returns the ID.\r\n\t *\r\n\t * @return int\r\n\t *\/\r\n\tpublic function getId() {\r\n\t\treturn $this-&gt;id;\r\n\t}\r\n\r\n\r\n\t\/**\r\n\t * Set the ID.\r\n\t *\r\n\t * @param int $id\r\n\t *\/\r\n\tpublic function setId( int $id ) {\r\n\t\t$this-&gt;id = $id;\r\n\t}\r\n\r\n\r\n\t\/**\r\n\t * Returns the creation date.\r\n\t *\r\n\t * @return \\DateTime\r\n\t *\/\r\n\tpublic function getCreatedAt() {\r\n\t\treturn $this-&gt;createdAt;\r\n\t}\r\n\r\n\r\n\t\/**\r\n\t * Set the creation date.\r\n\t *\r\n\t * @param \\DateTime $createdAt\r\n\t *\/\r\n\tpublic function setCreatedAt( $createdAt ) {\r\n\t\t$this-&gt;createdAt = $createdAt;\r\n\t}\r\n\r\n\r\n\t\/**\r\n\t * Returns the update date.\r\n\t *\r\n\t * @return \\DateTime\r\n\t *\/\r\n\tpublic function getUpdatedAt() {\r\n\t\treturn $this-&gt;updatedAt;\r\n\t}\r\n\r\n\r\n\t\/**\r\n\t * Set the update date.\r\n\t *\r\n\t * @param \\DateTime $updatedAt\r\n\t *\/\r\n\tpublic function setUpdatedAt( $updatedAt ) {\r\n\t\t$this-&gt;updatedAt = $updatedAt;\r\n\t}\r\n\r\n\r\n\t\/**\r\n\t * Returns the delete date.\r\n\t *\r\n\t * @return \\DateTime|null\r\n\t *\/\r\n\tpublic function getDeletedAt() {\r\n\t\treturn $this-&gt;deletedAt;\r\n\t}\r\n\r\n\r\n\t\/**\r\n\t * Set the delete date.\r\n\t *\r\n\t * @param \\DateTime|null $deletedAt\r\n\t *\/\r\n\tpublic function setDeletedAt( $deletedAt ) {\r\n\t\t$this-&gt;deletedAt = $deletedAt;\r\n\t}\r\n\r\n}\r\n<\/pre>\n<p>&nbsp;<\/p>\n<h4>Auto-Fill implementieren<\/h4>\n<p>Wir haben nun eine Basisklasse, welche vier Attribute besitzt, mit jeweils den zugeh\u00f6rigen Getter- und Setter-Methoden. Im n\u00e4chsten Schritt sorgen wir daf\u00fcr, dass im Konstruktor ein Daten-Array \u00fcbergeben werden kann, anhand dessen dann automatisch die entsprechenden Setter-Methoden ermittelt und ausgef\u00fchrt werden.<\/p>\n<p>Dazu schreiben wir uns zwei Helper-Klassen: eine f\u00fcr die String-Operationen bzgl. der Konvertierung von und zu Camel-Case, sowie eine f\u00fcr das Type-Casting:<\/p>\n<pre class=\"lang:php decode:true \">class StringHelper {\r\n\r\n\t\/**\r\n\t * Translates a camel case string into a string with\r\n\t * underscores (e.g. firstName -&gt; first_name)\r\n\t *\r\n\t * @param string $str String in camel case format\r\n\t *\r\n\t * @return string $str Translated into underscore format\r\n\t *\/\r\n\tpublic static function fromCamelCase( $str ) {\r\n\t\t$str[0] = strtolower( $str[0] );\r\n\t\t$func   = create_function( '$c', 'return \"_\" . strtolower($c[1]);' );\r\n\r\n\t\treturn preg_replace_callback( '\/([A-Z])\/', $func, $str );\r\n\t}\r\n\r\n\t\/**\r\n\t * Translates a string with underscores\r\n\t * into camel case (e.g. first_name -&gt; firstName)\r\n\t *\r\n\t * @param string $str String in underscore format\r\n\t * @param bool $capitalise_first_char If true, capitalise the first char in $str\r\n\t *\r\n\t * @return string $str translated into camel caps\r\n\t *\/\r\n\tpublic static function toCamelCase( $str, $capitalise_first_char = false ) {\r\n\t\tif ( $capitalise_first_char ) {\r\n\t\t\t$str[0] = strtoupper( $str[0] );\r\n\t\t}\r\n\t\t$func = create_function( '$c', 'return strtoupper($c[1]);' );\r\n\r\n\t\treturn preg_replace_callback( '\/_([a-z])\/', $func, $str );\r\n\t}\r\n\r\n}<\/pre>\n<pre class=\"lang:php decode:true \">class IoHelper {\r\n\r\n\t\/**\r\n\t * Helper function for casting variables into desired types.\r\n\t *\r\n\t * @param $value\r\n\t * @param string $type\r\n\t *\r\n\t * @return float|int|bool|string\r\n\t *\/\r\n\tpublic static function castValue( $value, $type = 'string' ) {\r\n\t\t$type = strtolower($type);\r\n\r\n\t\tswitch ( $type ) {\r\n\t\t\tcase 'string':\r\n\t\t\t\t$value = (string) $value;\r\n\t\t\t\tbreak;\r\n\t\t\tcase 'int':\r\n\t\t\tcase 'integer':\r\n\t\t\t\t$value = (int) $value;\r\n\t\t\t\tbreak;\r\n\t\t\tcase 'double':\r\n\t\t\t\t$value = (double) $value;\r\n\t\t\t\tbreak;\r\n\t\t\tcase 'float':\r\n\t\t\t\t$value = (float) $value;\r\n\t\t\t\tbreak;\r\n\t\t\tcase 'bool':\r\n\t\t\tcase 'boolean':\r\n\t\t\t\t$value = (bool) $value;\r\n\t\t\t\tbreak;\r\n\t\t\tdefault:\r\n\t\t\t\t$value = $value;\r\n\t\t}\r\n\r\n\t\treturn $value;\r\n\t}\r\n\r\n}<\/pre>\n<p>Widmen wir uns nun wieder unserer Basisklasse. Dazu erweitern wir den Konstruktor wie folgt:<\/p>\n<pre class=\"lang:php decode:true\">\/**\r\n * BaseElement constructor.\r\n *\r\n * @param array $data\r\n *\/\r\npublic function __construct( $data = null ) {\r\n\t\/\/ check if fill data was provided\r\n\tif ( $data ) {\r\n\r\n\t\t\/\/ cast object into array if it's an object\r\n\t\tif ( is_object( $data ) ) {\r\n\t\t\t$data = (array) $data;\r\n\t\t}\r\n\r\n\t\t\/\/ iterate over data array\r\n\t\tforeach ( $data as $key =&gt; $value ) {\r\n\r\n\t\t\t\/\/ build up name for equivalent setter-function\r\n\t\t\t$setterFunction = 'set' . StringHelper::toCamelCase( $key, true );\r\n\r\n\t\t\t\/\/ check if desired setter-functions exists and if so, execute it\r\n\t\t\tif ( method_exists( $this, $setterFunction ) ) {\r\n\r\n\t\t\t\t\/\/ get reflection method\r\n\t\t\t\t$reflectionMethod = new \\ReflectionMethod( $this, $setterFunction );\r\n\r\n\t\t\t\t\/\/ get parameters for reflection method\r\n\t\t\t\t$reflectionParameters = $reflectionMethod-&gt;getParameters();\r\n\r\n\t\t\t\t\/\/ check if desired parameter at position 0 exists\r\n\t\t\t\tif ( isset( $reflectionParameters[0] ) ) {\r\n\r\n\t\t\t\t\t\/\/ detect desired data type by reading doc-comments\r\n\t\t\t\t\t$type        = strtolower( (string) $reflectionParameters[0]-&gt;getType() );\r\n\t\t\t\t\t$castedValue = IoHelper::castValue( $value, $type );\r\n\r\n\t\t\t\t\t\/\/ call setter-function with casted parameter\r\n\t\t\t\t\t$this-&gt;$setterFunction( $castedValue );\r\n\t\t\t\t}\r\n\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n}<\/pre>\n<p>Was hier passiert, ist relativ leicht zu erkl\u00e4ren: es wird gepr\u00fcft, ob ein Daten-Array \u00fcbergeben wurde. Im positiven Fall wird dann gepr\u00fcft, ob es sich m\u00f6glicherweise um ein Objekt handelt (wie es beispielsweise beim Eloquent-ORM von Laravel der Fall w\u00e4re). Wenn ja, wird dieses zu einem Array konvertiert. Im n\u00e4chsten Schritt wird dann \u00fcber jedes einzelne \u00fcbergebene Attribut mittels einer <strong>foreach<\/strong>-Schleife im Key-Value-Stil iteriert. Da jeweils der Index bekannt ist, und dieser das betreffende Attribut repr\u00e4sentieren soll, gehen wir davon aus, dass die zugeh\u00f6rige Setter-Funktion im Stil \u201e<strong>set&lt;AttributName&gt;<\/strong>\u201c benannt ist.<\/p>\n<p>Sollte eine solche Setter-Methode in der aktuellen Objekt-Instanz tats\u00e4chlich existieren, wir diese mit der <strong>ReflectionMethod()<\/strong> instanziiert. Theoretisch w\u00e4re es nun bereits m\u00f6glich, diese direkt zu verwenden. Allerdings m\u00fcssen wir vorher noch den \u00fcbergebenen Wert entsprechend casten, damit dieser jeweils mit dem erwarteten Datentyp der jeweiligen Setter-Methode \u00fcbereinstimmt.<\/p>\n<p>Dazu wird als n\u00e4chstes mit Hilfe von <strong>getParameters()<\/strong> ausgelesen, welche Parameter an die betreffende Setter-Methode \u00fcbergeben werden k\u00f6nnen. Wir gehen hier davon aus, dass jede dieser Methoden jeweils genau einen Parameter, also den zu setzenden Wert, \u00fcbernehmen kann. Ist dieser Parameter definiert, wird nun mittels <strong>getType()<\/strong> der erwartete Datentyp retourniert, welcher \u00fcber den PHP-Annotations definiert wurden. Diesen ermittelten Ziel-Datentyp \u00fcbergeben wir nun unserer Helper-Klasse und erhalten die entsprechend gecastete Variable zur\u00fcck.<\/p>\n<p>Im letzten Schritt rufen wir nun die Setter-Methode mittels regul\u00e4rem Zugriff darauf auf und \u00fcbergeben dieser als Parameter den zuvor gecasteten Wert. Damit h\u00e4tten wir im Grunde unsere Kernfunktionalit\u00e4t betreffend dem Auto-Fill implementiert.<\/p>\n<h4>Daten ausgeben<\/h4>\n<p>Um innerhalb unserer Basisklasse nun unter Nutzung der jeweiligen Getter-Methode zuzugreifen, schreiben wir uns eine kleine private Hilfs-Methode:<\/p>\n<pre class=\"lang:php decode:true \">\/**\r\n * Tries to find an adequate Getter-function for the specified attribute key, and returns its value.\r\n *\r\n * @param $key\r\n *\r\n * @return null\r\n *\/\r\nprivate function getElementByKey( $key ) {\r\n\t\/\/ build up name for equivalent setter-function\r\n\t$getterFunction = 'get' . StringHelper::toCamelCase( $key, true );\r\n\r\n\t\/\/ check if desired setter-functions exists and if so, execute it\r\n\tif ( method_exists( $this, $getterFunction ) ) {\r\n\r\n\t\t\/\/ get reflection method\r\n\t\t$reflectionMethod     = new \\ReflectionMethod( $this, $getterFunction );\r\n\t\t$reflectionMethodName = $reflectionMethod-&gt;getName();\r\n\r\n\t\treturn $this-&gt;$reflectionMethodName();\r\n\t}\r\n\r\n\treturn null;\r\n}<\/pre>\n<p>Dieser k\u00f6nnen wir als Parameter den Namen des gew\u00fcnschten Attributs \u00fcbergeben. \u00c4hnlich wie bereits beim Setzen der Werte zuvor, ermitteln wir den Namen der zugeh\u00f6rigen Getter-Methode unter der Annahme, dass dieser in Form von \u201e<strong>get&lt;AttributName&gt;<\/strong>\u201c definiert wurde.<\/p>\n<p>Existiert die Methode im aktuellen Objekt-Kontext, wird diese mittels der <strong>ReflectionMethod()<\/strong> ausgef\u00fchrt, und als Resultat der Wert des gew\u00fcnschten Attributs retourniert.<\/p>\n<p>Damit wir f\u00fcr die Ausgabe aller Attribute auch dynamisch erm\u00f6glichen k\u00f6nnen, m\u00fcssen wir wissen, welche Attribute generell in der aufgerufenen Klasse, sowie in der Basisklasse (als Elternklasse) vorhanden sind. Dazu definieren wir eine Methode namens <strong>getDocument()<\/strong>, welches genau dies macht:<\/p>\n<pre class=\"lang:php decode:true \">\/**\r\n * Build up the public exposed data array.\r\n *\r\n * @param bool $buildRelations\r\n *\r\n * @return array\r\n *\/\r\npublic function getDocument() {\r\n\t$reflectionClass = new \\ReflectionClass( $this );\r\n\t$properties      = [];\r\n\r\n\t\/\/ determine properties of parent class from current context\r\n\t$this-&gt;buildPropertyArray( $reflectionClass-&gt;getParentClass()-&gt;getProperties(), $properties );\r\n\r\n\t\/\/ determine properties of current class\r\n\t$this-&gt;buildPropertyArray( $reflectionClass-&gt;getProperties(), $properties );\r\n\r\n\t\/\/ build up data array\r\n\t$data = [];\r\n\tforeach ( $properties as $property ) {\r\n\t\tif ( ! in_array( StringHelper::fromCamelCase( $property ), $this-&gt;_hidden ) ) {\r\n\t\t\t$data[ $property ] = $this-&gt;getElementByKey( $property );\r\n\t\t}\r\n\t}\r\n\r\n\treturn $data;\r\n}\r\n\r\n\r\n\r\n\/**\r\n * Internal helper function for extracting valid (public) attributes from ReflectionProperty() array.\r\n *\r\n * @param $reflectionProperties\r\n * @param $data\r\n *\/\r\nprivate function buildPropertyArray( $reflectionProperties, &amp;$data ) {\r\n\tforeach ( $reflectionProperties as $property ) {\r\n\t\t$propertyName = $property-&gt;getName();\r\n\r\n\t\tif ( substr( $propertyName, 0, 1 ) == '_' ) {\r\n\t\t\tcontinue;\r\n\t\t}\r\n\r\n\t\t$data[] = $property-&gt;getName();\r\n\t}\r\n}<\/pre>\n<p>Die Hilfs-Methode <strong>buildPropertyArray()<\/strong> dient in erster Linie dazu, doppelten Code zu vermeiden, und gleichzeitig auch dazu, Attribute, welche mit einem Unterstich (\u201e_\u201c) beginnen, zu ignorieren. Attribute, welche mit einem Unterstrich beginnen, werden in diesem Fall nicht nach au\u00dfen mittels <strong>getDocument()<\/strong> exportiert, bleiben also geheim.<\/p>\n<h4>Eigene Entit\u00e4ten ableiten<\/h4>\n<p>Um nun mit Hilfe der Basisklasse eigene Entit\u00e4ts-Klassen abzuleiten, ist es ausreichend, eine neue Klasse mit dem gew\u00fcnschten Namen zu erstellen, welche von unserer zuvor erstellten Basisklasse erbt.<\/p>\n<p>In dieser k\u00f6nnen nun nach dem gleichen Prinzip individuelle Attribute definiert werden \u2013 mehr ist hier nicht notwendig.<\/p>\n<pre class=\"lang:php decode:true \">\/**\r\n * Class Dummy\r\n *\/\r\nclass Dummy extends BaseElement {\r\n\r\n\r\n\t\/**\r\n\t * @var string $title\r\n\t *\/\r\n\tprivate $title;\r\n\r\n\r\n\r\n\t\/**\r\n\t * Returns the title.\r\n\t *\r\n\t * @return string\r\n\t *\/\r\n\tpublic function getTitle() {\r\n\t\treturn $this-&gt;title;\r\n\t}\r\n\r\n\r\n\t\/**\r\n\t * Set the title.\r\n\t *\r\n\t * @param string $title\r\n\t *\/\r\n\tpublic function setTitle( string $title ) {\r\n\t\t$this-&gt;title = $title;\r\n\t}\r\n\r\n\t\r\n}<\/pre>\n<p>Um unsere Entit\u00e4ts-Klasse zu testen, definieren wir ein kleines Daten-Array, mit ein paar Test-Werten. Dieses soll in unserem Fall eine externe Datenquelle simulieren.<\/p>\n<pre class=\"lang:php decode:true \">$data = [\r\n\t\"id\"\t=&gt; 14,\r\n\t\"title\"\t=&gt; \"Lorem ipsum\",\r\n];<\/pre>\n<p>Im n\u00e4chsten Schritt initialisieren wir unsere neu erstellte Entit\u00e4ts-Klasse mit diesem Daten-Array und k\u00f6nnen im Anschluss daran das vollst\u00e4ndige Ergebnis mittels <strong>getDocument()<\/strong> exportieren:<\/p>\n<pre class=\"lang:php decode:true\">$entity = new Dummy( $data );\r\n\r\nprint_r( $entity-&gt;getDocument() );<\/pre>\n<p>&nbsp;<\/p>\n<h2>Fazit<\/h2>\n<p>Mittels Reflection kann in der modernen Softwareentwicklung das Programm \u201eintelligent\u201c agieren, da dieses w\u00e4hrend der Laufzeit in der Lage ist, die Eigenschaften eigener Funktionen zu analysieren und sogar zu modifizieren.<\/p>\n<p>Die Implementierung von Reflection in PHP ist jedoch so umfangreich, dass wir hier nur einen kleinen Teil der M\u00f6glichkeiten demonstriert haben. Es gibt noch unz\u00e4hlige weitere Funktionen, um den Programmcode noch weiter zu zerlegen.<\/p>\n<p>Zusammengefasst k\u00f6nnen wir feststellen, dass man mittels Reflection eine sehr m\u00e4chtige Technologie in die H\u00e4nde gelegt bekommt, mit der man wirklich enorm viel bewerkstelligen kann.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Unter Reflection versteht man, dass ein Programm seine Struktur kennt und diese auch modifizieren kann. Wie das funktioniert, wird hier erkl\u00e4rt.<\/p>\n","protected":false},"author":15,"featured_media":2961,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4],"tags":[14,5,447,362,448],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v22.2 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Reflection in PHP Projekten - ANEXIA Blog<\/title>\n<meta name=\"description\" content=\"Unter Reflection versteht man, dass ein Programm seine Struktur kennt und diese auch modifizieren kann. Wie das funktioniert, wird hier erkl\u00e4rt.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/anexia.com\/blog\/de\/reflection-in-php-projekten\/\" \/>\n<meta property=\"og:locale\" content=\"de_DE\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Reflection in PHP Projekten - ANEXIA Blog\" \/>\n<meta property=\"og:description\" content=\"Unter Reflection versteht man, dass ein Programm seine Struktur kennt und diese auch modifizieren kann. Wie das funktioniert, wird hier erkl\u00e4rt.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/anexia.com\/blog\/de\/reflection-in-php-projekten\/\" \/>\n<meta property=\"og:site_name\" content=\"ANEXIA Blog\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/anexiagmbh\/\" \/>\n<meta property=\"article:published_time\" content=\"2018-04-11T09:10:24+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2022-04-19T05:28:11+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/anexia.com\/blog\/wp-content\/uploads\/2017\/09\/ManuelWutte-Blog-Teaser.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"672\" \/>\n\t<meta property=\"og:image:height\" content=\"372\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Manuel Wutte\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@_ANEXIA\" \/>\n<meta name=\"twitter:site\" content=\"@_ANEXIA\" \/>\n<meta name=\"twitter:label1\" content=\"Verfasst von\" \/>\n\t<meta name=\"twitter:data1\" content=\"Manuel Wutte\" \/>\n\t<meta name=\"twitter:label2\" content=\"Gesch\u00e4tzte Lesezeit\" \/>\n\t<meta name=\"twitter:data2\" content=\"13\u00a0Minuten\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/anexia.com\/blog\/de\/reflection-in-php-projekten\/\",\"url\":\"https:\/\/anexia.com\/blog\/de\/reflection-in-php-projekten\/\",\"name\":\"Reflection in PHP Projekten - ANEXIA Blog\",\"isPartOf\":{\"@id\":\"https:\/\/anexia.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/anexia.com\/blog\/de\/reflection-in-php-projekten\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/anexia.com\/blog\/de\/reflection-in-php-projekten\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/anexia.com\/blog\/wp-content\/uploads\/2017\/09\/ManuelWutte-Blog-Teaser.jpg\",\"datePublished\":\"2018-04-11T09:10:24+00:00\",\"dateModified\":\"2022-04-19T05:28:11+00:00\",\"author\":{\"@id\":\"https:\/\/anexia.com\/blog\/#\/schema\/person\/926f6b9e5aeed88b145cf86d87fd09de\"},\"description\":\"Unter Reflection versteht man, dass ein Programm seine Struktur kennt und diese auch modifizieren kann. Wie das funktioniert, wird hier erkl\u00e4rt.\",\"breadcrumb\":{\"@id\":\"https:\/\/anexia.com\/blog\/de\/reflection-in-php-projekten\/#breadcrumb\"},\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/anexia.com\/blog\/de\/reflection-in-php-projekten\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\/\/anexia.com\/blog\/de\/reflection-in-php-projekten\/#primaryimage\",\"url\":\"https:\/\/anexia.com\/blog\/wp-content\/uploads\/2017\/09\/ManuelWutte-Blog-Teaser.jpg\",\"contentUrl\":\"https:\/\/anexia.com\/blog\/wp-content\/uploads\/2017\/09\/ManuelWutte-Blog-Teaser.jpg\",\"width\":672,\"height\":372},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/anexia.com\/blog\/de\/reflection-in-php-projekten\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/anexia.com\/blog\/de\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Reflection in PHP Projekten\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/anexia.com\/blog\/#website\",\"url\":\"https:\/\/anexia.com\/blog\/\",\"name\":\"ANEXIA Blog\",\"description\":\"[:de] ANEXIA Blog - Technischen Themen, Anexia News und Insights [:]\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/anexia.com\/blog\/?s={search_term_string}\"},\"query-input\":\"required name=search_term_string\"}],\"inLanguage\":\"de\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/anexia.com\/blog\/#\/schema\/person\/926f6b9e5aeed88b145cf86d87fd09de\",\"name\":\"Manuel Wutte\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\/\/anexia.com\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/9528a61f48f4294cd5f7cd8a4141bd55?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/9528a61f48f4294cd5f7cd8a4141bd55?s=96&d=mm&r=g\",\"caption\":\"Manuel Wutte\"},\"url\":\"https:\/\/anexia.com\/blog\/author\/mwu\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Reflection in PHP Projekten - ANEXIA Blog","description":"Unter Reflection versteht man, dass ein Programm seine Struktur kennt und diese auch modifizieren kann. Wie das funktioniert, wird hier erkl\u00e4rt.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/anexia.com\/blog\/de\/reflection-in-php-projekten\/","og_locale":"de_DE","og_type":"article","og_title":"Reflection in PHP Projekten - ANEXIA Blog","og_description":"Unter Reflection versteht man, dass ein Programm seine Struktur kennt und diese auch modifizieren kann. Wie das funktioniert, wird hier erkl\u00e4rt.","og_url":"https:\/\/anexia.com\/blog\/de\/reflection-in-php-projekten\/","og_site_name":"ANEXIA Blog","article_publisher":"https:\/\/www.facebook.com\/anexiagmbh\/","article_published_time":"2018-04-11T09:10:24+00:00","article_modified_time":"2022-04-19T05:28:11+00:00","og_image":[{"width":672,"height":372,"url":"https:\/\/anexia.com\/blog\/wp-content\/uploads\/2017\/09\/ManuelWutte-Blog-Teaser.jpg","type":"image\/jpeg"}],"author":"Manuel Wutte","twitter_card":"summary_large_image","twitter_creator":"@_ANEXIA","twitter_site":"@_ANEXIA","twitter_misc":{"Verfasst von":"Manuel Wutte","Gesch\u00e4tzte Lesezeit":"13\u00a0Minuten"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/anexia.com\/blog\/de\/reflection-in-php-projekten\/","url":"https:\/\/anexia.com\/blog\/de\/reflection-in-php-projekten\/","name":"Reflection in PHP Projekten - ANEXIA Blog","isPartOf":{"@id":"https:\/\/anexia.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/anexia.com\/blog\/de\/reflection-in-php-projekten\/#primaryimage"},"image":{"@id":"https:\/\/anexia.com\/blog\/de\/reflection-in-php-projekten\/#primaryimage"},"thumbnailUrl":"https:\/\/anexia.com\/blog\/wp-content\/uploads\/2017\/09\/ManuelWutte-Blog-Teaser.jpg","datePublished":"2018-04-11T09:10:24+00:00","dateModified":"2022-04-19T05:28:11+00:00","author":{"@id":"https:\/\/anexia.com\/blog\/#\/schema\/person\/926f6b9e5aeed88b145cf86d87fd09de"},"description":"Unter Reflection versteht man, dass ein Programm seine Struktur kennt und diese auch modifizieren kann. Wie das funktioniert, wird hier erkl\u00e4rt.","breadcrumb":{"@id":"https:\/\/anexia.com\/blog\/de\/reflection-in-php-projekten\/#breadcrumb"},"inLanguage":"de","potentialAction":[{"@type":"ReadAction","target":["https:\/\/anexia.com\/blog\/de\/reflection-in-php-projekten\/"]}]},{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/anexia.com\/blog\/de\/reflection-in-php-projekten\/#primaryimage","url":"https:\/\/anexia.com\/blog\/wp-content\/uploads\/2017\/09\/ManuelWutte-Blog-Teaser.jpg","contentUrl":"https:\/\/anexia.com\/blog\/wp-content\/uploads\/2017\/09\/ManuelWutte-Blog-Teaser.jpg","width":672,"height":372},{"@type":"BreadcrumbList","@id":"https:\/\/anexia.com\/blog\/de\/reflection-in-php-projekten\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/anexia.com\/blog\/de\/"},{"@type":"ListItem","position":2,"name":"Reflection in PHP Projekten"}]},{"@type":"WebSite","@id":"https:\/\/anexia.com\/blog\/#website","url":"https:\/\/anexia.com\/blog\/","name":"ANEXIA Blog","description":"[:de] ANEXIA Blog - Technischen Themen, Anexia News und Insights [:]","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/anexia.com\/blog\/?s={search_term_string}"},"query-input":"required name=search_term_string"}],"inLanguage":"de"},{"@type":"Person","@id":"https:\/\/anexia.com\/blog\/#\/schema\/person\/926f6b9e5aeed88b145cf86d87fd09de","name":"Manuel Wutte","image":{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/anexia.com\/blog\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/9528a61f48f4294cd5f7cd8a4141bd55?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/9528a61f48f4294cd5f7cd8a4141bd55?s=96&d=mm&r=g","caption":"Manuel Wutte"},"url":"https:\/\/anexia.com\/blog\/author\/mwu\/"}]}},"lang":"de","translations":{"de":3430,"en":6693},"amp_enabled":true,"pll_sync_post":[],"_links":{"self":[{"href":"https:\/\/anexia.com\/blog\/wp-json\/wp\/v2\/posts\/3430"}],"collection":[{"href":"https:\/\/anexia.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/anexia.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/anexia.com\/blog\/wp-json\/wp\/v2\/users\/15"}],"replies":[{"embeddable":true,"href":"https:\/\/anexia.com\/blog\/wp-json\/wp\/v2\/comments?post=3430"}],"version-history":[{"count":15,"href":"https:\/\/anexia.com\/blog\/wp-json\/wp\/v2\/posts\/3430\/revisions"}],"predecessor-version":[{"id":6695,"href":"https:\/\/anexia.com\/blog\/wp-json\/wp\/v2\/posts\/3430\/revisions\/6695"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/anexia.com\/blog\/wp-json\/wp\/v2\/media\/2961"}],"wp:attachment":[{"href":"https:\/\/anexia.com\/blog\/wp-json\/wp\/v2\/media?parent=3430"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/anexia.com\/blog\/wp-json\/wp\/v2\/categories?post=3430"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/anexia.com\/blog\/wp-json\/wp\/v2\/tags?post=3430"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}