首先,将之前清单中的文档块解析器与反射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;}}
现在已经定义了静态函数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函数,对基数进行初始化。这个父类的构造过程必须在重写的构造器的第一行上完成,否则,可能出现错误,而且,甚至可能出现没有任何错误解释就崩溃的代码。
在基类实例初始化完成之后,文档扩展开始起作用。这个类调用了之前定义的用来解析文档注释的静态方法,并传入了它自身的文档注释,而且还将结果保存到几个受保护的成员变量中。最后,还在这个类中添加了几个访问器方法,一遍查看运行是否正常。有了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=@param36=DOCBLOCK_TEXT=mixed $param1 A variable to return.1=DOCBLOCK_NEWLINE= 2=DOCBLOCK_WHITESPACE=5=DOCBLOCK_TAG=@returns36=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标签中获得参数数据。
要做到这一点,必须自定义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;}}} 这个类比之前的类要复杂得多。这个类大多数的注释解析过程是在构造函数中实现的。 首先,需要构造这个类井调用父类的方法。如果没有相关联的文档注释,还需要给成员变量赋默认值。然后便可以开始处理过程了。
使用传递给构造函数的信息,对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 commentParam2 string The second comment
现在你已经完成了扩展的方法和参数。那么如何完成类的扩展?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;} }