将 PHP 文档注释解析为数据结构

Posted

技术标签:

【中文标题】将 PHP 文档注释解析为数据结构【英文标题】:Parsing PHP Doc Comments into a Data Structure 【发布时间】:2011-06-09 19:07:03 【问题描述】:

我在 php 中使用反射 API 从方法中提取 DocComment (PHPDoc) 字符串

$r = new ReflectionMethod($object);
$comment = $r->getDocComment();

这将返回一个看起来像这样的字符串(取决于方法的记录情况)

/**
* Does this great things
*
* @param string $thing
* @return Some_Great_Thing
*/

是否有可以将 PHP Doc Comment String 解析为数据结构的内置方法或函数?

$object = some_magic_function_or_method($comment_string);

echo 'Returns a: ', $object->return;

缺少这个,我应该自己查看PHPDoc source code 的哪个部分。

缺少和/或除此之外,是否有第三方代码被认为比 PHPDoc 代码“更好”?

我意识到解析这些字符串不是火箭科学,甚至不是计算机科学,但我更喜欢一个经过良好测试的库/例程/方法,该库/例程/方法旨在处理许多 janky、半不正确的 PHP可能存在于野外的文档代码。

【问题讨论】:

如果您尝试读取@标签及其值,那么使用 preg_match 将是最好的解决方案。 【参考方案1】:

我很惊讶这还没有被提及:使用 Zend Framework 的 Zend_Reflection 怎么样?这可能会派上用场,尤其是当您使用基于 Zend 框架(如 Magento)构建的软件时。

查看Zend Framework Manual 获取一些代码示例,查看API Documentation 获取可用方法。

有不同的方法可以做到这一点:

将文件名传递给 Zend_Reflection_File。 将对象传递给 Zend_Reflection_Class。 将对象和方法名传递给 Zend_Reflection_Method。 如果您真的只有注释字符串,您甚至可以将一个小的虚拟类的代码组合在一起,将其保存到一个临时文件并将该文件传递给 Zend_Reflection_File。

让我们来看一个简单的案例,假设您有一个想要检查的现有类。

代码是这样的(未经测试,请见谅):

$method = new Zend_Reflection_Method($class, 'yourMethod');
$docblock = $method->getDocBlock();

if ($docBlock->hasTag('return')) 
    $tagReturn = $docBlock->getTag('return'); // $tagReturn is an instance of Zend_Reflection_Docblock_Tag_Return
    echo "Returns a: " . $tagReturn->getType() . "<br>";
    echo "Comment for return type: " . $tagReturn->getDescription();

【讨论】:

这对特征有效吗?就像一个类使用特征一样,它会选择正确的文档块吗? 说实话:我不确定。据我所知,ZF1 的 Zend_Reflection 是在 PHP 5.4 和特性尚未发布时编写的。【参考方案2】:

您可以使用来自Fabien PotencierSami(“又一个 PHP API 文档生成器”)开源项目的“DocBlockParser”类。 首先,从GitHub获取萨米。 这是一个如何使用它的示例:

<?php

require_once 'Sami/Parser/DocBlockParser.php';
require_once 'Sami/Parser/Node/DocBlockNode.php';

class TestClass 
    /**
     * This is the short description.
     *  
     * This is the 1st line of the long description 
     * This is the 2nd line of the long description 
     * This is the 3rd line of the long description   
     *  
     * @param bool|string $foo sometimes a boolean, sometimes a string (or, could have just used "mixed")
     * @param bool|int $bar sometimes a boolean, sometimes an int (again, could have just used "mixed") 
     * @return string de-html_entitied string (no entities at all)
     */
    public function another_test($foo, $bar) 
        return strtr($foo,array_flip(get_html_translation_table(HTML_ENTITIES)));
    


use Sami\Parser\DocBlockParser;
use Sami\Parser\Node\DocBlockNode;

try 
    $method = new ReflectionMethod('TestClass', 'another_test');
    $comment = $method->getDocComment();
    if ($comment !== FALSE) 
        $dbp = new DocBlockParser();
        $doc = $dbp->parse($comment);
        echo "\n** getDesc:\n";
        print_r($doc->getDesc());
        echo "\n** getTags:\n";
        print_r($doc->getTags());
        echo "\n** getTag('param'):\n";
        print_r($doc->getTag('param'));
        echo "\n** getErrors:\n";
        print_r($doc->getErrors());
        echo "\n** getOtherTags:\n";
        print_r($doc->getOtherTags());
        echo "\n** getShortDesc:\n";
        print_r($doc->getShortDesc());
        echo "\n** getLongDesc:\n";
        print_r($doc->getLongDesc());
    
 catch (Exception $e) 
    print_r($e);


?>

这是测试页面的输出:

** getDesc:
This is the short description.

This is the 1st line of the long description 
This is the 2nd line of the long description 
This is the 3rd line of the long description
** getTags:
Array
(
    [param] => Array
        (
            [0] => Array
                (
                    [0] => Array
                        (
                            [0] => Array
                                (
                                    [0] => bool
                                    [1] => 
                                )

                            [1] => Array
                                (
                                    [0] => string
                                    [1] => 
                                )

                        )

                    [1] => foo
                    [2] => sometimes a boolean, sometimes a string (or, could have just used "mixed")
                )

            [1] => Array
                (
                    [0] => Array
                        (
                            [0] => Array
                                (
                                    [0] => bool
                                    [1] => 
                                )

                            [1] => Array
                                (
                                    [0] => int
                                    [1] => 
                                )

                        )

                    [1] => bar
                    [2] => sometimes a boolean, sometimes an int (again, could have just used "mixed")
                )

        )

    [return] => Array
        (
            [0] => Array
                (
                    [0] => Array
                        (
                            [0] => Array
                                (
                                    [0] => string
                                    [1] => 
                                )

                        )

                    [1] => de-html_entitied string (no entities at all)
                )

        )

)

** getTag('param'):
Array
(
    [0] => Array
        (
            [0] => Array
                (
                    [0] => Array
                        (
                            [0] => bool
                            [1] => 
                        )

                    [1] => Array
                        (
                            [0] => string
                            [1] => 
                        )

                )

            [1] => foo
            [2] => sometimes a boolean, sometimes a string (or, could have just used "mixed")
        )

    [1] => Array
        (
            [0] => Array
                (
                    [0] => Array
                        (
                            [0] => bool
                            [1] => 
                        )

                    [1] => Array
                        (
                            [0] => int
                            [1] => 
                        )

                )

            [1] => bar
            [2] => sometimes a boolean, sometimes an int (again, could have just used "mixed")
        )

)

** getErrors:
Array
(
)

** getOtherTags:
Array
(
)

** getShortDesc:
This is the short description.
** getLongDesc:
This is the 1st line of the long description 
This is the 2nd line of the long description 
This is the 3rd line of the long description

【讨论】:

这种方法不再有效。似乎在 2014 年,他们决定取消自己的解析器,转而只使用 DocBlox/phpDocumentor 解析器。 它适用于旧版本的 Sami:github.com/FriendsOfPHP/Sami/tree/v1.2【参考方案3】:

您可以使用 DocBlox (http://github.com/mvriel/docblox) 为您生成 XML 数据结构;您可以使用PEAR 安装 DocBlox,然后运行以下命令:

docblox parse -d [FOLDER] -t [TARGET_LOCATION]

这将生成一个名为structure.xml 的文件,其中包含有关您的源代码的所有元数据,包括已解析的文档块。

您可以使用DocBlox_Reflection_DocBlock* 类直接解析一段DocBlock 文本。

您可以通过确保启用自动加载(或包括所有 DocBlox_Reflection_DocBlock* 文件)并执行以下操作来做到这一点:

$parsed = new DocBlox_Reflection_DocBlock($docblock);

之后您可以使用getters 提取您想要的信息。

注意:不需要去掉星号;反射类负责这个。

【讨论】:

DocBlox 已死。好像已经合并到这个github.com/phpDocumentor/phpDocumentor2 确实,phpDocumentor 和 DocBlox 已经合并形成了 phpDocumentor2 刚刚注意到你是作者。鬼鬼祟祟的家伙。 @mvriel,DocBlox_Reflection_DocBlock 正如您所指出的那样已被弃用,我们如何将 phpDocumentor2 api 用于相同目的?【参考方案4】:

退房

http://pecl.php.net/package/docblock

我认为 docblock_tokenize() 函数将帮助您实现部分目标。

【讨论】:

【参考方案5】:

我建议补充一下,它很酷,很活跃,并在许多 php5 框架中使用...

http://code.google.com/p/addendum/

检查测试以获取示例

http://code.google.com/p/addendum/source/browse/trunk#trunk%2Fannotations%2Ftests

【讨论】:

【参考方案6】:

您始终可以从phpDoc 查看源代码。该代码位于LGPL 下,因此如果您决定复制它,您需要根据相同的许可许可您的软件并正确添加正确的通知。

编辑:除非@Samuel Herzog 指出您将其用作库。

感谢@Samuel Herzog 的澄清。

【讨论】:

只要您仅将 phpDoc 部分用作库,就可以使用您自己的许可模型。许可证信息既不需要也不正确。 抱歉,如果我的问题不清楚,但我知道我可以使用 PHPDoc 源代码。我希望这里有人可以提供我可以用来执行此操作的确切代码,以免我在不熟悉的源代码树中翻滚。【参考方案7】:

根据您的描述,我只能怀疑您正在尝试做什么(PHP 代码文档)。由于您没有说明为什么要这样做,我只能推测。

也许您应该尝试另一种方法。要记录 PHP 代码(如果这是您正在尝试的),我会使用 doxygen 并且从您的代码注释的外观来看,它已经格式化为 doxygen。

使用Graphviz,doxygen 还可以呈现漂亮的类图和调用树。

【讨论】:

我正在做我所说的我正在做的事情,试图将一串 PHP Documenter cmets 解析成一个数据结构。我希望这里有人可以提供我可以用来执行此操作的确切代码,以免我在不熟悉的源代码树中翻滚。【参考方案8】:

建议你看看http://code.google.com/p/php-annotations/

如果需要,代码很容易修改/理解。

【讨论】:

【参考方案9】:

正如上述答案之一所指出的,您可以使用 phpDocumentor。如果您使用作曲家,那么只需添加 “phpdocumentor/reflection-docblock”:“~2.0” 到您的“要求”块。

查看示例:https://github.com/abdulla16/decoupled-app/blob/master/composer.json

有关用法示例,请参阅: https://github.com/abdulla16/decoupled-app/blob/master/Container/Container.php

【讨论】:

【参考方案10】:

user1419445 代码的更新版本。 DocBlockParser::parse() 方法已更改,需要第二个上下文参数。它似乎也与 phpDocumentor 略微耦合,所以为了简单起见,我假设您已经通过 Composer 安装了 Sami。下面的代码适用于 Sami v4.0.16

<?php

require_once 'vendor/autoload.php';

class TestClass 
    /**
     * This is the short description.
     *  
     * This is the 1st line of the long description 
     * This is the 2nd line of the long description 
     * This is the 3rd line of the long description   
     *  
     * @param bool|string $foo sometimes a boolean, sometimes a string (or, could have just used "mixed")
     * @param bool|int $bar sometimes a boolean, sometimes an int (again, could have just used "mixed") 
     * @return string de-html_entitied string (no entities at all)
     */
    public function another_test($foo, $bar) 
        return strtr($foo,array_flip(get_html_translation_table(HTML_ENTITIES)));
    


use Sami\Parser\DocBlockParser;
use Sami\Parser\Filter\PublicFilter;
use Sami\Parser\ParserContext;

try 
    $method = new ReflectionMethod('TestClass', 'another_test');
    $comment = $method->getDocComment();
    if ($comment !== FALSE) 
        $dbp = new DocBlockParser();
        $filter = new PublicFilter;
        $context = new ParserContext($filter, $dbp, NULL);
        $doc = $dbp->parse($comment, $context);
        echo "\n** getDesc:\n";
        print_r($doc->getDesc());
        echo "\n** getTags:\n";
        print_r($doc->getTags());
        echo "\n** getTag('param'):\n";
        print_r($doc->getTag('param'));
        echo "\n** getErrors:\n";
        print_r($doc->getErrors());
        echo "\n** getOtherTags:\n";
        print_r($doc->getOtherTags());
        echo "\n** getShortDesc:\n";
        print_r($doc->getShortDesc());
        echo "\n** getLongDesc:\n";
        print_r($doc->getLongDesc());
    
 catch (Exception $e) 
    print_r($e);


?>

【讨论】:

【参考方案11】:

看看 Php Comment Manager 包。它允许解析方法 DocBloc cmets。它使用 Php Reflection API 来获取方法的 DocBloc cmets

【讨论】:

以上是关于将 PHP 文档注释解析为数据结构的主要内容,如果未能解决你的问题,请参考以下文章

Java语言基础

PHPDOC文档工具注释风格整理

godoc使用方法介绍

PHP Google Cloud Vision API:注释立即淹没内存

Java语言概述-注释与API文档等

03-注释与API文档