Introspection and Reflection in PHP

Tweet

Introspection is a common feature in any programming language which allows object classes to be manipulated by the programmer. You’ll find introspection particularly useful when you don’t know which class or method you need to execute at design time.

Introspection in PHP offers the useful ability to examine classes, interfaces, properties, and methods. PHP offers a large number functions that you can use to accomplish the task. In order to help you understand introspection, I’ll provide a brief overview of some of PHP’s classes, methods, and functions using examples in PHP to highlight how they are used.

During this article, you’ll see a couple examples of how to use some of the most useful PHP’s introspection function and a section dedicated to an API that offers functionality similar to introspection, the Reflection API.

PHP Introspection Functions

In the first example, I’ll demonstrate a handful of PHP’s introspection functions. You can use these functions to extract basic information about classes such as their name, the name of their parent class, and so on.

  • class_exists() – checks whether a class has been defined
  • get_class() – returns the class name of an object
  • get_parent_class() – returns the class name of an object’s parent class
  • is_subclass_of() – checks whether an object has a given parent class

Here is the example PHP code that contains the definition for Introspection and Child classes and outputs information extracted by the functions listed above:

<?php
class Introspection
{
    public function description() {
        echo "I am a super class for the Child class.n";
    }
}

class Child extends Introspection
{
    public function description() {
        echo "I'm " . get_class($this) , " class.n";
        echo "I'm " . get_parent_class($this) , "'s child.n";
    }
}

if (class_exists("Introspection")) {
    $introspection = new Introspection();
    echo "The class name is: " . get_class($introspection) . "n"; 
    $introspection->description();
}

if (class_exists("Child")) {
    $child = new Child();
    $child->description();

    if (is_subclass_of($child, "Introspection")) {
        echo "Yes, " . get_class($child) . " is a subclass of Introspection.n";
    }
    else {
        echo "No, " . get_class($child) . " is not a subclass of Introspection.n";
    }
}

The output of the above code should be as follows:

The class name is: Introspection
I am a super class for the Child class.
I'm Child class.
I'm Introspection's child.
Yes, Child is a subclass of Introspection.

You can determine whether or not the given class has been defined using the class_exists() method, which takes a string argument representing the name of the desired class to check, and an optional Boolean value whether or not to call the autoloader in the process.

The get_class() and get_parent_class() methods return the class name of an object or its parent’s class name respectively. Both takes as arguments an object instance.

The is_subclass_of() methods takes an object instance as its first argument and a string, representing the parent class name, and returns whether the object belongs to a class which is a subclass of the class given as argument.

Here’s a second example containing the definition for a ICurrencyConverter interface and GBPCurrencyConverter class and outputs information extracted by the functions listed above. As with the first example, I’ll list the functions first and then show you some code.

  • get_declared_classes() – returns a list of all declared classes
  • get_class_methods() – returns the names of the class’ methods
  • get_class_vars() – returns the default properties of a class
  • interface_exists() – checks whether the interface is defined
  • method_exists() – checks whether an object defines a method
<?php
interface ICurrencyConverter
{
    public function convert($currency, $amount);
}

class GBPCurrencyConverter implements ICurrencyConverter
{
    public $name = "GBPCurrencyConverter";
    public $rates = array("USD" => 0.622846,
                          "AUD" => 0.643478);
    protected $var1;
    private $var2;

    function __construct() {}

    function convert($currency, $amount) {
        return $rates[$currency] * $amount;
    }
}

if (interface_exists("ICurrencyConverter")) {
    echo "ICurrencyConverter interface exists.n";
}

$classes = get_declared_classes();
echo "The following classes are available:n";
print_r($classes);

if (in_array("GBPCurrencyConverter", $classes)) {
    print "GBPCurrencyConverter is declared.n";
 
    $gbpConverter = new GBPCurrencyConverter();

    $methods = get_class_methods($gbpConverter);
    echo "The following methods are available:n";
    print_r($methods);

    $vars = get_class_vars("GBPCurrencyConverter");
    echo "The following properties are available:n";
    print_r($vars);

    echo "The method convert() exists for GBPCurrencyConverter: ";
    var_dump(method_exists($gbpConverter, "convert"));
}

The output of the above code should be as follows:

ICurrencyConverter interface exists.
The following classes are available:
Array
(
    [0] => stdClass
    [1] => Exception
    [2] => ErrorException
    [3] => Closure
    [4] => DateTime
    [5] => DateTimeZone
    [6] => DateInterval
    [7] => DatePeriod
    ...
    [154] => GBPCurrencyConverter
)
GBPCurrencyConverter is declared.
The following methods are available:
Array
(
    [0] => __construct
    [1] => convert
)
The following properties are available:
Array
(
    [name] => GBPCurrencyConverter
    [rates] => Array
        (
            [USD] => 0.622846
            [AUD] => 0.643478
        )
)
The method convert() exists for GBPCurrencyConverter: bool(true)

As you may have guessed, the interface_exists() method is very similar to class_exists() discussed in the first example. It determines whether or not the given interface has been defined and takes a string argument for the interface name and an optional autoloader Boolean.

The get_declared_classes() method returns an array with the names of all of the defined classes and takes no arguments. Depending on what libraries you have loaded (either complied into PHP or loaded with require/include), additional classes could be present.

The get_class_method() takes either an object instance or a string as argument representing the desired class and returns an array of method names defined by the class.

Notice that from all the properties defined in the ICurrencyConverter class and returned by the get_class_vars() method, only $name and $rates appeared in the output. The private and protected properties were skipped.

PHP Reflection API

PHP supports reflection through its Reflection API. As you can see from the PHP manual, the Reflection API is much more generous then introspection and offers a large number of classes and methods that you can use to accomplish reflection tasks. The ReflectionClass class is the main class of the API and is used to apply reflection over classes, interfaces, and methods and to extract information about all class components. Reflection is easy to implement in your application code and, like introspection, is also very intuitive.

Here is an example to illustrate reflection using the same definitions for the ICurrencyConverter interface and the Child and GBPCurrencyConverter classes:

<?php
$child = new ReflectionClass("Child");
$parent = $child->getParentClass();
echo $child->getName() . " is a subclass of " . $parent->getName() . ".n";

$reflection = new ReflectionClass("GBPCurrencyConverter");
$interfaceNames = $reflection->getInterfaceNames();
if (in_array("ICurrencyConverter", $interfaceNames)) {
    echo "GBPCurrencyConverter implements ICurrencyConverter.n";
}

$methods = $reflection->getMethods();
echo "The following methods are available:n";
print_r($methods);

if ($reflection->hasMethod("convert")) {
    echo "The method convert() exists for GBPCurrencyConverter.n";
}

The output of the above code should be as follows:

Child is a subclass of Introspection.
GBPCurrencyConverter implements ICurrencyConverter.
The following methods are available:
Array
(
    [0] => ReflectionMethod Object
        (
            [name] => __construct
            [class] => GBPCurrencyConverter
        )

    [1] => ReflectionMethod Object
        (
            [name] => convert
            [class] => GBPCurrencyConverter
        )

)
The method convert() exists for GBPCurrencyConverter.

The getInterfaceNames() method returns an array with interface names that a class implements. The getParentClass() method can return a ReflectionClass object representation of the parent class or false if there is no parent. To list the name of the ReflectionClass object, you use the getName() method, as you have seen in the above code.

The getMethods() method retrieves an array of methods and can take as an optional argument a bitmask combination of ReflectionMethod::IS_STATIC, IS_PUBLIC, IS_PROTECTED, IS_PRIVATE, IS_ABSTRACT, and IS_FINAL to filter the list based on visibility.

The Reflection API provides a good implementation of reflection giving you the ability to create more complex applications, such as ApiGen, though further discussion is beyond this goal of this article.

Summary

In this article you’ve seen how to use PHP’s introspection functions and Refection API to obtain information about classes, interfaces, properties, and methods. The purpose of pulling this information is to gain greater insight into your code at run time and to create complex applications.

Image via Fotolia

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • http://jeungun.wordpress.com Jeune

    In what situations would you use reflection and introspection? :)

    • http://www.cellpex.com Cornel Les

      In an ORM you can tell in an entity which of the member is a field from the db table, you can tell what is the db table name the entity is for …
      Ex.
      /**
      * @Table: users
      */
      class User
      {
      /**
      * @field: user_id
      * @type: primary_key
      */
      $id
      by using reflection you will know the actual db name and the actual db field name, now you can build queries to fill that entity …

  • Oliver Bayes-Shelton

    If you wanted to get a list of all properties or methods from a class which would be good for documentation. Or maybe if you want to set a value to a property which is private or protected, but if you wanted to change the value of a private or protected property you should make it public haha.

  • Matt-Houston

    These tools are very useful when trying to troubleshoot yo-yo code. What is yo-yo code you ask? Yo-yo code results in an engineer finding (him|her)self chasing a problem up and down the hierarchy of object inheritance. I find it common in large projects, especially ones that use a tool-kit/CMS/framework/etc beyond it’s supported or intended capabilities.

  • http://stepansuvorov.com/ STEVER

    thx, for article, but would like to have more info about Reflection

  • http://www.nerdherd.com.ph jrsalunga

    maybe you can have more topic about Introspection and give more example and also a topic about Late Static Bindings.

  • http://seaobjects.com emmanuel

    can you add a class member through reflection?