9.3 Reflection

The Reflection classes allow you to retrieve detailed data about classes. Although you don't need to use them in most web applications, they're invaluable when you're writing a program such as a class documenter, unit tester, or debugger. These applications all require generic class-manipulation routines.

PHP 4 lets you discover information about classes using a series of functions. There's get_class_methods( ) to get an array of class methods and get_class_vars( ) for an array of properties. These functions aren't integrated, so you cannot find out all the information about a class at once.

The functions are also very simplistic. They don't contain any of the new information that you can set in PHP 5. For example, there's no way to distinguish between private, protected, and public methods and properties.

That's where the Reflection classes come in. They help you extract from a class or method any piece of data you need.

9.3.1 Getting an Overview with Reflection::export

To understand how the Reflection classes work, Example 9-10 contains an example Person class that uses many of PHP 5's OO features.

Example 9-10. Person class
class Person {

    public $name;

    protected $spouse;

    private $password;

    

    public function _ _construct($name) {

        $this->name = $name

    }



    public function getName( ) {

        return $name;

    }



    protected function setSpouse(Person $spouse) {

        if (!isset($this->spouse)) { 

            $this->spouse = $spouse;

        }

    }



    private function setPassword($password) {

        $this->password = $password;

    }

}

For a quick overview of the class, call Reflection::export( ):

Reflection::export(new ReflectionClass('Person'));

Class [ <user> class Person ] {

  @@ /www/reflection.php 3-25



  - Constants [0] {

  }



  - Static properties [0] {

  }



  - Static methods [0] {

  }



  - Properties [3] {

    Property [ <default> public $name ]

    Property [ <default> protected $spouse ]

    Property [ <default> private $password ]

  }



  - Methods [4] {

    Method [ <user> <ctor> public method _ _construct ] {

      @@ /www/reflection.php 8 - 10



      - Parameters [1] {

        Parameter #0 [ $name ]

      }

    }



    Method [ <user> public method getName ] {

      @@ /www/reflection.php 12 - 14

    }



    Method [ <user> protected method setSpouse ] {

      @@ /www/reflection.php 16 - 20



      - Parameters [1] {

        Parameter #0 [ Person or NULL $spouse ]

      }

    }



    Method [ <user> private method setPassword ] {

      @@ /www/reflection.php 22 - 24



      - Parameters [1] {

        Parameter #0 [ $password ]

      }

    }

  }

}

The Reflection::export( ) static method takes an instance of the ReflectionClass class and returns a copious amount of information. As you can see, it details the number of constants, static properties, static methods, properties, and methods in the class. Each item is broken down into component parts. For instance, all the entries contain visibility identifiers (private, protected, or public), and methods have a list of their parameters underneath their definition.

Reflection::export( ) not only reports the file where everything is defined, but even gives the line numbers! This lets you extract code from a file and place it in your documentation.

9.3.2 Inspecting Classes, Methods, and Properties

Reflection::export( ) is a handy way to see everything about a class at once, but it's not a very useful method of extracting specific pieces of information.

That's where the individual Reflection classes come in. There's a Reflection class for every major entity in PHP: classes, functions, methods, parameters, properties, and even extensions. Instances of these classes have methods that you can query to determine whether a class is abstract, a method is final, or a property is private. For example:

$class = new ReflectionClass('Person');

if ($class->isAbstract( )) {

    // Person is abstract

}



$method = new ReflectionMethod('Person', 'getName');

if ($method->isFinal( )) {

    // Person::getName( ) is final

}



$property = new ReflectionProperty('Person', 'name');

if ($property->isPrivate( )) {

    // Person::name is private

}

To inspect a class, create a new ReflectionClass, passing the class name to the constructor. You can query the returned object using any of the methods listed in the next section in Table 9-3.

The ReflectionMethod and ReflectionProperty classes operate similarly, except that they take two arguments. The first argument is still the class name, and the second is the method or property name. Table 9-5 has a list of all the ReflectionMethod methods, and Table 9-6 covers ReflectionProperty.

While it's somewhat useful to check specific methods or properties, the utility of the Reflection classes shines through when they're used in conjunction with each other. For instance, you want to create API documentation for your classes. However, you only want to document public methods because private and protected methods are reserved for internal use. Example 9-11 shows how to do this, using the Person class in Example 9-10 as a base.

Example 9-11. Printing public methods
$class = new ReflectionClass('Person');

foreach ($class->getMethods( ) as $method) {

    if ($method->isPublic( )) {

        print $method->getName( ) . "( )\n";

    }

}

_ _construct( )

getName( )

The getMethods( ) method in Example 9-11 returns an array containing all the methods in the class. The elements of the array are ReflectionMethod( ) objects.

A ReflectionMethod object can be intraspected similarly to a ReflectionClass. Therefore, you can call isPublic( ) to discover whether a method is declared public. If it is, the example uses getName( ) to retrieve its name and prints it out.

You can use another class, ReflectionParameter, to add further detail to your documentation, as in Example 9-12.

Example 9-12. Printing public methods with parameters
$class = new ReflectionClass('Person');

foreach ($class->getMethods( ) as $method) {

    if ($method->isPublic( )) {

        print $method->getName( ) . '(';

        $params = '';

        foreach($method->getParameters( ) as $parameter) {

            $params .= '$' . $parameter->getName( ) . ', ';

        }

        print substr($params, 0, -2) . ")\n";

    }

}

Now you can also list method parameters:

_ _construct($name)

getName( )

The ReflectionParameter methods appear in Table 9-7.

9.3.3 Reflection Class Methods

This section provides a complete listing of every method in all six of the Reflection classes. Each entry in Tables Table 9-3 through Table 9-8 details what parameters a method takes and what types of values it returns.

There are just over 100 methods in total, but the number of unique methods is almost half of that. As you can see, there's a lot of duplication. For example, classes, methods, and properties can all be declared public; therefore, the ReflectionClass, ReflectionMethod, and ReflectionProperty classes all have an isPublic( ) method.

The method names are largely self-explanatory, but here's a quick overview that explains the naming and behavior patterns.

All of the methods beginning with get retrieve values or objects. For instance, getName( ) returns the object's name.

Some get methods return an array of objects, such as getMethods( ), which returns an array of ReflectionMethods. These methods are marked with square array brackets ([ ]), like ReflectionMethod[ ].

If a method begins with is, it returns a boolean value: either true or false.

One method different from the rest is invoke( ). This method is similar to call_user_func( ), as it lets you pass arguments to a method or function. For example:

function add($i, $j) {

    return $i + $j; 

}



$math = new ReflectionFunction('add');

print $math->invoke(1, 2);

3

While it makes little sense to create a ReflectionFunction just to call invoke( ), it's handy when you're using the Reflection classes to automate the processing of class methods. One example program is a unit testing application that takes an object and runs all methods beginning with the word test.

Table 9-3. ReflectionClass methods

Name

Returns

ReflectionClass::_ _construct(mixed argument) throws ReflectionException

ReflectionClass

ReflectionClass::_ _toString( )

string

ReflectionClass::export(mixed argument, [, bool return]) throws ReflectionException

mixed

ReflectionClass::getConstant(string name)

mixed

ReflectionClass::getConstants( )

array

ReflectionClass::getConstructor( )

ReflectionMethod

ReflectionClass ReflectionProperty::getDeclaringClass( )

ReflectionClass

ReflectionClass::getDefaultProperties( )

array

ReflectionClass::getDocComment( )

string

ReflectionClass::getEndLine( )

int

ReflectionClass::getExtension( )

ReflectionExtension or NULL

ReflectionClass::getExtensionName( )

string or false

ReflectionClass::getFileName( )

string

ReflectionClass::getInterfaces( )

ReflectionClass[ ]

ReflectionClass::getMethod(string name)

ReflectionMethod

ReflectionClass::getMethods( )

ReflectionMethod[ ]

ReflectionClass::getModifiers( )

int

ReflectionClass::getName( )

string

ReflectionClass::getParentClass( )

ReflectionClass

ReflectionClass::getProperties( )

ReflectionProperty[ ]

ReflectionClass::getProperty(string name)

ReflectionProperty

ReflectionClass::getStartLine( )

int

ReflectionClass::getStaticProperties( )

array

ReflectionClass::implementsInterface(string or ReflectionClass interface_name)

boolean

ReflectionClass::isAbstract( )

boolean

ReflectionClass::isFinal( )

boolean

ReflectionClass::isInstance(object)

boolean

ReflectionClass::isInstantiable( )

boolean

ReflectionClass::isInterface( )

boolean

ReflectionClass::isInternal( )

boolean

ReflectionClass::isIterateable( )

boolean

ReflectionClass::isSubclassOf(string or ReflectionClass class)

boolean

ReflectionClass::isUserDefined( )

boolean

ReflectionClass::newInstance(mixed args)

object


Table 9-4. ReflectionFunction methods

Name

Returns

ReflectionFunction::_ _construct(string name)

ReflectionFunction

ReflectionFunction::_ _toString( )

string

ReflectionFunction::export(string name, [, bool return]) throws Reflection_Exception

mixed

ReflectionFunction::getDocComment( )

string

ReflectionFunction::getEndLine( )

int

ReflectionFunction::getFileName( )

string

ReflectionFunction::getName( )

string

ReflectionFunction::getParameters( )

Reflection_Parameter[ ]

ReflectionFunction::getStartLine( )

int

ReflectionFunction::getStaticVariables( )

array

ReflectionFunction::invoke(mixed args)

mixed

ReflectionFunction::isInternal( )

bool

ReflectionFunction::isUserDefined( )

bool

ReflectionFunction::returnsReference( )

bool


Table 9-5. ReflectionMethod methods

Name

Returns

ReflectionMethod::_ _construct(mixed class, string name)

ReflectionMethod

ReflectionMethod::_ _toString( )

string

ReflectionMethod::export(mixed class, string name, [, bool return]) throws Reflection_Exception

mixed

ReflectionMethod::getDeclaringClass( )

Reflection_Class

ReflectionMethod::getDocComment( )

string

ReflectionMethod::getEndLine( )

int

ReflectionMethod::getFileName( )

string

ReflectionMethod::getModifiers( )

int

ReflectionMethod::getName( )

string

ReflectionMethod::getParameters( )

Reflection_Parameter[ ]

ReflectionMethod::getStartLine( )

int

ReflectionMethod::getStaticVariables( )

array

ReflectionMethod::invoke(mixed object, mixed args)

mixed

ReflectionMethod::isAbstract( )

boolean

ReflectionMethod::isConstructor( )

boolean

ReflectionMethod::isDestructor( )

boolean

ReflectionMethod::isFinal( )

boolean

ReflectionMethod::isInternal( )

boolean

ReflectionMethod::isPrivate( )

boolean

ReflectionMethod::isProtected( )

boolean

ReflectionMethod::isPublic( )

boolean

ReflectionMethod::isStatic( )

boolean

ReflectionMethod::isUserDefined( )

boolean

ReflectionMethod::returnsReference( )

boolean


Table 9-6. ReflectionProperty methods

Name

Returns

ReflectionProperty::_ _construct(mixed class, string name)

ReflectionProperty

ReflectionProperty::_ _toString( )

string

ReflectionProperty::export(mixed class, string name, [, bool return]) throws Reflection_Exception

mixed

ReflectionProperty::getDeclaringClass( )

Reflection_Class

ReflectionProperty::getModifiers( )

int

ReflectionProperty::getName( )

string

ReflectionProperty::getValue(object object)

mixed

ReflectionProperty::isDefault( )

boolean

ReflectionProperty::isPrivate( )

boolean

ReflectionProperty::isProtected( )

boolean

ReflectionProperty::isPublic( )

boolean

ReflectionProperty::isStatic( )

boolean

ReflectionProperty::setValue(object object, mixed value)

void


Table 9-7. ReflectionParameter methods

Name

Returns

ReflectionParameter::_ _construct(mixed function, mixed parameter)

ReflectionParameter

ReflectionParameter::_ _toString( )

string

ReflectionParameter::allowsNull( )

boolean

ReflectionParameter::export(mixed function, mixed parameter, [, bool return]) throws Reflection_Exception

mixed

ReflectionParameter::getClass( )

string

ReflectionParameter::getName( )

string

ReflectionParameter::isPassedByReference( )

boolean


Table 9-8. ReflectionExtension methods

Name

Returns

ReflectionExtension::_ _construct(string name)

ReflectionExtension

ReflectionExtension::_ _toString( )

string

ReflectionExtension::export(string name, [, bool return]) throws Reflection_Exception

mixed

ReflectionExtension::getClasses( )

Reflection_Class[ ]

ReflectionExtension::getClassNames( )

array

ReflectionExtension::getConstants( )

array

ReflectionExtension::getFunctions( )

Reflection_Function[ ]

ReflectionExtension::getINIEntries( )

array

ReflectionExtension::getName( )

string

ReflectionExtension::getVersion( )

string