Wer nutzt die PHP-Reflection API?Und für was?
Fragen können jederzeit gestellt werden
Metaprogrammierung und Reflection
Tool-Entwicklung für und mit PHP
Stefan Marr PHP Unconference
26./27. April 2008
1. Begriffsklärung
2. Open Implementations– Metaprogrammierung und Frameworkdesign
3. Relevante Sprachfeatures von PHP
4. Reflection und Runkit für die Tool-Entwicklung
5. Zusammenfassung
Agenda
METAPROGRAMMIERUNG UND REFLECTION
Kurze Begriffsklärung zur Einleitung
Verarbeitendes SystemComputational
System
Interpreter
State(Data)
Behavior(Program )
Domain(some part of the world)
reasons aboutand acts upon
representation of/returning information about
• Daten repräsentieren Domäne• Programm beschreibt
Veränderungen• Interpreter führt Programm aus
• Domäne• Buchhaltung, Kundendaten,
Logistik, Webinhalte, …
nach [1]
Kausal verbundenes System
• Ursache-Wirkungsbeziehung zwischen System und Domäne
• Direkte wechselseitige Auswirkungen
• Regellungstechnik• Klimaanlagen• Autopiloten• …
ComputationalSystem
Interpreter
State(Data)
Behavior(Program )
Domain(some part of the world)
reasons aboutand acts upon
causally connectedrepresentation
nach [1]
Meta-System
• Spezielle Domäne: Programme• Meta-Programme und -system
arbeiten auf Basis-Programmen und -systemen
• Compiler• Code-Transformation,
Generierung• …
Base System
Interpreter
State(Data)
Behavior(Program )
Domain(some part of the world )
reasons aboutand acts upon
representation of/returning information about
Meta System
Interpreter
State(Data)
Behavior(Program )
reasons aboutand acts upon
representation of/returning information about
nach [1]
Reflektives System
• Spezielle Meta-Systeme• Selbstrepräsentation bestimmter
Strukturen• Kausal verbunden
• Reflektives Verhalten• Introspection (beobachten)• Intercession
(Selbstveränderung)
ComputationalSystem
Interpreter
State(Data)
Behavior(Program )
Domain(some part of the world )
reasons aboutand acts upon
causally connectedrepresentation
reasons aboutand acts upon
representation of/returning information about
nach [1]
Meta- und Reflektive SystemeComputational
System
Interpreter
State(Data)
Behavior(Program )
Domain(some part of the world)
reasons aboutand acts upon
representation of/returning information about
ComputationalSystem
Interpreter
State(Data)
Behavior(Program )
Domain(some part of the world)
reasons aboutand acts upon
causally connectedrepresentation
Base System
Interpreter
State(Data)
Behavior(Program )
Domain(some part of the world )
reasons aboutand acts upon
representation of/returning information about
Meta System
Interpreter
State(Data)
Behavior(Program )
reasons aboutand acts upon
representation of/returning information about
ComputationalSystem
Interpreter
State(Data)
Behavior(Program )
Domain(some part of the world )
reasons aboutand acts upon
causally connectedrepresentation
reasons aboutand acts upon
representation of/returning information about
Kausal verbunden
Meta-System
Reflektiv
nach [1]
OPEN IMPLEMENTATIONSMetaprogrammierung und Moduldesign
• Verstecken von Implementierungsdetails– Vereinfachung des Client-
Codes• Verringerung von
Abhängigkeiten zwischen Modulen und Clients– Erhöhung der
Wiederverwendbarkeit– Client-Code lesbarer und
verständlich ohne Wissen über unterliegende Implementierung
Abstraktion in einer Black Box
• Geeignete Implementierungsstrategie abhängig von der Art der Nutzung des Moduls
• Implementierungs-entscheidungen – selten dokumentiert– für bestimmte Fälle
ungeeignet– nicht anpassbar– zu starke Abstraktion
• Client weiß meist am besten welche Strategie geeignet wäre
Probleme der Black Box
• Implementierungs-entscheidungen nicht im Interface sichtbar, aber:– Performanceauswirkungen
• Abbildung von High-Level auf Low-Level Funktionen vielfältig möglich– Beeinflussen Performance– Entscheidung daher
feststehend, aber nicht verborgen
Keine 100%ige Black Box möglich
• Neuimplementierung von Interfacefunktionen– Um gewünschte Eigenschaften zu
erreichen– Code wird größer und komplexer
• „Code zwischen den Zeilen“– Behandelt Probleme die eigentlich
vom Interface verborgen werden sollten
– Basiert auf undokumentierten Interna, verborgen vom Interface, aber dem Programmierer bekannt
Probleme und Workarounds
Lösung: Reflektive Module
Basis Interface
• Bietet Funktionalität an
• Das WASMeta Interface• Ermöglicht Steuerung
des Basis Interfaces• Das WIE
Reflektive Module/Programme
• Vorteile– Schnittstellennutzung
jetzt wieder als Black Box– Konfiguration bzw.
Implementierungsstrategie der Black Box vom Client wählbar
• Programmierer können sich auf Modulnutzung konzentrieren
• Optimierung/Konfiguration separat möglich
• Frameworks– Aufteilung in Client API und Framework API
– Konfigurationsdateien, Konstanten, Parameter, Flags, …
• Dependency Injection als Pattern verbreitet
• Funktionen mit Callbacks altbekannt
Umsetzungsmöglichkeiten
• Frameworks sollten Erweiterungs- bzw. Spezialisierungspunkte anbieten– Problematische Teile vom Client anpassbar
• Anpassung, Konfiguration über Meta-APIs
• Design trotzdem nicht mit den zu kapselnden Details überfrachten– Auf geeignete Trennung zwischen WAS und WIE
achten
ZusammenfassungOpen Implementations
RELEVANTE SPRACHFEATURES VON PHP
Magic Methods und die Reflection API
• Dynamische Object-Properties– __g e t ( ) , __s e t ( )
• Werden verwendet wenn die angefragten Properties nicht vorhanden sind
• z. B. für Validierung und Vermeidung von unspezifizierten Properties
• __i s s e t ( ) , __u n s e t ( )
• __c l o n e ( ) : ändert Semantik von c l o n e• __t o S t r i n g ( ) , __s l e e p ( ) ,
__wa k e u p ( ) , __s e t _s t a t e ( )
Magic Methods
• __a u t o l o a d ( $ c l a s s Na me )– Ermöglicht echtes Lazy-loading von Klassen
– Implementiert eigene Laderoutine
• __c a l l ( $ me t h o d Na me , $ a r g s )– Ermöglicht echtes Late-Binding
– Instanzverhalten zur Laufzeit entscheidbar
• __c a l l S t a t i c ( $ me t h o d Na me , $ a r g s )– La t e - Bi n d i n g f ü r Kl a s s e n me t h o d e n
Magic Methods
Reflection APIKlassendiagramm der Reflection API von PHP 5.2
+getName() : string+isPublic () : bool+isPrivate() : bool+isProtected() : bool+isStatic () : bool+isDefault() : bool+getModifiers() : int+getValue(in object : object) : mixed+setValue(in object : object, in value : mixed)+getDeclaringClass() : ReflectionClass+getDocComment() : string
ReflectionProperty
+getName() : string+isInternal() : bool+isUserDefined() : bool+isInstantiable () : bool+hasConstant(in name : string) : bool+hasMethod(in name : string) : bool+hasProperty(in name : string) : bool+getFileName() : string+getStartLine() : int+getEndLine() : int+getDocComment() : string+getConstructor() : ReflectionMethod+getMethod(in name : string) : ReflectionMethod+getMethods() : ReflectionMethod[]+getProperty(in name : string) : ReflectionProperty+getProperties() : ReflectionProperty[]+getConstants() : array+getConstant(in name : string) : mixed+getInterfaces() : ReflectionClass[]+isInterface() : bool+isAbstract() : bool+isFinal() : bool+getModifiers() : int+isInstance(in object : object) : bool+newInstance(in args : mixed) : object+newInstanceArgs(in args : array) : object+getParentClass() : ReflectionClass+isSubclassOf (in class : ReflectionClass) : bool+getStaticProperties() : array+getStaticPropertyValue(in name : string, in default : mixed = null) : mixed+setStaticPropertyValue(in name : string, in value : mixed)+getDefaultProperties() : array+isIterateable() : bool+implementsInterfaces(in name : string) : bool+getExtension() : ReflectionExtension+getExtensionName() : string
ReflectionClass
ReflectionObject
+invoke(in object : object, in ... : mixed) : mixed+invokeArgs(in object : object, in args : mixed) : mixed+isFinal() : bool+isAbstract() : bool+isPublic () : bool+isPrivate() : bool+isProtected() : bool+isStatic () : bool+isConstructor() : bool+isDestructor() : bool+getModifiers() : int+getDeclaringClass() : ReflectionClass
ReflectionMethod
+getName() : string+isPassedByReference() : bool+getDeclaringClass() : ReflectionClass+getClass() : ReflectionClass+isArray() : bool+allowsNull() : bool+isPassedByReference() : bool+isOptional () : bool+isDefaultValueAvailable () : bool+getDefaultValue() : mixed
ReflectionParameter
+getName() : string+getVersion() : string+getFunctions() : ReflectionFunction[]+getConstants() : array+getINIEntries() : array+getClasses() : ReflectionClass[]+getClassNames() : array+info() : string
ReflectionExtension
+getName() : string+isInternal() : bool+isDisabled() : bool+isUserDefined() : bool+getFileName() : string+getStartLine() : int+getEndLine() : int+getDocComment() : string+getStaticVariables() : array+invoke(in args : mixed, in ... : mixed) : mixed+invokeArgs() : mixed+returnsReference() : bool+getParameters() : ReflectionParameter[]+getNumberOfParameters() : int+getNumberOfRequiredParameters() : int
ReflectionFunction
func_get_arg()func_get_args()func_num_args()is_subclass_of()get_parent_class()get_class()get_class_methods()get_object_vars()get_class_vars()class_exists()interface_exists()function_exists()method_exists()property_exists ()get_declared_classes()get_declared_interfaces()get_included_files()get_required_files()ini_set()ini_get()get_defined_constants()get_defined_functions()get_defined_vars()call_user_func()call_user_func_array()create_function()eval()
<<functions>>
ReflectionPropertyReflectionClass
ReflectionObject
ReflectionMethod
ReflectionParameter
ReflectionExtension
func_get_arg()func_get_args()func_num_args()is_subclass_of()get_parent_class ()get_class()get_class_methods()get_object_vars()get_class_vars()class_exists()interface_exists()function_exists()method_exists()property_exists ()get_declared_classes()get_declared_interfaces()get_included_files()get_required_files()ini_set()ini_get()get_defined_constants()get_defined_functions()get_defined_vars()call_user_func()call_user_func_array()create_function()eval()
<<functions>>
ReflectionFunction
ReflectionPropertyReflectionClass
ReflectionObject
ReflectionMethod
ReflectionParameter
ReflectionExtension
func_get_arg()func_get_args()func_num_args()is_subclass_of()get_parent_class()get_class()get_class_methods()get_object_vars()get_class_vars()class_exists()interface_exists()method_exists ()property_exists()get_declared_classes ()get_declared_interfaces()get_included_files()get_required_files()ini_set()ini_get()get_defined_constants()get_defined_functions()get_defined_vars()
<<functions>>
ReflectionFunction
PHP System Runtime Data
ReflectionPHP Core
Classes Objects ...
PH
P V
irtu
al M
achi
ne
with
Sys
tem
Lev
el E
xte
nsio
ns
REFLECTION UND RUNKIT FÜR DIE TOOL-ENTWICKLUNG MIT PHP
phpCrow, ezcReflection und isvcRunkit
splash
• Generierten Code ausführen: e v a l ( )
• Funktionen erstellen: c r e a t e _f u n c t i o n ( )
• Dynamische Aufrufe:– c a l l _u s e r _f u n c t i o n ( )– c a l l _u s e r _f u n c t i o n _a r r a y ( )– c a l l _u s e r _me t h o d ( )– c a l l _u s e r _me t h o d _a r r a y ( )– $ f u n c t i o n Na me ( $ a , $ b ) ;
Intercession mit Standard-PHP
$ f u n c = c r e a t e _f u n c t i o n ( ' $ f o o , $ b a r ' ,' e c h o " Cr e a t e d F u n c t i o n : $ f o o
$ b a r \n " ; ' ) ;$ f u n c ( ' He l l o ' , ' Wo r l d ! ' ) ;v a r _d u mp ( a d d s l a s h e s ( $ f u n c ) ) ;c a l l _u s e r _f u n c ( $ f u n c , ' F OO' , ' b a z ' ) ;
/ / c o mmo n us ag e e xampl e , wi t h pe r f o r manc e i s s ue s !$ a v = a r r a y ( ' t h e ' , ' a ' , ' t h a t ' , ' t h i s ' ) ;a r r a y _wa l k ( $ a v ,
c r e a t e _f u n c t i o n ( ' &$ v , $ k ' , ' $ v = $ v . " ma n g o " ; ' ) ) ;
p r i n t _r ( $ a v ) ;
create_function
c l a s s I n v o k e Te s t {p u b l i c f u n c t i o n c a l l Me ( ) {
e c h o " Ye a h , y o u c a l l e d me ! \n " ;}
}
$ o = n e w I n v o k e Te s t ( ) ;$ c l a s s = n e w Re f l e c t i o n Cl a s s ( $ o ) ;$ me t h o d = $ c l a s s - >g e t Me t h o d ( ' c a l l Me ' ) ;$ me t h o d - >i n v o k e ( $ o ) ;
f u n c t i o n f o o F u n c ( $ b a r ) {e c h o " $ b a r \n " ;
}$ f u n c = ' f o o F u n c ' ;$ f u n c ( ' He l l o Wo r l d ' ) ;
Funktionen/Methoden Aufrufe
+getAnnotations()+hasAnnotation()+getType()
ezcReflectionProperty
+getAnnotations()+hasAnnotation()
ezcReflectionClass
+getAnnotations()+hasAnnotation()+getReturnType()+isMagic()+isInherited()+isOveridden()+isIntroduced()
ezcReflectionMethod
+getType()
ezcReflectionParameter
ezcReflectionClassType
«interface»ezcReflectionType
ezcReflectionAbstractType
ezcReflectionPrimitiveType ezcReflectionArrayType
ReflectionProperty ReflectionClass ReflectionMethod ReflectionParameter
ezcReflection
+getAnnotations()+hasAnnotation()+getType()
ezcReflectionProperty
+getAnnotations()+hasAnnotation()
ezcReflectionClass
+getAnnotations()+hasAnnotation()+getReturnType()+isMagic()+isInherited()+isOveridden()+isIntroduced()
ezcReflectionMethod
+getType()
ezcReflectionParameter
ezcReflectionClassType
«interface»ezcReflectionType
ezcReflectionAbstractType
ezcReflectionPrimitiveType ezcReflectionArrayType
ReflectionProperty ReflectionClass ReflectionMethod ReflectionParameter
ezcReflection
+getAnnotations()+hasAnnotation()+getType()
ezcReflectionProperty
+getAnnotations()+hasAnnotation()
ezcReflectionClass
+getAnnotations()+hasAnnotation()+getReturnType()+isMagic()+isInherited()+isOveridden()+isIntroduced()
ezcReflectionMethod
+getType()
ezcReflectionParameter
ezcReflectionClassType
«interface»ezcReflectionType
ezcReflectionAbstractType
ezcReflectionPrimitiveType ezcReflectionArrayType
ReflectionProperty ReflectionClass ReflectionMethod ReflectionParameter
ezcReflection
ezcReflection
PHP Core Functions Reflection API
PHP System Runtime DataClasses Objects ...
PH
P V
M
ezcReflection
+getAnnotations()+hasAnnotation()+getType()
ezcReflectionProperty
+getAnnotations()+hasAnnotation()
ezcReflectionClass
+getAnnotations()+hasAnnotation()+getReturnType()+isMagic()+isInherited()+isOveridden()+isIntroduced()
ezcReflectionMethod
+getType()
ezcReflectionParameter
ezcReflectionClassType
«interface»ezcReflectionType
ezcReflectionAbstractType
ezcReflectionPrimitiveType ezcReflectionArrayType
ReflectionProperty ReflectionClass ReflectionMethod ReflectionParameter
PH
P L
angu
age
Leve
l Im
ple
men
tatio
ns
ezcReflection Überblick
• Ermöglicht Reflection auf PHPDoc-Annotationen
• Erweiterte Typ-Informationen zur Laufzeit
• Aber keine direkten Laufzeit-Einflüsse der Typ-Annotationen
• Design ermöglicht beliebige Erweiterungen der Reflection API als Datenquelle mit zu nutzen– z. B. eine StaticReflection
• Genutzt für z. B. WSDL Generierung
/ * ** @we bs e r v i c e* /c l a s s He l l o Wo r l d {
/ * ** @var s t r i ng* /p r i v a t e $ h e l l o = ' He l l o ' ;/ * ** @par am s t r i ng $ name* @r e t ur n s t r i ng* @we bme t ho d* /p u b l i c f u n c t i o n g e t Gr e e t i n g ( $ n a me ) {
r e t u r n " $ t h i s - >h e l l o $ n a me ! " ;}
}
Annotationen
Intercession mit Runkit
func_get_arg()func_get_args()func_num_args()is_subclass_of()get_parent_class()get_class()get_class_methods()get_object_vars()get_class_vars()class_exists()interface_exists()method_exists ()property_exists()get_declared_classes()get_declared_interfaces()get_included_files()get_required_files()ini_set()ini_get()get_defined_constants()get_defined_functions()get_defined_vars()
<<functions>>
PHP System Runtime Data
ReflectionPHP Core
Classes Objects ...
PH
P V
irtu
al M
achi
ne
with
Sys
tem
Lev
el E
xte
nsio
ns
runkit_class_adopt()runkit_class_emancipate()runkit_constant_add()runkit_constant_redefine()runkit_constant_remove()runkit_function_add()runkit_function_copy()runkit_function_redefine()runkit_function_remove()runkit_function_rename()runkit_import()runkit_lint_file()runkit_lint()runkit_method_add()runkit_method_copy()runkit_method_redefine()runkit_method_remove()runkit_method_rename()runkit_return_value_used()runkit_sandbox_output_handler()runkit_superglobals()
<<functions>>
Runkit
ezcReflection und isvcRunkit
PHP Core Functions
PHP System Runtime DataClasses Objects ...
PH
P V
M
isvcRunkit
+emancipate()+setParentClass()+addNewMethod()+addExistingMethod()+removeMethod()
isvcRunkitClass
+getCode()+setCode()+delete()+setName()
isvcRunkitMethod
ReflectionClass ReflectionMethod
Lang
uage
Le
vel I
mp
lem
ent
atio
ns
Reflection API Runkit Extension
ezcReflection
ZUSAMMENFASSUNGMetaprogramming und Reflection
• PHP hat viele Features um Reflection einzusetzen
• Intercession über Runkit-Extension– Vorallem für Tool-Entwicklung
• Reflection hat Einflüsse auf Performance– PHP Ausführungsmodell beachten
– Besonders in Web-Anwendungen nicht erste Wahl
• Nützlich für länger lebende Prozesse/Programme– z. B. GTK+, QT, MFC oder Kommandozeilen-
Anwendungen
Zusammenfassung
Literatur
Paper zu diesem VortragG. GABRYSIAK, S. MARR, AND F. MENGE, Meta Programming and Reflection in PHP, Hasso-Plattner Institute, Universiy of Potsdam, Germany, February 2008. https://instantsvc.svn.sourceforge.net/svnroot/instantsvc/branches/metaprogramming/doc/
Der CodephpCrow – A PHP-PHP-IDE, February 2008.
https://instantsvc.svn.sourceforge.net/svnroot/instantsvc/branches/metaprogramming/src/MetaPHP
ezcReflection, SVN Repository, eZ Components, January 2008. http://svn.ez.no/svn/ezcomponents/experimental/Reflection
Weitere Literatur und Software
[1] R. HIRSCHFELD, Course on "Metaprogrammierung und Reflection", 2007. Lecture Material, Non-public.
[2] G. BRACHA AND D. UNGAR, Mirrors: design principles for meta-level facilities of object-oriented programming languages, in OOPSLA '04: Proceedings of the 19th annual ACM SIGPLAN conference on Object-oriented programming, systems, languages, and applications, New York, NY, USA, 2004, ACM, pp. 331–344.
[3] S. GOLEMON, Runkit, PHP Extension, The PHP Group, January 2008. http://pecl.php.net/package/runkit
[4] A. P. GREGOR KICZALES, Open Implementations and Metaobject Protocols, MIT Press, Cambridge, MA, USA, 1996. http://www2.parc.com/csl/groups/sda/publications/papers/Kiczales-TUT95/for-web.pdf
[5] PHP DOCUMENTATION GROUP, PHP Manual, documentation, November 2007. http://www.php.net/manual/en/language.oop5.reflection.php
[6] THE PHP GROUP, PHP Extension Community Library, Project Site, January 2008. http://pecl.php.net/