Practical Application Example of PHP Reflection

  • 2021-12-05 05:42:15
  • OfStack

This paper describes the practical application of PHP reflection with examples. Share it for your reference, as follows:

1. Automatically generate documents

Documentation can be automatically generated based on reflection analysis of the internal structure of classes, interfaces, functions, and methods, the parameters of methods and functions, and the properties and methods of classes.


<?php
class Student
{
  const NORMAL = 1;
  const FORBIDDEN = 2;
  /**
   *  Users ID
   * @var  Type 
   */
  public $id;
  /**
   *  Get id
   * @return int
   */
  public function getId()
  {
    return $this->id;
  }
  public function setId($id = 1)
  {
    $this->id = $id;
  }
}
$ref = new ReflectionClass('Student');
$doc = $ref->getDocComment();
echo $ref->getName() . ':' . getComment($ref) , "<br/>";
echo " Attribute list: <br/>";
printf("%-15s%-10s%-40s<br/>", 'Name', 'Access', 'Comment');
$attr = $ref->getProperties();
foreach ($attr as $row) {
  printf("%-15s%-10s%-40s<br/>", $row->getName(), getAccess($row), getComment($row));
}
echo " Constant list: <br/>";
printf("%-15s%-10s<br/>", 'Name', 'Value');
$const = $ref->getConstants();
foreach ($const as $key => $val) {
  printf("%-15s%-10s<br/>", $key, $val);
}
echo "<br/><br/>";
echo " Method list <br/>";
printf("%-15s%-10s%-30s%-40s<br/>", 'Name', 'Access', 'Params', 'Comment');
$methods = $ref->getMethods();
foreach ($methods as $row) {
  printf("%-15s%-10s%-30s%-40s<br/>", $row->getName(), getAccess($row), getParams($row), getComment($row));
}
//  Getting permission 
function getAccess($method)
{
  if ($method->isPublic()) {
    return 'Public';
  }
  if ($method->isProtected()) {
    return 'Protected';
  }
  if ($method->isPrivate()) {
    return 'Private';
  }
}
//  Get method parameter information 
function getParams($method)
{
  $str = '';
  $parameters = $method->getParameters();
  foreach ($parameters as $row) {
    $str .= $row->getName() . ',';
    if ($row->isDefaultValueAvailable()) {
      $str .= "Default: {$row->getDefaultValue()}";
    }
  }
  return $str ? $str : '';
}
//  Get comments 
function getComment($var)
{
  $comment = $var->getDocComment();
  //  Simply get the first 1 Row information, which can be extended by itself here 
  preg_match('/\* (.*) *?/', $comment, $res);
  return isset($res[1]) ? $res[1] : '';
}

Output:

Student:
Attribute list:
Name Access Comment
id Public User ID
Constant list:
Name Value
NORMAL 1
FORBIDDEN 2
Method list
Name Access Params Comment
getId Public Get id
setId Public id,Default: 1

2. Implement the MVC architecture

Many frameworks are now MVC architectures, which locate the names of controllers ($controller) and methods ($method) based on routing information, and then use reflection to realize automatic invocation.


$class = new ReflectionClass(ucfirst($controller) . 'Controller');
$controller = $class->newInstance();
if ($class->hasMethod($method)) {
  $method = $class->getMethod($method);
  $method->invokeArgs($controller, $arguments);
} else {
  throw new Exception("{$controller} controller method {$method} not exists!");
}

3. Implement unit tests

In general, we will test functions and classes to determine whether they can return results as we expect. We can use reflection to implement a simple and general class test case.


<?php
class Calc
{
  public function plus($a, $b)
  {
    return $a + $b;
  }
  public function minus($a, $b)
  {
    return $a - $b;
  }
}
function testEqual($method, $assert, $data)
{
  $arr = explode('@', $method);
  $class = $arr[0];
  $method = $arr[1];
  $ref = new ReflectionClass($class);
  if ($ref->hasMethod($method)) {
    $method = $ref->getMethod($method);
    $res = $method->invokeArgs(new $class, $data);
    if($res === $assert){
      echo " The test result is correct ";
    };
  }
}
testEqual('Calc@plus', 3, [1, 2]);
echo "<br/>";
testEqual('Calc@minus', -1, [1, 2]);

This is a test method for classes, and it is also possible to use reflection to test functions.


<?php
function title($title, $name)
{
  return sprintf("%s. %s\r\n", $title, $name);
}
$function = new ReflectionFunction('title');
echo $function->invokeArgs(array('Dr', 'Phil'));
?>

Here is just a test case I wrote briefly. The PHPUnit unit test framework relies on the characteristics of Reflection to a great extent, so you can understand it.

4. Work with the DI container to resolve dependencies

Many frameworks, such as Laravel, use Reflection to solve dependency injection problems. You can see the source code of Laravel for analysis.

The following code simply implements an DI container to demonstrate that Reflection solves dependency injection problems.


<?php
class DI
{
  protected static $data = [];
  public function __set($k, $v)
  {
    self::$data[$k] = $v;
  }
  public function __get($k)
  {
    return $this->bulid(self::$data[$k]);
  }
  //  Get an instance 
  public function bulid($className)
  {
    //  If it is an anonymous function, execute it directly and return the result 
    if ($className instanceof Closure) {
      return $className($this);
    }
    //  If it is already an instantiated object, return it directly 
    if(is_object($className)) {
      return $className;
    }
    //  If it is a class, use reflection to load 
    $ref = new ReflectionClass($className);
    //  Monitor whether the class can be instantiated 
    if (!$ref->isInstantiable()) {
      throw new Exception('class' . $className . ' not find');
    }
    //  Get the constructor 
    $construtor = $ref->getConstructor();
    //  No constructor, direct instantiation returns 
    if (is_null($construtor)) {
      return new $className;
    }
    //  Get constructor parameters 
    $params = $construtor->getParameters();
    //  Analytic constructor 
    $dependencies = $this->getDependecies($params);
    //  Create a new instance 
    return $ref->newInstanceArgs($dependencies);
  }
  //  Parse the parameter, and recursively instantiate the dependent class if it appears in the parameter 
  public function getDependecies($params)
  {
    $data = [];
    foreach($params as $param)
    {
      $tmp = $param->getClass();
      if (is_null($tmp)) {
        $data[] = $this->setDefault($param);
      } else {
        $data[] = $this->bulid($tmp->name);
      }
    }
    return $data;
  }
  //  Set default values 
  public function setDefault($param)
  {
    if ($param->isDefaultValueAvailable()) {
      return $param->getDefaultValue();
    }
    throw new Exception('no default value!');
  }
}
class Demo
{
  public function __construct(Calc $calc)
  {
    echo $calc->plus(1, 2);
  }
}
class Calc
{
  public function plus($a, $b)
  {
    return $a + $b;
  }
  public function minus($a, $b)
  {
    return $a - $b;
  }
}
$di = new DI();
$di->calc = 'Calc';
$di->demo = 'Demo';
$di->demo;// The output is 3

For more readers interested in PHP related content, please check the topics on this site: "Introduction to php Object-Oriented Programming", "Encyclopedia of PHP Array (Array) Operation Skills", "Introduction to PHP Basic Grammar", "Summary of PHP Operation and Operator Usage", "Summary of php String (string) Usage", "Introduction to php+mysql Database Operation Skills" and "Summary of php Common Database Operation Skills"

I hope this article is helpful to everyone's PHP programming.


Related articles: