解析器与反射API的集成

2025-12-31 15:41:28

1、  首先,将之前清单中的文档块解析器与反射API集成在一起。通过扩展核心的Reflecticn

类并且将解析器作为静态方法添加进去,就可以做到这一点。

    代码显示了这一扩展。将这段代码和所有随后的反射类都放到一个名为

DocumentingReflection.php的公共的包含文件中。

集成文档注释解析器(DocumentingReflection.php)

     Class DocumentingReflection extends Reflection{

   Public static function ParseDocComment($docComment){

  $returnData =$comments = $tags =$array();

  $tagNames = $tagData = array();

 $tokens =docblock_tokenize($docComment,true);

 Foreach($tokens as $token){

   Switch($token[0]){

Case DOCBLOCK_TEXT:

  If(!isset($tagId)){

   $comments[] = $token[1];

}else{

  If(array_key_exists($tagId,$tagData)){

  $tagData[$tagId] .=’’.trim($token[1]);

}else{

  $tagData[$tagId] = trim($token[1]);

}

}

Break;

Case DOCBLOCK_TAG:

 $tagId = uniquid();

 $tagNames[$tagId] = trim($token[1],’@’);

Break;

}

}

Foreach($tagData as $tagId =>$data){

  $tagName = $tagNames[$tagId];

  If(array_key_exists($tagName,$tags)){

  If(!is_array($tags[$tagName])){

         $backupData = $tags[$tagName];

$tags[$tagName] = array();

$tags[$tagName] = $backupData;

}

 $tag[$tagName] = $data;

}else{

  $tags[$tagName] = $data;

}

}

$returnData[‘comments’] = $comments;

$returnData[‘tags’] = $tags;

$returnData[‘tokens’]=$tokens;

Return $returnData;

}

}

2、现在已经定义了静态函数ParseDocComment.这正是添加附加的处理逻辑功能的地方,例如处理行内标签的功能。但是在实现这些功能之前,需要扩展另外一些反射类。

扩展反射类

下一个要扩展的类是ReflectionMethod类。这是因为它是最全面的反射实现类,它仍然包含了getDocComment()方法。还需要扩展ReflectionParameter类,但是需要从相关联的方法中获取数据,然后这个方法类的扩展才能实现。下面代码,创建了DocumentingReflectionMethod类,将reflectionMethod类与ParseDocComment类集成。

创建DocumentingReflectionMethod类(DocumentingReflection.php)

Class DocmentingReflectionMethod extends ReflectionMethod{

         Protected $_comments,$_tags,$_tokens,$_declaringClass;

  Public function __construct($object,$method){

  Parent::__construct($object,$method);

  $docComment = $this->getDocComment();

 $this->_declaringClass = $object;

 $parsedComment = DocumentingReflection::ParseDocComment($docComment);

$this->_comments = $parsedComment[‘comments’];

$this->_tags=$parsedComment[‘tags’];

$this->_tokens=$parsedComment[‘tokens’];

}

Public function printDocTokens(){

  Foreach($this->_tokens as $token){

  Echo $token[0].”=”;

  Echo docblock_token_name($token[1]).’=’;

  Print_r($token[1]);

  Echo “\n”;

}

}

Public function getParseTags(){

 Return $this->_tags;

}

Public function getParsedComments(){

 Return $this->_comments;

}

}

DocumentingReflectionMethod类从ReflectionMethod类继承,从而继承了ReflectionMethod类的所有功能。然后通过调用parent::__construct函数,对基数进行初始化。这个父类的构造过程必须在重写的构造器的第一行上完成,否则,可能出现错误,而且,甚至可能出现没有任何错误解释就崩溃的代码。

3、在基类实例初始化完成之后,文档扩展开始起作用。这个类调用了之前定义的用来解析文档注释的静态方法,并传入了它自身的文档注释,而且还将结果保存到几个受保护的成员变量中。最后,还在这个类中添加了几个访问器方法,一遍查看运行是否正常。

有了DocumentReflection.php文件中代码。创建另外文件test.php

例子测试DocumentingReflection类(test.php)

Require_once(‘DocumentingReflection.php’);

 Class demo{

 /*

  *这个方法是用于演示功能的。

  *它引入了一个参数并且返回这个参数

  *@param mixed $param1  一个返回的变量。

  *$returns mixed 返回输入的变量

*/

Public function demoMethod($param1){

 Return $param1;

}

}

$reflector = new DocumentingReflectionMethod(‘demo’,’demoMethod’);

$reflector->printDocTokens();

Print_r($reflector->getParsedTags());

Print_r($reflector->getParsedComments());

运行结果如下:

1=DOCBLOCK_NEWLINE=

1=DOCBLOCK_NEWLINE=

2=DOCBLOCK_WHITESPACE=

36=DOCBLOCK_TEXT=This method is for demonstration purposes.

1=DOCBLOCK_NEWLINE=

1=DOCBLOCK_NEWLINE=

2=DOCBLOCK_WHITESPACE=

36=DOCBLOCK_TEXT=It takes a single parameter and returns it.

1=DOCBLOCK_NEWLINE=

1=DOCBLOCK_NEWLINE=

2=DOCBLOCK_WHITESPACE=

5=DOCBLOCK_TAG=@param

36=DOCBLOCK_TEXT=mixed $param1 A variable to return.

1=DOCBLOCK_NEWLINE=

2=DOCBLOCK_WHITESPACE=

5=DOCBLOCK_TAG=@returns

36=DOCBLOCK_TEXT=mixed The input variable is returned.

1=DOCBLOCK_NEWLINE=

结果:

Array(

 [param]=>mixed $param1 A variable to return.

 [returns]=>mixed  The input variable is returned

)

Array(

  [0]=>This method is for demonstration purposes

  [1]=>It takes a single parameter and returns it.

)

下一步,需要为ReflectionParameter创建一个扩展类.如果没有与参数相关联的文档注

 释,就需要从相关联方法的文档注释的param标签中获得参数数据。

4、要做到这一点,必须自定义ReflectionParameter以便从方法中获取数据,代码所示。

 Public void ReflectionParameter::__construct(mixed function,mixed parameter);

    值得注意的是,函数参数是混合类型的. 这个参数可能会被传入一个数据数组,其中包含了类名字符串或者对象实例以及方法名字符串。

ReflectionParameter的代码。

Class DocumentingReflectionParameter extends ReflectionParameter{

  Protected $_reflectionMethod,$_reflectionClass,$_comment,$_type;

 Public function __construct($method,$parameter){

  Parent::__construct($method,$parameter);

  $this->_comment=’’;

  $this->_type =’undefined’;

  $this->_reflectionMethod=new DocumentingflectionMethod($method[0],$method[1]);

  $tags = $this->_reflectionMethod->getParsedTags();

  If(array_key_exists(‘param’,$tags)){

   $params = $tags[‘param’];

  If(is_array($params)){

   Foreach($params as $param){

     If($this->_isParamTag($this->getName(),$param)){

    $paramFound = $param;

}

}

}else{

 If($this->_isParamTag($this->getName(),$params)){

  $paramFound = $params;

}

}

 If(isset($paramFound)){

   $tokens = preg_split(‘/[\s\t]+/’,$paramFound,3);

   $this->_comment = $cokens[2];

   $this->_type = $tokens[0];

}

}

}

Public function getDeclaringFunction(){

  Return $this->_reflectionMethod;

}

Public function getComment(){

 Return $this->_comment;

}

Public function getType(){

 Return $this->_type;

}

Private function _isParamTag($paramName,$paramData){

  $paramSplit = preg_split(‘/[\s\t]+/’,$paramData,3);

  $explodedName = trim($paramSplit[1],’$,.’);

 If($explodedName == $paramName){

  Return ture;

}else{

  Return false;

}

}

}

    这个类比之前的类要复杂得多。这个类大多数的注释解析过程是在构造函数中实现的。

    首先,需要构造这个类井调用父类的方法。如果没有相关联的文档注释,还需要给成员变量赋默认值。然后便可以开始处理过程了。

5、  使用传递给构造函数的信息,对DocumentingReflectionMethod对象进行实例化。这个类使用getParsedTags()方法向你提供访问文档注释信息的功能。下一步,这段代码在$tags数组中查找是否存在’param’标签。如果存在,首先确定这个标签项是数组还是.单个的字符串值,从而做出相应的处理。

    在这一过程中,这段代码还调用了私有成员函数_isParamTag()以确定参数是否是标签描

述的那个,将paran标签分割为三部分,这个函数就可以确定这一点。分割是基于将字符串分解为标识符的一个正则表达式来完成的,用tab字符或者空格字符来对标识符进行分解。函数的第三个参数表示字符串只会被分割三次,这会形成一个拥有类型、变量名称和注释的数组。

    然后,这段代码会检查变量名称项以确定它是否与ReflectionParameter类自身的名称相

匹配.如果匹配。当前被检测的标签就属f那个参数。

    一旦找到正确的标签,数据就会被分割开,并保存到受保护的成员变量_comment和_type中。之后,通过get函数访问这些数据。

    现在,可以对这个类进行试用.如代码所示,

实验DocumentingReflection的用法(Experiment.php)

Require_once(‘DocumentingReflection.php’);

Class demo{

  /*

  *@param string $param 这是一个注释

*/

Public function demoMethod($param=’test’){}

}

$refparam = new DocumentingReflectionParameter(array(

   ‘demo’,’demoMethod’),

    ‘param’

);

Var_dump($refparam->getComment());

Var_dump($refparam->getType());

运行会输出一下结果

String(19)” this is the comment”

String(6) “string”

通常你不会希望提供太多信息去访问参数。我们来修改一下DocumentingReflectionMethod类,重写getParameters()函数,使它返回DocumentingReflectionParameter[],而不是ReflectionParameter[].在DocumentingReflectionMethod类中包含代码

Public function getParameters(){

  $parameters =array();

  If(is_object($this->_declaringClass)){

    $class = get_class($this->_declaringClass);

}else if(is_string($this->_declaringClass)){

  $class = $this->_declaringClass;

}

Foreach(parent::getParameters() as $parameter){

   $parameters[]= new DocumentingReflectionParameter(

      Array($class,$this->getName()),

         $parameter->getName()

);

}

Return $parameters;

}

这一方法首先检查在构造时保存的声明类,看它是对象还是字符串。因为在下一步操作中需要一个字符串,所以需要使用get_class()函数来检查对象的类型。

   接着,需要调用父类的getParameters()方法。这个函数会返回ReflectionParameter对象的一个数组,而不是DocumentingReflectionParameter对象。这个函数的全部用途是用来挑用扩展的文档形式,而不是内置的形式。

  为了测试重写的getParameters()函数,可以写如下代码

Require_once(‘DocumentingReflection.php’);

Class demo{

 /*

* @param mixed $param1 第一个注释

*$param string $param2  第二个注释

*/

Public function demoMethod($param1,$param2){}

}

$reflector = new DocumentingReflectionMethod(‘demo’,’demoMethod’);

Foreach($reflector->getParameters() as $param){

      Echo $param->getName().””;

      Echo $param->getType().””;

      Echo $param->getComment();

      Echo “\n”;

}

运行结果:

Param1 mixed The first comment

Param2 string The second comment

6、现在你已经完成了扩展的方法和参数。那么如何完成类的扩展?DocumentingReflectionClass正是需要创建的下一个类。

//创建DocumentingReflectionClass类(DocumentingReflection.php)

Class DocumentingReflectionClass extends ReflectionClass{

Protected $_Comments,$_tags,$_tokens;

Public function __construct($class){

  Parent::__construct($class);

  $docComment = $this->getDocComment();

  $parsedComment = DocumentingReflection::ParseDocComment($docComment);

  

  $this->_comments = $parseComment[‘comments’];

  $this->_tags = $parsedComment[‘tags’];

   $this->_tokens = $parsedComment[‘tokens’];

}

Public function getMethods(){

  $methods =array();

Foreach(parent::getMethods() as $method){

  $methods[] = new DocumentingReflectionMethod(

   $this->getName90,$method->getName()

);

}

Return $methods;

}

Public function printDocTokens(){

 Foreach($this->_tokens as $token){

  Echo $token[0] .’=’;

  Echo docblock_token_name[$token[0]].’=’;

  Print_r($token[1]);

  Echo ‘\n’;

}

}

Public function getParseTags(){

 Return $this->_tags;

}

Public function getParseComments(){

 Return $this->_comments;

}

}

声明:本网站引用、摘录或转载内容仅供网站访问者交流或参考,不代表本站立场,如存在版权或非法内容,请联系站长删除,联系邮箱:site.kefu@qq.com。
相关推荐
  • 阅读量:69
  • 阅读量:185
  • 阅读量:78
  • 阅读量:111
  • 阅读量:51
  • 猜你喜欢