Introspection and Reflection in PHP

    Octavia Anghel
    Octavia Anghel
    Share

    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

    Frequently Asked Questions (FAQs) about Introspection and Reflection in PHP

    What is the main purpose of introspection in PHP?

    Introspection in PHP is a powerful feature that allows developers to examine the structure and behavior of objects at runtime. This can be particularly useful when debugging or trying to understand unfamiliar code. With introspection, you can retrieve information about a class’s methods, properties, constants, and parent classes, among other things. It can also be used to dynamically call methods or access properties, making your code more flexible and adaptable.

    How does reflection differ from introspection in PHP?

    While both introspection and reflection deal with examining the structure of objects, they are used in slightly different ways. Reflection is a more advanced feature that not only allows you to examine objects, but also to change their structure and behavior at runtime. This includes creating new instances of a class, invoking methods, and changing property values. Reflection is a powerful tool, but it should be used with caution as it can make code more complex and harder to understand.

    Can you provide an example of using introspection in PHP?

    Sure, here’s a simple example of using introspection to retrieve information about a class:

    class MyClass {
    public $prop1 = "I'm a class property!";
    }

    $obj = new MyClass;
    echo $obj->prop1; // Outputs: I'm a class property!

    In this example, we’re creating a new instance of MyClass and then using introspection to access its public property, prop1.

    How can I use reflection to modify the behavior of an object in PHP?

    Reflection allows you to modify the behavior of an object at runtime. Here’s an example:

    class MyClass {
    private $prop1 = "I'm a class property!";
    }

    $reflector = new ReflectionClass('MyClass');
    $property = $reflector->getProperty('prop1');
    $property->setAccessible(true);

    $obj = new MyClass;
    echo $property->getValue($obj); // Outputs: I'm a class property!

    In this example, we’re using reflection to access a private property of MyClass, which would not be possible with regular object-oriented code.

    What are some potential pitfalls of using introspection and reflection in PHP?

    While introspection and reflection are powerful tools, they should be used with caution. They can make code more complex and harder to understand, especially for developers who are not familiar with these features. Additionally, reflection can potentially break encapsulation by allowing access to private and protected class members. Therefore, it’s important to use these features judiciously and to thoroughly document any code that uses them.

    Can introspection and reflection be used with PHP’s built-in classes?

    Yes, introspection and reflection can be used with PHP’s built-in classes. This can be useful for understanding how these classes work or for debugging purposes. However, keep in mind that the internal structure of built-in classes may change between different versions of PHP, so code that relies on this structure may not be forward-compatible.

    How can I use introspection to list all the methods of a class in PHP?

    You can use the get_class_methods() function to retrieve an array of all the methods of a class. Here’s an example:

    class MyClass {
    public function method1() {}
    public function method2() {}
    }

    print_r(get_class_methods('MyClass')); // Outputs: Array ( [0] => method1 [1] => method2 )

    In this example, get_class_methods() is used to retrieve all the methods of MyClass.

    Can I use reflection to invoke a method with arguments in PHP?

    Yes, you can use the ReflectionMethod class to invoke a method with arguments. Here’s an example:

    class MyClass {
    public function sayHello($name) {
    echo "Hello, $name!";
    }
    }

    $method = new ReflectionMethod('MyClass', 'sayHello');
    $obj = new MyClass;
    $method->invoke($obj, 'World'); // Outputs: Hello, World!

    In this example, we’re using reflection to invoke the sayHello method with the argument ‘World’.

    How can I use introspection to check if a class has a specific method in PHP?

    You can use the method_exists() function to check if a class has a specific method. Here’s an example:

    class MyClass {
    public function myMethod() {}
    }

    var_dump(method_exists('MyClass', 'myMethod')); // Outputs: bool(true)

    In this example, method_exists() is used to check if MyClass has a method named myMethod.

    Can I use reflection to create a new instance of a class without calling its constructor in PHP?

    Yes, you can use the ReflectionClass class to create a new instance of a class without calling its constructor. Here’s an example:

    class MyClass {
    public function __construct() {
    echo "Constructor called!";
    }
    }

    $reflector = new ReflectionClass('MyClass');
    $obj = $reflector->newInstanceWithoutConstructor(); // No output

    In this example, we’re using reflection to create a new instance of MyClass without calling its constructor.