Historical review

I marveled at the integrity of Laravel’s ecosystem and the absence of tp5 class apis (previously, thinkPHP3 helped the company generate a 3.1 API using Apigen NetBeans).

After I left, no one would be using NetBeans so no one would be upgrading.

Then I thought Laravel’s quick list was good, as shown below:

So I thought of porting it to TP5 as my first contribution back to the development of ThinkPHP5.

Remember that time is before qingming began to begin. Use the most primitive, static pages, with each class edited manually.

I found there were so many classes that I opened one class at a time in Sublime, copied the temporary file, processed it, and pasted it into static index.html. It was too much work, dealing with the navigation on the left, and locating the insertion location. So in the May holiday WHEN I changed a way of thinking, I do not do static, I do dynamic.

So they built a plugin into the dolphin,

Add as chapters. Finally the efficiency went up, May Day time took time to complete the content.

Posted to the Nuggets and got 17 likes.

A quick guide to attack

I thought I was done. The bosses were too motivated to release 5.0.10 on July 4th and then work on 5.1dev.

Well, since the boss put my spreadsheet up on the tp website. I also can’t let users wait to see the old version 5.0.7,

But I don’t want to manually compare which classes were changed and which methods were added or removed. Over 20 chapters.

I thought “code problems should be solved by code”. PHP annotations and reflection classes come to mind.

It happened that the project used a CRada/PHP-APidoc API document generation tool.

So I started to implement the project of getting information through class reflection.

Reflected data

PHP 5 has a full reflection API, adding the ability to reverse-engineer classes, interfaces, functions, methods, and extensions. In addition, the reflection API provides methods to retrieve documentation comments in functions, classes, and methods.

There are several types of reflections mentioned in the manual on the official website:

Reflection

ReflectionClass

ReflectionZendExtension

ReflectionExtension

ReflectionFunction

ReflectionFunctionAbstract

ReflectionMethod

ReflectionObject

ReflectionParameter

ReflectionProperty

ReflectionType

ReflectionGenerator

Reflector

ReflectionException

Implementation approach

// TODO iterates the class namespace data to be processed // TODO iterates the class reflection // TODO iterates the class information (name, method list)// TODO iterates the method list to get the method type and commentCopy the code

I read that reflection is to reflect the class and then find the method and then find the method’s parameters

Class for

Tp5’s core classes are all in one directory

So I thought of glob traversal

 public function get_core_class(){
    $class_path = CORE_PATH;
    $before_cwd = getcwd();
    chdir($class_path);
    $names = glob('*.php');
    $ret = [];    foreach ($names as $key => $name) {
      $ret[] = 'think\\'. str_ireplace('.php', '', $name);
    }
    chdir($before_cwd);   return $ret;
  }Copy the code

The initialization of a reflection class passes the actual namespace path of the class.

$class= new\ReflectionClass($className); Like this. Then there are the following methods:

ReflectionClass: : __construct – initializes the ReflectionClass class ReflectionClass: : export – a derived class ReflectionClass: : getConstant – For defined a constant ReflectionClass: : ReflectionClass getConstants – to obtain a set of constants: : getConstructor – access to the constructor of a class ReflectionClass: : getDefaultProperties – get default attributes ReflectionClass: : getDocComment – get the document comment ReflectionClass: : getEndLine – For the last line of the number of rows ReflectionClass: : getExtension – according to the defined ReflectionClass class takes place extended ReflectionExtension object: : getExtensionName – For the expansion of the classes defined in the name of the ReflectionClass: : filename ReflectionClass getFileName – access to define the class: : getInterfaceNames – access interface (interface) name ReflectionClass: : getInterfaces – access interface ReflectionClass: : getMethod ReflectionMethod – get a class method. ReflectionClass ReflectionClass: : getMethods – acquisition method of array: : getModifiers – get the modifier ReflectionClass class: : getName – get the name of the class ReflectionClass: : getNamespaceName – for namespace name ReflectionClass: : getParentClass – get the superclass ReflectionClass: : getProperties – Get a set of attributes ReflectionClass: : getProperty – get a property of a class of ReflectionProperty ReflectionClass: : getShortName – for short name ReflectionClass: : getStartLine – get starting line number ReflectionClass: : getStaticProperties – get static properties (static) ReflectionClass: : getStaticPropertyValue – access to static (static) the value of the attribute ReflectionClass: : getTraitAliases – returns an array of trait alias ReflectionClass: : getTraitNames – returns the name of the traits of this class are using array ReflectionClass: : getTraits – returns the traits of the class by using an array ReflectionClass: : hasConstant – check to see if the constants has been defined ReflectionClass: : hasMethod – check methods defined ReflectionClass: : hasProperty – Check whether the attribute is defined ReflectionClass: : implementsInterface – the implementation of the interface ReflectionClass: : inNamespace – check whether in the namespace ReflectionClass: : isAbstract – check class is an abstract class (abstract) ReflectionClass: : isAnonymous – check whether class is an anonymous class ReflectionClass: : isCloneable – returns a class whether can copy the ReflectionClass: : isFinal – check whether the class is declared as final ReflectionClass: : isInstance – check the instance of the class ReflectionClass: : isInstantiable – check to see if the class can be instantiated ReflectionClass: : isInterface – check whether a class (interface) ReflectionClass: : isInternal – check whether the class is defined by the extension or core in internal ReflectionClass: : isIterateable – check whether can be iterative (iterateable) ReflectionClass: : isSubclassOf – check for a subclass ReflectionClass: : isTrait – returns whether it is a trait ReflectionClass: : isUserDefined – Check whether the ReflectionClass defined by the user: : newInstance – from the specified parameters to create a new class instance ReflectionClass: : newInstanceArgs – from the given parameters to create a new class instance. ReflectionClass: : newInstanceWithoutConstructor – create a new class instance and not call its constructor ReflectionClass: : setStaticPropertyValue – set the value of static properties ReflectionClass::__toString – Returns the string representation of the ReflectionClass object.

$class->getShortName(); $class->getShortName()

I mainly need to get the class shorthand name and method.

public function generate($classNames){ config('default_return_type', 'json'); $outputs = []; $outputs = []; $outputs = []; foreach ($classNames as $k => $className) { $class= new\ReflectionClass($className); $key = $class->getShortName(); // dump($key); $outputs[$key] = $this->getClassAnnotation($class); } return $outputs; }Copy the code

The getClassAnonation method is what I use to get information about all the methods of the class.

Methods the related

Once you get the reflection class, you need to instantiate the ReflectionMethod class to get the ReflectionMethod.

Public function getClassAnnotation($class){$ret = ['hasPublicMethods'=>0,]; $ret['name'] = $class->getName(); $methods = $class->getMethods(); foreach ($methods as $key => $method) { $class = $method->class; $method_name = $method->name; $rm = new \ReflectionMethod($class, $method_name); / / ignore structure and destructor if ($rm - > isConstructor () | | $rm - > isDestructor ()) {continue; } $foo = []; $foo['docComment'] = $rm->getDocComment(); $foo['docComment_formated'] = $this->parseMethodDoc($foo['docComment']); $foo['args'] = $rm->getParameters(); $foo['args_formated'] = $this->parseParameters($class, $method_name, $foo['args']); if($rm->isPublic()){ $type = $rm->isStatic()? 'public_static' : 'public_public'; }else{ $type = $rm->isStatic()? 'private_static' : 'private_public'; } // Update only when hasPublicMethods is 0, If ($ret['hasPublicMethods']) {$ret['hasPublicMethods'] = stripos($type, '_public')! == false; } $foo['type'] = $type; $ret['methods'][$method_name] = $foo; } return $ret; // $className = 'think\\App'; // $class = new \ReflectionClass($className); // config('default_return_type', 'json'); // return $class->name; // return $class->__toString(); Return \ReflectionClass::export($className, 1); Return json_encode($class->getConstants()); Return $class->getConstructor(); Var_dump ($class->inNamespace()); // var_dump($class->getName()); // var_dump($class->getNamespaceName()); // var_dump($class->getShortName()); $class->getDefaultProperties(); $class->getDefaultProperties(); // return $class->getProperties(\ReflectionProperty::IS_PUBLIC | \ReflectionProperty::IS_PROTECTED); // const integer IS_STATIC = 1 ; // const integer IS_PUBLIC = 256 ; // const integer IS_PROTECTED = 512 ; // const integer IS_PRIVATE = 1024 ; // return $class->getStaticProperties(); Return $class->getDocComment(); }Copy the code

Get all class methods first:

$class->getMethods();

Examples from the official website:

array(3) {
  [0]=>
  &object(ReflectionMethod)#2 (2) {
    ["name"]=>    string(11) "firstMethod"
    ["class"]=>    string(5) "Apple"
  }
  [1]=>
  &object(ReflectionMethod)#3 (2) {
    ["name"]=>    string(12) "secondMethod"
    ["class"]=>    string(5) "Apple"
  }
  [2]=>
  &object(ReflectionMethod)#4 (2) {
    ["name"]=>    string(11) "thirdMethod"
    ["class"]=>    string(5) "Apple"
  }
}Copy the code

You can also pass the attribute type to filter when getting:

<? phpclass Apple { public function firstMethod() { } final protected function secondMethod() { } private static function thirdMethod() { } } $class = new ReflectionClass('Apple'); $methods = $class->getMethods(ReflectionMethod::IS_STATIC | ReflectionMethod::IS_FINAL); var_dump($methods); ? >Copy the code

Once we get the method, we need to get the public, private, static, and other properties of the class’s methods.

Because I made a distinction between different types of methods when I showed them.

$rm = new \ReflectionMethod($class, $method_name); / / ignore structure and destructor if ($rm - > isConstructor () | | $rm - > isDestructor ()) {continue; }Copy the code

First filter out the construction and destructor methods.

$foo = []; $foo['docComment'] = $rm->getDocComment(); $foo['docComment_formated'] = $this->parseMethodDoc($foo['docComment']); $foo['args'] = $rm->getParameters(); $foo['args_formated'] = $this->parseParameters($class, $method_name, $foo['args']);Copy the code

I took the documentation information and parameter information of the original method and formatted it as I wanted.

When you get parameters, note that you return an array of parameters

Official example:

<? phppublic static function fire_theme_method($class, $method){ $fire_args=array(); $reflection = new ReflectionMethod($class, $method); foreach($reflection->getParameters() AS $arg) { if($_REQUEST[$arg->name]) $fire_args[$arg->name]=$_REQUEST[$arg->name]; else $fire_args[$arg->name]=null; } return call_user_func_array(array($class, $method), $fire_args); }? >Copy the code

We get the parameter name and instantiate ReflectionParameter with methodName to get the parameter information

Class parameter information

public function parseParameters($class, $method, $args){ if($args){ $args_str = []; foreach ($args as $key => $arg) { $p = new \ReflectionParameter(array($class, $method), $key); / / determine whether reference parameters the if ($p - > isPassedByReference ()) {$arg_str_new = "& \ $". $p - > getName (); }else{ $arg_str_new = "\$".$p->getName(); } if ($p->isOptional() && $p->isDefaultValueAvailable()) { $a_clsss = $class; $def = $p->getDefaultValue(); $def = $p->getDefaultValue(); $arg_str_new .= is_array($defaul) ? ' = '. '[]': ' = '. var_export($defaul, 1); }catch(\Exception $e){ trace($p->isVariadic()); trace($a_clsss.'/'.$method.'_'.$key); } } $args_str[] = $arg_str_new; } return implode(', ', $args_str); } return ''; }Copy the code

Some methods return null with no arguments, some with arguments, we want to concatenate the argument string like

<? phpclass A{ function foo($a){ } }Copy the code

PHP will not return foo($a). You have to deal with it yourself.

Check whether a parameter has a default value and is referenced

  • getName

  • isOptional

  • isDefaultValueAvailable

  • getDefaultValue

When I get the default value parameter, I find that sometimes exceptions are thrown, and they are always reported for inner classes.

So make sure you try and catch to get the default.

At this point, the whole function of obtaining the method and method annotation and parameter information of some classes is completely realized.

If you want to get a list of classes you just need to change the return value of get_CORE_class, as long as it’s a class that tp5 automatically loads. It’s all resolvable.

Overall idea, according to the class array to obtain method -> method document, obtain parameters -> obtain parameter information

In this way, I can feel relieved to be lazy. Next time TP upgrade, I will directly composer update, refresh the home page, and get static HTML update to my GH-Pages branch. This completes the update of the new framework sketch sheet.

Once and for all.

The end of the

Specific source code reference: github.com/yangweijie/…

See the quick table directly: yangweijie. Making. IO/thinkphp – lt…

Welcome to give me your advice.