What is the difference between introspection and reflexivity

December 15, 2021 MrAnyx 3 min de lecture

In object-oriented programming, reflexivity is a more complex notion than it appears. Indeed, the reflexivity APIs in most languages generally include two aspects :

  • The introspection, which allows to examine an object, to recover attributes, methods, interfaces, inheritance, ...
  • The reflexivity, which allows to modify the behavior of an object or a class.

We can therefore see that introspection is a more global whole which we call reflexivity.

In the majority of object-oriented languages, there is an API that allows us to apply both the principle of introspection and reflexivity.

It is thanks to this principle that most dumper libraries work. For example, the PHP library symfony/var-dumper uses reflexivity to debug variables, objects, ... By displaying the properties of this object.

However, this principle should not be confused with introspection.

Finally, applying this type of principle allows us to :

  • Access the properties of an object via introspection,
  • Modify the behavior of these objects at the time of program execution via reflexivity,
  • Call a non-static method without creating an object corresponding to the class.

Advantages

Thus, the main advantage of such a principle is that, at first, it becomes easier to debug the behavior of a class or a method, because thanks to reflexivity, and more particularly to introspection, it is possible to recover the implemented interfaces, the inherited classes, the attributes, the methods, ... It is therefore relatively easy to detect unusual behavior on the part of the class we are examining.

Another advantage is that it becomes possible, still thanks to reflexivity, to modify the behavior of a class. For example, if we have to call a private method under certain conditions, thanks to reflexivity, it will be possible to conditionally modify the behavior of the method by making it public.

Example

A complete example is available in a Github repository I created for the occasion : https://github.com/MrAnyx/example-reflexivity

Introspection

Suppose we have the following class.

// src/Student.php

namespace App;

use App\Interfaces\StudentBehaviour;
use Exception;
use Stringable;

class Student extends SchoolPerson implements StudentBehaviour, Stringable
{
    /**
     * @param string $firstname
     * @param string $lastname
     * @param string $birth
     * @param string $schoolName
     * @param string $identifier
     * @throws Exception
     */
    public function __construct(
            string $firstname,
            string $lastname,
            string $birth,
            string $schoolName,
            string $identifier
        ){
        parent::__construct($firstname, $lastname, $birth, $schoolName, $identifier);
    }

    /**
     * @return void
     */
    public function study(): void
    {
        echo "I'm studying for my maths exam";
    }

    /**
     * @return string
     */
    public function __toString(): string
    {
        return parent::__toString() . printf(" and I'm currently studying at the UQAC");
    }
}

Thus, by applying the principle of introspection as illustrated in the code below

// introspection.php

use ReflectionClass;
use App\Student;

$reflection = new ReflectionClass(Student::class);

$constructor = $reflection->getConstructor();
printf("To create an object of type %s, you must specify %d parameters\n",
    $constructor->getDeclaringClass()->getName(),
    $constructor->getNumberOfParameters()
);
if ($constructor->getNumberOfParameters() !== 0) {
    foreach ($constructor->getParameters() as $parameter) {
        printf("    > %s of type %s\n",
            $parameter->getName(),
            $parameter->getType()->getName()
        );
    }
}

We obtain the following result.

To create an object of type App\Student, you must specify 0 parameters
    > firstname of type string
    > lastname of type string
    > birth of type string
    > schoolName of type string
    > identifier of type string

Reflection

As for reflection, let's assume we have the following class.

// src/Foo.php

namespace App;

class Foo
{
    /**
     * @return void
     */
    private function bar(): void
    {
        echo "Initially, I was a private function";
    }
}

Thus, if we apply the reflection principle with the following code

use App\Foo;

// We create a reflection of the "bar" method from the Foo class
$method = new ReflectionMethod(Foo::class, 'bar');

// We modify its behavior by making it accessible from the outside
// In other words, we make the method public
$method->setAccessible(true);

// We call the function
$method->invoke(new Foo);

// We make the method private again
$method->setAccessible(false);

Instead of getting an error when running the program, we get the following result.

Initially, i was a private function

In other words, it was possible to call the bar method of the Foo class even though it is private.

Cover by Vadim Bogulov


Cette œuvre est mise à disposition selon les termes de la licence Licence Creative Commons