Reflection mechanism

brief introduction

PHP Reflection API is a new function of PHP 5. It is used to export or extract detailed information about classes, methods, properties, parameters, etc., including comments.

class Reflection { } 
interface Reflector { }
class ReflectionException extends Exception { }
class ReflectionFunction implements Reflector { }
class ReflectionParameter implements Reflector { }
class ReflectionMethod extends ReflectionFunction { }
class ReflectionClass implements Reflector { }
class ReflectionObject extends ReflectionClass { }
class ReflectionProperty implements Reflector { }
class ReflectionExtension implements Reflector { } 

There are only two reflectionclasses and ReflectionObject that are used more often. They are both the same. The former is for classes, the latter is for objects, and the latter is a class that inherits the former. Then some properties or methods can return the corresponding ReflectionProperty and Reflection Method

ReflectionClass

Refer to the manual for details: http://php.net/manual/zh/clas...

With ReflectionClass, we can get the following information about the Person class:

  • Constant contents
  • Property Names
  • Method Names
  • Static Properties
  • Namespace
  • Whether the Person class is final or abstract
<?php
namespace app;

class Person{
  /**
   * For the sake of demonstration, we"re setting this private
   */
  private $_allowDynamicAttributes = false;

  /** type=primary_autoincrement */
  protected $id = 0;

  /** type=varchar length=255 null */
  protected $name;

  /** type=text null */
  protected $biography;

  public function getId(){
    return $this->id;
  }

  public function setId($v){
    $this->id = $v;
  }

  public function getName(){
    return $this->name;
  }

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

  public function getBiography(){
    return $this->biography;
  }

  public function setBiography($v){
    $this->biography = $v;
  }
}

//Pass in the class name or object
$class = new \ReflectionClass('app\Person');

//Gets a property, whether it is public or not
$properties = $class->getProperties();
foreach($properties as $property) {
  echo $property->getName()."\n";
}
// Output:
// _allowDynamicAttributes
// id
// name
// biography

//By default, ReflectionClass will get all the attributes, private and protected can also. If you only want to get the private property, you need to transfer an extra parameter:
/*
 * ReflectionProperty::IS_STATIC
 * ReflectionProperty::IS_PUBLIC
 * ReflectionProperty::IS_PROTECTED
 * ReflectionProperty::IS_PRIVATE
 */
 
 //↓ pay attention to a | combination: obtain the attribute of is | private or is | protected
$private_properties = $class->getProperties(\ReflectionProperty::IS_PRIVATE|\ReflectionProperty::IS_PROTECTED);

foreach($private_properties as $property) {
  //↓ if the attribute is a protected attribute;
  if($property->isProtected()) {

    // ↓ get comments
    $docblock = $property->getDocComment();
    preg_match('/ type\=([a-z_]*) /', $property->getDocComment(), $matches);
    echo $matches[1]."\n";
  }
}
// Output:
// primary_autoincrement
// varchar
// text

$data = array("id" => 1, "name" => "Chris", "biography" => "I am am a PHP developer");
foreach($data as $key => $value) {
  if(!$class->hasProperty($key)) {
    throw new \Exception($key." is not a valid property");
  }

  if(!$class->hasMethod("get".ucfirst($key))) {
    throw new \Exception($key." is missing a getter");
  }

  if(!$class->hasMethod("set".ucfirst($key))) {
    throw new \Exception($key." is missing a setter");
  }

  $object = new Person();

  // http://php.net/manual/zh/class.reflectionmethod.php
  // getMethod gets a reflectionmethod object of the method, and then uses the invoke method in it;
  $setter = $class->getMethod("set".ucfirst($key));
  $ok = $setter->invoke($object, $value);

  // Get the setter method and invoke it
  $getter = $class->getMethod("get".ucfirst($key));
  $objValue = $getter->invoke($object);

  // Now compare
  if($value == $objValue) {
    echo "Getter or Setter has modified the data.\n";
  } else {
    echo "Getter and Setter does not modify the data.\n";
  }
}

getMethod and invoke

ReflectionClass::getMethod - get the ReflectionMethod of a class method (it can be understood to obtain the control of this class method, whether it is public or not).

Specific reference:

<?php
class HelloWorld {
  private function sayHelloTo($name,$arg1,$arg2) {
    return 'Hello ' . $name.' '.$arg1.' '.$arg2;
  }
}

$obj = new HelloWorld();
// The first parameter can be an object or a class
$reflectionMethod = new ReflectionMethod($obj , 'sayHelloTo');
if(!$reflectionMethod -> isPublic()){
  $reflectionMethod -> setAccessible(true);
}
/*
 * public mixed ReflectionMethod::invoke ( object $object [, mixed $parameter [, mixed $... ]] )
 * 1. Get the ReflectionMethod of a class method
 * 2. $object The object of the class instance in which the method is located, and then the second parameter is set to each parameter of the method;
 * 3. This method can be executed through invoke
 */
echo $reflectionMethod->invoke($obj, 'GangGe','How','are you');

//You can also pass in parameters as arrays
echo $reflectionMethod -> invokeArgs($obj,array('GangGe','How','are you'));

getProperty

Get an instance of the ReflectionProperty class (as above, get control of the property) http://cn2.php.net/manual/zh/...

getValue get property value
public mixed ReflectionProperty::getValue ([ object $object ] )

If the class attribute to get the instance is not a static attribute, the instance of the class must be passed

<?php
class Foo {
  public static $staticProperty = 'foobar';
  public $property = 'barfoo';
  protected $privateProperty = 'foofoo';
}
$reflectionClass = new ReflectionClass('Foo');
var_dump($reflectionClass->getProperty('staticProperty')->getValue()); //Static properties can be without parameters
var_dump($reflectionClass->getProperty('property')->getValue(new Foo)); //A class instance must be passed to a non static attribute
$reflectionProperty = $reflectionClass->getProperty('privateProperty'); //The protected property needs to obtain its permission through setAccessible
$reflectionProperty->setAccessible(true);
var_dump($reflectionProperty->getValue(new Foo));

Example

Implementation of controller calling method in the framework of simulation YII

<?php

if (PHP_SAPI != 'cli') {
    exit('Please run it in terminal!');
}
if ($argc < 3) {
    exit('At least 2 arguments needed!');
}
 
$controller = ucfirst($argv[1]) . 'Controller';
$action = 'action' . ucfirst($argv[2]);
 
// Check if the class exists
if (!class_exists($controller)) {
    exit("Class $controller does not existed!");
}
 
// Get reflection of class
$reflector = new ReflectionClass($controller);
// Check if the method exists
if (!$reflector->hasMethod($action)) {
    exit("Method $action does not existed!");
}
 
// Take the constructor of the class and return the ReflectionMethod object
$constructor = $reflector->getConstructor();

// Take the parameters of the constructor, which is an array of objects
$parameters = $constructor->getParameters();

// Ergodic parameter
foreach ($parameters as $key => $parameter) {
    // Get the class of parameter declaration
    $injector = new ReflectionClass($parameter->getClass()->name);
    // Instantiate the parameter declaration class and fill in the parameter list
    $parameters[$key] = $injector->newInstance(); //Instantiate $parameter - > getclass() - > name class
}
 
// Using the parameter list instance controller class
$instance = $reflector->newInstanceArgs($parameters);
// implement
$instance->$action();
 
class HelloController
{
    private $model;
 
    public function __construct(TestModel $model)
    {
        $this->model = $model;
    }
 
    public function actionWorld()
    {
        echo $this->model->property, PHP_EOL;
    }
}
 
class TestModel
{
    public $property = 'property';
}

Implementation of front and back controller in TP framework

<?php
class BlogAction {

    public function detail() {
        echo 'detail' . "\r\n";
    }

    public function test($year = 2014, $month = 4, $day = 21) {
        echo $year . '--' . $month . '--' . $day . "\r\n";
    }

    public function _before_detail() {
        echo __FUNCTION__ . "\r\n";
    }

    public function _after_detail() {
        echo __FUNCTION__ . "\r\n";
    }
}

// Execute the detail method
$method = new ReflectionMethod('BlogAction', 'detail');
$instance = new BlogAction();

// Make authority judgment
if ($method->isPublic()) {

    $class = new ReflectionClass('BlogAction');

    // Execute pre method
    if ($class->hasMethod('_before_detail')) {
        $beforeMethod = $class->getMethod('_before_detail');
        if ($beforeMethod->isPublic()) {
            $beforeMethod->invoke($instance);
        }
    }

    $method->invoke(new BlogAction);

    // Execute post method
    if ($class->hasMethod('_after_detail')) {
        $beforeMethod = $class->getMethod('_after_detail');
        if ($beforeMethod->isPublic()) {
            $beforeMethod->invoke($instance);
        }
    }
}

// Execute method with parameters
$method = new ReflectionMethod('BlogAction', 'test');
$params = $method->getParameters();
foreach ($params as $param) {
    $paramName = $param->getName();
    if (isset($_REQUEST[$paramName])) {
        $args[] = $_REQUEST[$paramName];
    } elseif ($param->isDefaultValueAvailable()) {
        $args[] = $param->getDefaultValue();
    }
}

if (count($args) == $method->getNumberOfParameters()) {
    $method->invokeArgs($instance, $args);
} else {
    echo 'parameters is wrong!';
}

Other reference

/**
 * Execute App controller
 */
public function execApp() {

    // Create action controller instance
    $className = MODULE_NAME . 'Controller';
    $namespaceClassName = '\\apps\\' . APP_NAME . '\\controller\\' . $className;
    load_class($namespaceClassName, false);

    if (!class_exists($namespaceClassName)) {
        throw new \Exception('Oops! Module not found : ' . $namespaceClassName);
    }

    $controller = new $namespaceClassName();

    // Get current operation name
    $action = ACTION_NAME;

    // Perform the current operation
    //Call "user" func (array (& $controller, $action)); / / actually, this function is enough!!!
    try {
        $methodInfo = new \ReflectionMethod($namespaceClassName, $action);
        if ($methodInfo->isPublic() && !$methodInfo->isStatic()) {
            $methodInfo->invoke($controller);
        } else { // Operation method is not of public type, exception thrown
            throw new \ReflectionException();
        }
    } catch (\ReflectionException $e) {
        // After an exception occurs in a method call, boot to the call method for handling
        $methodInfo = new \ReflectionMethod($namespaceClassName, '__call');
        $methodInfo->invokeArgs($controller, array($action, ''));
    }
    return;
}

Keywords: PHP Attribute

Added by myfafa on Mon, 18 Nov 2019 20:35:57 +0200