Details of PHP Reflection API

Keywords: Programming PHP Attribute

It has a complete reflection API and adds the ability to reverse engineer classes, interfaces, functions, methods and extensions. In addition, the reflection API provides a way to extract document annotations from functions, classes, and methods.

Note that some of the internal API s are missing the code needed for reflection extension work. For example, a built-in PHP class may lose data about reflection attributes. These few cases are considered errors, but for that reason, they should be discovered and repaired.

Knowing the structure of the human body and the distribution of the true Qi in the body, you can guide the true Qi to the finger, and train it into a Yangzhi, Six-Vein Sword, Flip-finger Shentong, Nine-Yin White Bone Claw, etc. You can also let the true Qi converge, break through Ren Du's two veins, and open a hole in the sky. You can also reverse the whole body meridians and practice toad meridians. The benefits of introspection are evident.

What's the benefit of reflection in letting the code perceive its own structure? Reflection API provides three capabilities for code manipulation at runtime:

  1. Set access control: setAccessible. Private methods/attributes are available. Note: SetAccessible only allows method / member variables to invoke/getValue/setValue, and does not represent a change in access rights defined by the class.
  2. Call function/method: invoke/invokeArgs. With API to obtain function parameters, it can safely transfer parameters and call functions, an enhanced version of call_user_func(_array);
  3. Generate instances independent of constructors: new Instance Without Constructor.

To illustrate the function of reflection API in a singleton, the singleton class code is as follows:

# foo.php
class Foo {
  private static $id;
  private static $instance;
 
  private function __construct() {
    ++ self::$id;
    fwrite(STDOUT, "construct, instance id: " . self::$id . "\n");
  }
 
  public static function getSingleton() {
    if (self::$instance === null) {
      self::$instance = new self();
    }
    return self::$instance;
  }
}

In the Foo class, the constructor is private, the instance can be retrieved only through the getSingleton method, and the singleton is retrieved. However, under the support of reflection API, multiple instances can be obtained:

$instance1 = Foo::getSingleton();
var_dump($instance1);
 
$class = new ReflectionClass("Foo");
$constructor = $class->getConstructor();
if ((ReflectionProperty::IS_PUBLIC & $constructor->getModifiers()) === 0) {
    $constructor->setAccessible(true);
}
$instance2 = $class->newInstanceWithoutConstructor();
$constructor->invoke($instance2);
var_dump($instance2);
 
# Script execution results
construct, instance id: 1
object(Foo)#1 (0) {
}
construct, instance id: 2
object(Foo)#4 (0) {
}

We successfully generated two instances and called the constructor to initialize the object. This is almost impossible without a reflection API.

Some Classes of Reflective API

class Description
Reflection Provide static function export() for summary information of classes
ReflectionClass Class information and tools
ReflectionMethod Class Method Information and Tools
ReflectionParameter Method parameter information
ReflectionProperty Class attribute information
ReflectionFunction Functional information and tools
ReflectionExtension PHP Extension Information
ReflectionException Error class

Using reflection API classes, we can get information about extensions to access objects, functions, and scripts at runtime. This information can be used to analyze classes or build frameworks.

Getting Class Information

We have used some functions to check class attributes in our work, such as get_class_methods, getProduct, etc. These methods have great limitations in obtaining detailed class information.

We can get information about the class by reflecting the static method export provided by API classes: Reflection and ReflectionClass. Export can provide almost all information about the class, including access control status of attributes and methods, parameters required by each method, and the location of each method in the script document. The output of the export static method is the same for the two tool classes, but in different ways.

First, build a simple class

<?php

class Student {
    public    $name;
    protected $age;
    private   $sex;

    public function __construct($name, $age, $sex)
    {
        $this->setName($name);
        $this->setAge($age);
        $this->setSex($sex);
    }

    public function setName($name)
    {
        $this->name = $name;
    }

    protected function setAge($age)
    {
        $this->age = $age;
    }

    private function setSex($sex)
    {
        $this->sex = $sex;
    }
}

Use ReflectionClass::export() to get class information

ReflectionClass::export('Student');

Print results:

Class [ class Student ] {
    @@ D:\wamp\www\test2.php 3-29
    - Constants [0] { }
    - Static properties [0] { }
    - Static methods [0] { }
    - Properties [3] {
        Property [ public $name ]
        Property [ protected $age ]
        Property [ private $sex ]
    }
    - Methods [4] {
        Method [ public method __construct ] {
            @@ D:\wamp\www\test2.php 8 - 13
            - Parameters [3] {
                Parameter #0 [ $name ]
                Parameter #1 [ $age ]
                Parameter #2 [ $sex ]
            }
        }
        Method [ public method setName ] {
            @@ D:\wamp\www\test2.php 15 - 18
            - Parameters [1] {
                Parameter #0 [ $name ]
            }
        }
        Method [ protected method setAge ] {
            @@ D:\wamp\www\test2.php 20 - 23
            - Parameters [1] {
                Parameter #0 [ $age ]
            }
        }
        Method [ private method setSex ] {
            @@ D:\wamp\www\test2.php 25 - 28
            - Parameters [1] {
                Parameter #0 [ $sex ]
            }
        }
    }
}

The ReflectionClass class provides many tools and methods. The official manual gives the following list:

ReflectionClass::__construct — Initialization ReflectionClass class
ReflectionClass::export — Export a class
ReflectionClass::getConstant — Gets a defined constant
ReflectionClass::getConstants — Get a set of constants
ReflectionClass::getConstructor — Get the constructor of the class
ReflectionClass::getDefaultProperties — Get default properties
ReflectionClass::getDocComment — Obtain document annotations
ReflectionClass::getEndLine — Get the number of rows in the last row
ReflectionClass::getExtension — Get the extension based on the defined class ReflectionExtension object
ReflectionClass::getExtensionName — Gets the name of the extension where the defined class resides
ReflectionClass::getFileName — Get the file name of the defined class
ReflectionClass::getInterfaceNames — Access interface( interface)Name
ReflectionClass::getInterfaces — Get interface
ReflectionClass::getMethod — Getting a class method's ReflectionMethod. 
ReflectionClass::getMethods — Get an array of methods
ReflectionClass::getModifiers — Get modifiers for classes
ReflectionClass::getName — Get class name
ReflectionClass::getNamespaceName — Get the name of the namespace
ReflectionClass::getParentClass — Get parent class
ReflectionClass::getProperties — Get a set of attributes
ReflectionClass::getProperty — Getting an attribute of a class ReflectionProperty
ReflectionClass::getReflectionConstant — Gets a ReflectionClassConstant for a class's constant
ReflectionClass::getReflectionConstants — Gets class constants
ReflectionClass::getShortName — Get short name
ReflectionClass::getStartLine — Get the starting line number
ReflectionClass::getStaticProperties — Getting static( static)attribute
ReflectionClass::getStaticPropertyValue — Getting static( static)Attribute value
ReflectionClass::getTraitAliases — Return trait An array of aliases
ReflectionClass::getTraitNames — Returns the class used traits An array of names
ReflectionClass::getTraits — Returns what is used by this class traits array
ReflectionClass::hasConstant — Check that constants are defined
ReflectionClass::hasMethod — Check if the method is defined
ReflectionClass::hasProperty — Check whether properties are defined
ReflectionClass::implementsInterface — Implementation of Interface
ReflectionClass::inNamespace — Check if you are in a namespace
ReflectionClass::isAbstract — Check whether a class is an abstract class( abstract)
ReflectionClass::isAnonymous — Check if the class is anonymous
ReflectionClass::isCloneable — Returns whether a class is replicable
ReflectionClass::isFinal — Check whether the class is declared as final
ReflectionClass::isInstance — Checking an instance of a class
ReflectionClass::isInstantiable — Check whether classes can be instantiated
ReflectionClass::isInterface — Check if the class is an interface( interface)
ReflectionClass::isInternal — Check whether classes are internally defined by extensions or cores
ReflectionClass::isIterateable — Check for iteration( iterateable)
ReflectionClass::isSubclassOf — Check if it is a subclass
ReflectionClass::isTrait — Has returned whether it is a single trait
ReflectionClass::isUserDefined — Check if it is user-defined
ReflectionClass::newInstance — Create a new class instance from the specified parameter
ReflectionClass::newInstanceArgs — Create a new class instance from the given parameters.
ReflectionClass::newInstanceWithoutConstructor — Create a new class instance without calling its constructor
ReflectionClass::setStaticPropertyValue — Setting the value of static properties
ReflectionClass::__toString — Return ReflectionClass Representation of object strings.

Use Reflection::export() to get class information

$prodClass = new ReflectionClass('Student');
Reflection::export($prodClass);

Print results

Class [ class Student ] {
    @@ D:\wamp\www\test2.php 3-29
    - Constants [0] { }
    - Static properties [0] { }
    - Static methods [0] { }
    - Properties [3] {
        Property [ public $name ]
        Property [ protected $age ]
        Property [ private $sex ]
    }
    - Methods [4] {
        Method [ public method __construct ] {
            @@ D:\wamp\www\test2.php 8 - 13
            - Parameters [3] {
                Parameter #0 [ $name ]
                Parameter #1 [ $age ]
                Parameter #2 [ $sex ]
            }
        }
        Method [ public method setName ] {
            @@ D:\wamp\www\test2.php 15 - 18
            - Parameters [1] {
                Parameter #0 [ $name ]
            }
        }
        Method [ protected method setAge ] {
            @@ D:\wamp\www\test2.php 20 - 23
            - Parameters [1] {
                Parameter #0 [ $age ]
            }
        }
        Method [ private method setSex ] {
            @@ D:\wamp\www\test2.php 25 - 28
            - Parameters [1] {
                Parameter #0 [ $sex ]
            }
        }
    }
}

After you create the ReflectionClass object, you can use the ReflectionTool class to output information about the Student class. Reflection::export() can format and output instances of any class that implements the Reflector interface.

Check class

The RelectionClass toolkit class we learned earlier provides a lot of toolkit methods for getting information about classes. For example, we can get the type of Student class and whether it can be instantiated or not.

Instrumental function

function classData(ReflectionClass $class) {
    $details = '';
    $name = $class->getName();          // Returns the class name to be checked
    if ($class->isUserDefined()) {      // Check whether the class is user-defined
        $details .= "$name is user defined" . PHP_EOL;
    }
    if ($class->isInternal()) {         // Check whether classes are internally defined by extensions or cores
        $details .= "$name is built-in" . PHP_EOL;
    }
    if ($class->isInterface()) {        // Check if the class is an interface
        $details .= "$name is interface" . PHP_EOL;
    }
    if ($class->isAbstract()) {         // Check whether a class is an abstract class
        $details .= "$name is an abstract class" . PHP_EOL;
    }
    if ($class->isFinal()) {            // Check whether the class is declared final
        $details .= "$name is a final class" . PHP_EOL;
    }
    if ($class->isInstantiable()) {     // Check whether classes can be instantiated
        $details .= "$name can be instantiated" . PHP_EOL;
    } else {
        $details .= "$name can not be instantiated" . PHP_EOL;
    }
    return $details;
}

$prodClass = new ReflectionClass('Student');
print classData($prodClass);

Print results

Student is user defined
Student can be instantiated

In addition to obtaining information about classes, ReflectionClass objects can also provide source code information such as the file name of the custom class and the start and end lines of the class in the file.

function getClassSource(ReflectionClass $class) {
    $path  = $class->getFileName();  // Get the absolute path of the class file
    $lines = @file($path);           // Get an array of all lines in a file
    $from  = $class->getStartLine(); // Provide the starting line of the class
    $to    = $class->getEndLine();   // Termination rows for providing classes
    $len   = $to - $from + 1;
    return implode(array_slice($lines, $from - 1, $len));
}

$prodClass = new ReflectionClass('Student');
var_dump(getClassSource($prodClass));

Print results

string 'class Student {
    public    $name;
    protected $age;
    private   $sex;

    public function __construct($name, $age, $sex)
    {
        $this->setName($name);
        $this->setAge($age);
        $this->setSex($sex);
    }

    public function setName($name)
    {
        $this->name = $name;
    }

    protected function setAge($age)
    {
        $this->age = $age;
    }

    private function setSex($sex)
    {
        $this->sex = $sex;
    }
}
' (length=486)

We see that getClassSource accepts a ReflectionClass object as its parameter and returns the source code of the corresponding class. This function ignores error handling and should check parameters and result codes in practice.

Inspection method

Similar to checking classes, ReflectionMethod objects can be used to check methods in classes.

There are two ways to obtain ReflectionMethod objects:

The first is to get an array of ReflectionMethod objects through ReflectionClass::getMethods(), which has the advantage of returning ReflectionMethod objects for all methods in the class without knowing the method name in advance.

The second is to instantiate objects directly using ReflectionMethod classes, which can only get one class method object and need to know the method name in advance.

Tool methods for ReflectionMethod objects:

ReflectionMethod::__construct — ReflectionMethod Constructive function
ReflectionMethod::export — Output a callback method
ReflectionMethod::getClosure — Returns a dynamically established method invocation interface. Translator's Note: This return value can be used to invoke private methods directly.
ReflectionMethod::getDeclaringClass — Getting the class representation of reflection function call parameters
ReflectionMethod::getModifiers — Get modifiers for methods
ReflectionMethod::getPrototype — Return method prototype (If exist)
ReflectionMethod::invoke — Invoke
ReflectionMethod::invokeArgs — Parametric execution
ReflectionMethod::isAbstract — Judging whether a method is abstract
ReflectionMethod::isConstructor — Judging whether a method is a construction method
ReflectionMethod::isDestructor — Judging whether a method is a destructive method
ReflectionMethod::isFinal — Judgment method is negative meaning final
ReflectionMethod::isPrivate — Judging whether a method is private
ReflectionMethod::isProtected — Judging whether a method is a protection method (protected)
ReflectionMethod::isPublic — Whether the judgment method is public or not
ReflectionMethod::isStatic — Judging whether a method is static
ReflectionMethod::setAccessible — Set whether the method is accessible
ReflectionMethod::__toString — String representation of return reflection method objects

ReflectionClass::getMethods()

We can get an array of ReflectionMethod objects through ReflectionClass::getMethods().

$prodClass = new ReflectionClass('Student');
$methods = $prodClass->getMethods();
var_dump($methods);

Print results

array (size=4)
  0 => &
    object(ReflectionMethod)[2]
      public 'name' => string '__construct' (length=11)
      public 'class' => string 'Student' (length=7)
  1 => &
    object(ReflectionMethod)[3]
      public 'name' => string 'setName' (length=7)
      public 'class' => string 'Student' (length=7)
  2 => &
    object(ReflectionMethod)[4]

You can see that we have an array of ReflectionMethod objects for Student, each element being an object with two common attributes, name being the method name and class being the class to which it belongs. We can call object methods to get information about methods.

ReflectionMethod

Use the ReflectionMethod class directly to get information about class methods

$method = new ReflectionMethod('Student', 'setName');
var_dump($method);

Print results

object(ReflectionMethod)[1]
  public 'name' => string 'setName' (length=7)
  public 'class' => string 'Student' (length=7)

Be careful

In PHP 5, ReflectionMethod::retursReference() does not return true if the checked method only returns the object (even if the object is assigned or passed by reference). ReflectionMethod::returnsReference() returns true only if the method being detected has been explicitly declared to return a reference (with A & sign in front of the method name).

Check method parameters

In PHP 5, declaring class methods can limit the type of objects in parameters, so it is necessary to check the parameters of methods.

Similar to checking methods, ReflectionParameter objects can be used to check methods in classes. This object can tell you the name of parameters, whether variables can be passed by reference, and whether parameter type prompts and methods accept null values as parameters.

There are two ways to get the ReflectionParameter object, which is very similar to getting the ReflectionMethod object:

The first is to return the ReflectionParameter object array through the ReflectionMethod::getParameters() method, which can obtain all the parameter objects of a method.

The second is to instantiate the object directly using ReflectionParameter class, which can only get the object with a single parameter.

Tool methods for ReflectionParameter objects:

rameter::allowsNull — Checks if null is allowed
ReflectionParameter::canBePassedByValue — Returns whether this parameter can be passed by value
ReflectionParameter::__clone — Clone
ReflectionParameter::__construct — Construct
ReflectionParameter::export — Exports
ReflectionParameter::getClass — Get the type hinted class
ReflectionParameter::getDeclaringClass — Gets declaring class
ReflectionParameter::getDeclaringFunction — Gets declaring function
ReflectionParameter::getDefaultValue — Gets default parameter value
ReflectionParameter::getDefaultValueConstantName — Returns the default value's constant name if default value is constant or null
ReflectionParameter::getName — Gets parameter name
ReflectionParameter::getPosition — Gets parameter position
ReflectionParameter::getType — Gets a parameter's type
ReflectionParameter::hasType — Checks if parameter has a type
ReflectionParameter::isArray — Checks if parameter expects an array
ReflectionParameter::isCallable — Returns whether parameter MUST be callable
ReflectionParameter::isDefaultValueAvailable — Checks if a default value is available
ReflectionParameter::isDefaultValueConstant — Returns whether the default value of this parameter is constant
ReflectionParameter::isOptional — Checks if optional
ReflectionParameter::isPassedByReference — Checks if passed by reference
ReflectionParameter::isVariadic — Checks if the parameter is variadic
ReflectionParameter::__toString — To string

ReflectionMethod::getParameters()

With the same acquisition method, this method returns an array containing ReflectionParameter objects for each parameter of the method.

$method = new ReflectionMethod('Student', 'setName');
$params = $method->getParameters();
var_dump($params);

Print results

array (size=1)
  0 => &
    object(ReflectionParameter)[2]
      public 'name' => string 'name' (length=4)

ReflectionParameter

Let's take a look at this approach. For a better understanding, I'll modify the setName method of the Student class and add two parameters a, b.

...
    public function setName($name, $a, $b)
    {
        $this->name = $name;
    }
...

Let's first look at the construction of ReflectionParameter classes

public ReflectionParameter::__construct ( string $function , string $parameter )

You can see that this class is instantiated with two parameters:

$function: When you need to get a function as a public function, just pass the name of the function. When the function is a class method, you need to pass an array in the form of array ('class','function').

$parameter: This parameter can be passed in two ways: the first is the parameter name (without a $symbol), and the second is the parameter index. Note: Whether the parameter name or index, the parameter must exist, otherwise it will be wrong.

The following examples are given:

$params = new ReflectionParameter(array('Student', 'setName'), 1);
var_dump($params);

Print results

object(ReflectionParameter)[1]
  public 'name' => string 'a' (length=1)

Let's define another function to test.

function foo($a, $b, $c) { }
$reflect = new ReflectionParameter('foo', 'c');
var_dump($reflect);

Print results

object(ReflectionParameter)[2]
  public 'name' => string 'c' (length=1)

epilogue

The reflection API of php is very powerful, it can get the detailed information of a class. Module objects can be dynamically invoked by writing a class through the reflection API, which is free to load third-party plug-ins and integrate into existing systems. There is no need to hard-code third-party code into the original code. Although reflection is seldom used in actual development, understanding reflection API is very helpful for understanding code structure and developing business patterns.

Posted by sactown on Fri, 27 Sep 2019 01:52:07 -0700