Отражение

PHP 5 содержит комплект API отражений, который дает возможность восстанавливать исходную информацию о классах, интерфейсах, функциях и методах равно как и расширения. Кроме того, API отражений также позволяет восстанавливать комментарии документации к функциям, классам и методам.

API отражения является объектно-ориентированным расширением Zend Engine, состоящим из следующих классов:

<?php
class Reflection { }
interface Reflector { }
class ReflectionException extends Exception { }
class ReflectionFunction implements Reflector { }
class ReflectionParameter implements Reflector { }
class ReflectionMethod extends ReflectionFunction { }
class ReflectionClass implements Reflector { }
class ReflectionObject extends ReflectionClass { }
class ReflectionProperty implements Reflector { }
class ReflectionExtension implements Reflector { }
?>

Пример. Стандартное применение API отражения

<?php
Reflection::export(new ReflectionClass('Exception'));
?>

Результат выполнения данного примера:

Class [  class Exception ] {
- Constants [0] {
}

- Static properties [0] {
}

- Static methods [0] {
}

- Properties [6] {
Property [ protected $message ]
Property [ private $string ]
Property [ protected $code ]
Property [ protected $file ]
Property [ protected $line ]
Property [ private $trace ]
}

- Methods [9] {
Method [ final private method __clone ] {
}

Method [ public method __construct ] {

- Parameters [2] {
Parameter #0 [ $message ]
Parameter #1 [ $code ]
}
}

Method [ final public method getMessage ] {
}

Method [ final public method getCode ] {
}

Method [ final public method getFile ] {
}

Method [ final public method getLine ] {
}

Method [ final public method getTrace ] {
}

Method [ final public method getTraceAsString ] {
}

Method [ public method __toString ] {
}
}
}

ReflectionException

ReflectionException расширяет стандартный класс Exception и генерируется API отражений. Не содержит каких-либо специфических методов или свойств.

ReflectionFunction

Класс ReflectionFunction позволяет восстанавливать исходную информацию о функции

<?php
class ReflectionFunction implements Reflector
{
final private __clone()
public object __construct(string name)
public string __toString()
public static string export(string name, bool return)
public string getName()
public bool isInternal()
public bool isUserDefined()
public string getFileName()
public int getStartLine()
public int getEndLine()
public string getDocComment()
public array getStaticVariables()
public mixed invoke(mixed args)
public mixed invokeArgs(array args)
public bool returnsReference()
public ReflectionParameter[] getParameters()
public int getNumberOfParameters()
public int getNumberOfRequiredParameters()
}
?>

Замечание: getNumberOfParameters() И getNumberOfRequiredParameters() были добавлены в PHP 5.0.3, а invokeArgs() - в PHP 5.1.0.

Для разбора функции сначала придется создать экземпляр класса ReflectionFunction. Затем станет возможным вызов любого из вышеприведенных методов этого экземпляра.

Пример. Использование класса ReflectionFunction

<?php
/**
* Простой счетчик
*
* @return int
*/
function counter()
{
static $c = 0;
return $c++;
}

// Создаем экземпляр класса Reflection_Function
$func = new ReflectionFunction('counter');

// Выводим основную информациюprintf(
"===> The %s function '%s'\n".
" declared in %s\n".
" lines %d to %d\n",
$func->isInternal() ? 'internal' : 'user-defined',
$func->getName(),
$func->getFileName(),
$func->getStartLine(),
$func->getEndline()
);

// Выводим комментарии документации
printf("---> Documentation:\n %s\n", var_export($func->getDocComment(), 1));

// Выводим статические переменные при их наличии
if ($statics = $func->getStaticVariables())
{
printf("---> Static variables: %s\n", var_export($statics, 1));
}

// Вызываем функциюprintf("---> Invokation results in: ");
var_dump($func->invoke());

// можем предпочесть метод export()
echo "\nReflectionFunction::export() results:\n";
echo ReflectionFunction::export('counter');
?>

Замечание: метод invoke() принимает переменное число аргументов, которые передаются функцииon таким же образом, как и в call_user_func().

ReflectionParameter

Класс ReflectionParameter восстанавливает информацию о параметрах функций и методов

<?php
class ReflectionParameter implements Reflector
{
final private __clone()
public object __construct(string name)
public string __toString()
public static string export(mixed function, mixed parameter, bool return)
public string getName()
public bool isPassedByReference()
public ReflectionFunction getDeclaringFunction()
public ReflectionClass getDeclaringClass()
public ReflectionClass getClass()
public bool isArray()
public bool allowsNull()
public bool isPassedByReference()
public bool getPosition()
public bool isOptional()
public bool isDefaultValueAvailable()
public mixed getDefaultValue()
}
?>

Замечание: getDefaultValue(), isDefaultValueAvailable() и isOptional() были добавлены в PHP 5.0.3, а isArray() - в PHP 5.1.0. getDeclaringFunction() and getPosition() были добавлены в PHP 5.1.3.

Для разбора параметров функции сначала создается экземпляр класса ReflectionFunction или ReflectionMethod, а затем используются их метод getParameters() для получения массива параметров.

Пример. Использование класса ReflectionParameters

<?php
function foo($a, $b, $c) { }
function bar(Exception $a, &$b, $c) { }
function baz(ReflectionFunction $a, $b = 1, $c = null) { }
function abc() { }

// Создаем экземпляр класса Reflection_Function с
// параметром, заданным из коммандной строки $reflect = new ReflectionFunction($argv[1]);

echo $reflect;

foreach ($reflect->getParameters() as $i => $param) {
printf(
"-- Parameter #%d: %s {\n".
" Class: %s\n".
" Allows NULL: %s\n".
" Passed to by reference: %s\n".
" Is optional?: %s\n".
"}\n",
$i,
$param->getName(),
var_export($param->getClass(), 1),
var_export($param->allowsNull(), 1),
var_export($param->isPassedByReference(), 1),
$param->isOptional() ? 'yes' : 'no'
);
}
?>

ReflectionClass

Класс ReflectionClass позволяет восстанавливать исходную информацию классов

<?php
class ReflectionClass implements Reflector
{
final private __clone()
public object __construct(string name)
public string __toString()
public static string export(mixed class, bool return)
public string getName()
public bool isInternal()
public bool isUserDefined()
public bool isInstantiable()
public bool hasConstant(string name)
public bool hasMethod(string name)
public bool hasProperty(string name)
public string getFileName()
public int getStartLine()
public int getEndLine()
public string getDocComment()
public ReflectionMethod getConstructor()
public ReflectionMethod getMethod(string name)
public ReflectionMethod[] getMethods()
public ReflectionProperty getProperty(string name)
public ReflectionProperty[] getProperties()
public array getConstants()
public mixed getConstant(string name)
public ReflectionClass[] getInterfaces()
public bool isInterface()
public bool isAbstract()
public bool isFinal()
public int getModifiers()
public bool isInstance(stdclass object)
public stdclass newInstance(mixed args)
public stdclass newInstanceArgs(array args)
public ReflectionClass getParentClass()
public bool isSubclassOf(ReflectionClass class)
public array getStaticProperties()
public mixed getStaticPropertyValue(string name [, mixed default])
public void setStaticPropertyValue(string name, mixed value)
public array getDefaultProperties()
public bool isIterateable()
public bool implementsInterface(string name)
public ReflectionExtension getExtension()
public string getExtensionName()
}
?>

Замечание: hasConstant(), hasMethod(), hasProperty(), getStaticPropertyValue() и setStaticPropertyValue() были введены в PHP 5.1.0, а newInstanceArgs() - в PHP 5.1.3.

Для восстановления информации класса сперва создается экземпляр класса ReflectionClass. Затем можно вызывать любой из вышеприведенных методов этого экземпляра.

Пример. Использование класса ReflectionClass

<?php
interface Serializable
{
// ...
}

class Object
{
// ...
}

/**
* class счетчика
*/
class Counter extends Object implements Serializable
{
const START = 0;
private static $c = Counter::START;

/**
* Вызываем счетчик
*
* @access public
* @return int
*/
public function count() {
return self::$c++;
}
}

// Создаем экземпляр класса ReflectionClass$class = new ReflectionClass('Counter');

// Выводим базовую информациюprintf(
"===> The %s%s%s %s '%s' [extends %s]\n" .
" declared in %s\n" .
" lines %d to %d\n" .
" having the modifiers %d [%s]\n",
$class->isInternal() ? 'internal' : 'user-defined',
$class->isAbstract() ? ' abstract' : '',
$class->isFinal() ? ' final' : '',
$class->isInterface() ? 'interface' : 'class',
$class->getName(),
var_export($class->getParentClass(), 1),
$class->getFileName(),
$class->getStartLine(),
$class->getEndline(),
$class->getModifiers(),
implode(' ', Reflection::getModifierNames($class->getModifiers()))
);

// Выводим комментарии документации
printf("---> Documentation:\n %s\n", var_export($class->getDocComment(), 1));

// Какие интерфейсы задействованы классом
printf("---> Implements:\n %s\n", var_export($class->getInterfaces(), 1));

// Константы класса
printf("---> Constants: %s\n", var_export($class->getConstants(), 1));

// Свойства класса
printf("---> Properties: %s\n", var_export($class->getProperties(), 1));

// Методы
printf("---> Methods: %s\n", var_export($class->getMethods(), 1));

// Если класс позволяет, создаем его экземпляр
if ($class->isInstantiable()) {
$counter = $class->newInstance();

echo '---> $counter is instance? ';
echo $class->isInstance($counter) ? 'yes' : 'no';

echo "\n---> new Object() is instance? ";
echo $class->isInstance(new Object()) ? 'yes' : 'no';
}
?>

Замечание: метод newInstance() принимает переменное число аргументов, которые передаются функции так же, как и в call_user_func().

Замечание: выражения $class = new ReflectionClass('Foo'); $class->isInstance($arg) эквивалентны $arg instanceof Foo или is_a($arg, 'Foo').

ReflectionObject

Класс ReflectionObject позволяет восстанавливать исходную информацию объектов

<?php
class ReflectionObject extends ReflectionClass
{
final private __clone()
public object __construct(mixed object)
public string __toString()
public static string export(mixed object, bool return)
}
?>

ReflectionMethod

Класс ReflectionMethod позволяет восстанавливать исходную информацию методов класса

<?php
class ReflectionMethod extends ReflectionFunction
{
public __construct(mixed class, string name)
public string __toString()
public static string export(mixed class, string name, bool return)
public mixed invoke(stdclass object, mixed args)
public mixed invokeArgs(stdclass object, array args)
public bool isFinal()
public bool isAbstract()
public bool isPublic()
public bool isPrivate()
public bool isProtected()
public bool isStatic()
public bool isConstructor()
public bool isDestructor()
public int getModifiers()
public ReflectionClass getDeclaringClass()

// Inherited from ReflectionFunction
final private __clone()
public string getName()
public bool isInternal()
public bool isUserDefined()
public string getFileName()
public int getStartLine()
public int getEndLine()
public string getDocComment()
public array getStaticVariables()
public bool returnsReference()
public ReflectionParameter[] getParameters()
public int getNumberOfParameters()
public int getNumberOfRequiredParameters()
}
?>

Для исследования метода сначала создается экземпляр класса ReflectionMethods. Затем можно вызывать любой из вышеприведенных методов этого экземпляра.

Пример. Использование класса ReflectionMethod

<?php
class Counter
{
private static $c = 0;

/**
* Инкрементный счетчик
*
* @final
* @static
* @access public
* @return int
*/
final public static function increment()
{
return ++self::$c;
}
}

// Создаем экземпляр класса Reflection_Method
$method = new ReflectionMethod('Counter', 'increment');

// Выводим основную информацию
printf(
"===> The %s%s%s%s%s%s%s method '%s' (which is %s)\n" .
" declared in %s\n" .
" lines %d to %d\n" .
" having the modifiers %d[%s]\n",
$method->isInternal() ? 'internal' : 'user-defined',
$method->isAbstract() ? ' abstract' : '',
$method->isFinal() ? ' final' : '',
$method->isPublic() ? ' public' : '',
$method->isPrivate() ? ' private' : '',
$method->isProtected() ? ' protected' : '',
$method->isStatic() ? ' static' : '',
$method->getName(),
$method->isConstructor() ? 'the constructor' : 'a regular method',
$method->getFileName(),
$method->getStartLine(),
$method->getEndline(),
$method->getModifiers(),
implode(' ', Reflection::getModifierNames($method->getModifiers()))
);

// Комментарии
printf("---> Documentation:\n %s\n", var_export($method->getDocComment(), 1));

// Статические переменные при их наличии
if ($statics= $method->getStaticVariables()) {
printf("---> Static variables: %s\n", var_export($statics, 1));
}

// Вызов метода
printf("---> Invokation results in: ");
var_dump($method->invoke(NULL));
?>

Замечание: При попытке вызова частного (private), защищенного (protected) или абстрактного (abstract) метода результатом будет исключение, генерируемое методом invoke().

Замечание: для статичных (static) методов, как видно выше, следует передавать NULL в качестве первого аргумента invoke(). Не статичным методам передаются экземпляры класса.

ReflectionProperty

Класс ReflectionProperty позволяет восстанавливать исходную информацию свойств класса.

<?php
class ReflectionProperty implements Reflector
{
final private __clone()
public __construct(mixed class, string name)
public string __toString()
public static string export(mixed class, string name, bool return)
public string getName()
public bool isPublic()
public bool isPrivate()
public bool isProtected()
public bool isStatic()
public bool isDefault()
public int getModifiers()
public mixed getValue(stdclass object)
public void setValue(stdclass object, mixed value)
public ReflectionClass getDeclaringClass()
public string getDocComment()
}
?>

Замечание: getDocComment() был добавлен в PHP 5.1.0.

Для получения информации свойства сначала создается экземпляр класса ReflectionProperty. Затем становится возможным вызов любого из вышеприведенных методов этого экземпляра.

Пример. Применение класса ReflectionProperty

<?php
class String
{
public $length = 5;
}

// Создание экземпляра класса ReflectionProperty
$prop = new ReflectionProperty('String', 'length');

// Вывод базовой информации
printf(
"===> The%s%s%s%s property '%s' (which was %s)\n" .
" having the modifiers %s\n",
$prop->isPublic() ? ' public' : '',
$prop->isPrivate() ? ' private' : '',
$prop->isProtected() ? ' protected' : '',
$prop->isStatic() ? ' static' : '',
$prop->getName(),
$prop->isDefault() ? 'declared at compile-time' : 'created at run-time',
var_export(Reflection::getModifierNames($prop->getModifiers()), 1)
);

// Создание экземпляра класса String
$obj= new String();

// Получение текущего значения
printf("---> Value is: ");
var_dump($prop->getValue($obj));

// Изменение значения
$prop->setValue($obj, 10);
printf("---> Setting value to 10, new value is: ");
var_dump($prop->getValue($obj));

// Дамп объекта
var_dump($obj);
?>

Замечание: Попытка получить или задать значение свойства класса типовы private или protected приведет к генерации исключения.

ReflectionExtension

Класс ReflectionExtension позволяет восстанавливать исходную информацию расширений. Вы можете восстановить все загруженные расширения во время исполнения сценария используя get_loaded_extensions()

<?php
class ReflectionExtension implements Reflector {
final private __clone()
public __construct(string name)
public string __toString()
public static string export(string name, bool return)
public string getName()
public string getVersion()
public ReflectionFunction[] getFunctions()
public array getConstants()
public array getINIEntries()
public ReflectionClass[] getClasses()
public array getClassNames()
}
?>

Для получения информации расширения сначала вызывается экземпляр класса ReflectionExtension. Затем становится возможным вызов любого из вышеперечисленных методов этого экземпляра.

Пример. Использование класса ReflectionExtensions

<?php 
// Create an instance of the ReflectionProperty 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)
);
?>

Расширение классов отражения

В случае, если необходимо создавать специализированные версии встроенных классов (скажем, для создания цветной версии HTML во время экспорта, для получения легко доступных переменных вместо методов или для получения методов утилит), имеется возможность расширить встроенные классы.

Пример. Расширение встроенных классов

<?php
/**
* My Reflection_Method class
*/
class My_Reflection_Method extends ReflectionMethod
{
public $visibility = '';

public function __construct($o, $m)
{
parent::__construct($o, $m);
$this->visibility= Reflection::getModifierNames($this->getModifiers());
}
}

/**
* Demo class #1
*
*/
class T {
protected function x() {}
}

/**
* Demo class #2
*
*/
class U extends T {
function x() {}
}

// Вывод информации
var_dump(new My_Reflection_Method('U', 'x'));
?>

Замечание: Если вы переписываете конструктор, не забывайте вызывать родительский конструктор _before_ перед любым кодом, который вы вставляете.

Несоблюдение этого условия вызовет ошибку: Fatal error: Internal error: Failed to retrieve the reflection object