在PHPDoc中记录数组选项的最佳方法是什么?

103

我很难编写易读且易于理解的文档,以描述传递给函数的数组选项的多树结构。

以下是一个示例数组结构。

$arr = [
   'fields' => [
       'title' => [
           'name'     => 'Document.title',
           'format'   => 'string',
           'readonly' => true
       ]
   ]
];

以上数组有许多可能的选项,但它作为参数传递给一个了解该结构的函数。

function doSomething(array $arr) { ... }

我希望记录下PHPDoc中数组应该如何结构化,但我不确定正确的方法是什么。

这是我现在拥有的。

/**
 * Holds configuration settings for each field in a model.
 * Defining the field options
 *
 * array['fields'] array Defines the feilds to be shown by scaffolding.
 * array['fields'][fieldName] array Defines the options for a field, or just enables the field if array is not applied.
 * array['fields'][fieldName]['name'] string Overrides the field name (default is the array key)
 * array['fields'][fieldName]['model'] string (optional) Overrides the model if the field is a belongsTo assoicated value.
 * array['fields'][fieldName]['width'] string Defines the width of the field for paginate views. Examples are "100px" or "auto"
 * array['fields'][fieldName]['align'] string Alignment types for paginate views (left, right, center)
 * array['fields'][fieldName]['format'] string Formatting options for paginate fields. Options include ('currency','nice','niceShort','timeAgoInWords' or a valid Date() format)
 * array['fields'][fieldName]['title'] string Changes the field name shown in views.
 * array['fields'][fieldName]['desc'] string The description shown in edit/create views.
 * array['fields'][fieldName]['readonly'] boolean True prevents users from changing the value in edit/create forms.
 * array['fields'][fieldName]['type'] string Defines the input type used by the Form helper (example 'password')
 * array['fields'][fieldName]['options'] array Defines a list of string options for drop down lists.
 * array['fields'][fieldName]['editor'] boolean If set to True will show a WYSIWYG editor for this field.
 * array['fields'][fieldName]['default'] string The default value for create forms.
 *
 * @param array $arr (See above)
 * @return Object A new editor object.
 **/

我的问题是,在生成 HTML 文档时,它的格式不太好看。此外,我不确定上面的内容清楚地解释了数组结构。

是否有其他解决方案?


3
请看这个被采纳的答案:https://dev59.com/RnE85IYBdhLWcg3wkkY8#2725073 - Chris L
@ChrisL 谢谢,我也认为那是我应该做的。 - Reactgular
请参见 https://github.com/phpDocumentor/phpDocumentor2/issues/650 以获取功能请求。 - Ben Creasy
2
如果您需要数据结构如此具体的话,也许数组不是正确的数据类型?您是否考虑过使用DTO类(基本上只是一个带有一堆属性的类)呢? - GordonM
你可能想要查看Phan联合类型 - MAChitgarha
9个回答

99

在被广泛接受的关键类型文档格式中,我想在这里提到几种流行的格式:

Psalm/PHPStan/phan格式

/** @param array{foo: string, bar: int} $args */

作为额外的福利,这些工具还可以与静态代码分析工具一起使用。

WordPress格式

/**
 * @param array $args {
 *     Optional. An array of arguments.
 *
 *     @type type $key Description. Default 'value'. Accepts 'value', 'value'.
 *                     (aligned with Description, if wraps to a new line)
 *     @type type $key Description.
 * }
 */

并且两者都得到了deep-assoc-completion插件的支持


3
+1 给 PHPStan,因为它在 PHPStorm 中默认支持,无需额外插件。作者的数组看起来像这样:/** @param array{fields: array{title: array{name: string, format: string, readonly: bool}}} $arr */ - Oleksandr Boiko
很好的回答。PHPStan格式确实与PhpStorm捆绑在一起,使其得到原生支持。谢谢。 - undefined

85

我是这样做的:

/**
 * Class constructor.
 *
 * @param array $params Array containing the necessary params.
 *    $params = [
 *      'hostname'     => (string) DB hostname. Required.
 *      'databaseName' => (string) DB name. Required.
 *      'username'     => (string) DB username. Required.
 *      'password'     => (string) DB password. Required.
 *      'port'         => (int) DB port. Default: 1433.
 *      'sublevel'     => [
 *          'key' => (\stdClass) Value description.
 *      ]
 *    ]
 */
 public function __construct(array $params){}

我认为很清晰易懂,$params应该是什么。


1
你会如何记录你的示例中的'sublevel'键?它没有自己的注释。我仍然想将其记录为我的代码中的可选项。 - JM-AGMS
2
@JM-AGMS 我可能会这样做:https://pastebin.com/0ebYpfpQ我认为在注释中再加入注释看起来并不好看,但它可以使其与其他参数键区分开来。 - siannone

75

我为phpstorm编写了一个插件,它允许这样指定键:

(格式类似于@siannone的,但使用示例值而不是类型)

/**
 * @param array $arr = [
 *     'fields' => [ // Defines the feilds to be shown by scaffolding
 *         $anyKey => [
 *             'name' => 'sale', // Overrides the field name (default is the array key)
 *             'model' => 'customer', // (optional) Overrides the model if the field is a belongsTo associated value.
 *             'width' => '100px', // Defines the width of the field for paginate views. Examples are "100px" or "auto"
 *             'align' => 'center', // Alignment types for paginate views (left, right, center)
 *             'format' => 'nice', // Formatting options for paginate fields. Options include ('currency','nice','niceShort','timeAgoInWords' or a valid Date() format)
 *             'title' => 'Sale', // Changes the field name shown in views.
 *             'desc' => 'A deal another person that results in money', // The description shown in edit/create views.
 *             'readonly' => false, // True prevents users from changing the value in edit/create forms.
 *             'type' => 'password', // Defines the input type used by the Form helper
 *             'options' => ['option1', 'option2'], // Defines a list of string options for drop down lists.
 *             'editor' => false, // If set to True will show a WYSIWYG editor for this field.
 *             'default' => '', // The default value for create forms.
 *         ],
 *     ],
 * ]
 */
public static function processForm($arr)
{
    $fieldName = 'sale';
    $arr['fields'][$fieldName][''];
}

在此输入图片描述

它也允许指定@return关键字:

/**
 * @return array [
 *     'success' => true,
 *     'formObject' => new Form,
 *     'errors' => [],
 * ]
 */
public static function processForm($arr);

在此输入图片描述


非常好,我会在注释中添加类型提示 'name' => 'sale', // (string) Overrides...。对于 VSCode 有类似的东西吗? - Alex
除非您愿意将扩展从 Java 移植到 TypeScript,否则不行。在 intelephense 中已经有一个相关问题(https://github.com/bmewburn/intelephense/issues/97),您可以尝试提高它的优先级。 - Klesun
这个与 Vimeo/PSALM 兼容吗? - Henk Poley
1
是的,它 应该能够理解 psalm php 文档格式,包括 @template。不过如果你发现某个 psalm 功能在插件中无法正常工作,请提交问题,我将非常乐意更新它。 - Klesun
1
@Alex,好消息,有一位慷慨的善人赞助了vscode版本的开发启动。希望在接下来的几周内实现基本功能。 - Klesun
显示剩余3条评论

50

只需要添加一些制表符,就能让它看起来好看且容易理解。

/**
 * Holds configuration settings for each field in a model.
 * Defining the field options
 *
 * array['fields']              array Defines the fields to be shown by scaffolding.
 *          [fieldName]         array Defines the options for a field, or just enables the field if array is not applied.
 *              ['name']        string Overrides the field name (default is the array key)
 *              ['model']       string (optional) Overrides the model if the field is a belongsTo associated value.
 *              ['width']       string Defines the width of the field for paginate views. Examples are "100px" or "auto"
 *              ['align']       string Alignment types for paginate views (left, right, center)
 *              ['format']      string Formatting options for paginate fields. Options include ('currency','nice','niceShort','timeAgoInWords' or a valid Date() format)
 *              ['title']       string Changes the field name shown in views.
 *              ['desc']        string The description shown in edit/create views.
 *              ['readonly']    boolean True prevents users from changing the value in edit/create forms.
 *              ['type']        string Defines the input type used by the Form helper (example 'password')
 *              ['options']     array Defines a list of string options for drop down lists.
 *              ['editor']      boolean If set to True will show a WYSIWYG editor for this field.
 *              ['default']     string The default value for create forms.
 *
 * @param array $arr (See above)
 * @return Object A new editor object.
 **/

嵌套列表方法:

<ul>
    <li>
        array['fields'] array Defines the fields to be shown by scaffolding.
        <ul>
            <li>
                [fieldName]             array Defines the options for a field, or just enables the field if array is not applied.
                <ul>
                    <li> ['name']       <i><u>string</u></i> Overrides the field name (default is the array key) </li>
                    <li> ['model']      <i><u>string</u></i> (optional) Overrides the model if the field is a belongsTo associated value.</li>
                    <li> ['width']      <i><u>string</u></i> Defines the width of the field for paginate views. Examples are "100px" or "auto"</li>
                    <li> ['align']      <i><u>string</u></i> Alignment types for paginate views (left, right, center)</li>
                    <li> ['format']     <i><u>string</u></i> Formatting options for paginate fields. Options include ('currency','nice','niceShort','timeAgoInWords' or a valid Date() format)</li>
                    <li> ['title']      <i><u>string</u></i> Changes the field name shown in views.</li>
                    <li> ['desc']       <i><u>string</u></i> The description shown in edit/create views.</li>
                    <li> ['readonly']   <i><u>boolean</u></i> True prevents users from changing the value in edit/create forms.</li>
                    <li> ['type']       <i><u>string</u></i> Defines the input type used by the Form helper (example 'password')</li>
                    <li> ['options']    <i><u>array</u></i> Defines a list of string options for drop down lists.</li>
                    <li> ['editor']     <i><u>boolean</u></i> If set to True will show a WYSIWYG editor for this field.</li>
                    <li> ['default']    <i><u>string</u></i> The default value for create forms.</li>
                </ul>
            </li>
        </ul>
    </li>
 </ul>

结果:

  • array['fields'] array 定义Scaffolding显示的字段。
    • [fieldName] array 定义一个字段的选项,或者如果没有应用数组,则仅启用该字段。
      • ['name'] string 覆盖字段名称(默认为数组键)
      • ['model'] string (可选)如果字段是belongsTo关联值,则覆盖模型。
      • ['width'] string 定义分页视图中字段的宽度。示例为“100px”或“auto”
      • ['align'] string 用于分页视图的对齐类型(左、右、居中)
      • ['format'] string 分页字段的格式选项。选项包括('currency'、'nice'、'niceShort'、'timeAgoInWords'或有效的Date()格式)
      • ['title'] string 更改在视图中显示的字段名称。
      • ['desc'] string 编辑/创建视图中显示的描述。
      • ['readonly'] boolean True会阻止用户在编辑/创建表单中更改值。
      • ['type'] string 定义Form helper使用的输入类型(例如'password')
      • ['options'] array 为下拉列表定义字符串选项列表。
      • ['editor'] boolean 如果设置为True,将为此字段显示WYSIWYG编辑器。
      • ['default'] string 创建表单的默认值。

如果您想要它看起来花哨一些,使用一些CSS就会有奇效!xd


当文档转换为HTML时,缩进会被移除。 :( - Reactgular
它们不应该在HTML源代码中消失。如果您打算在HTML渲染中显示它们,我建议您使用表格,甚至可以使用嵌套的无序列表,可能性是无限的。我不知道您想要一个HTML输出。 - aleation
1
哇,谢谢 :) 我认为混合使用<code>示例就可以解决问题了。 - Reactgular
1
是的,如果您添加任何CSS,它将使其看起来非常专业!祝您的项目好运:D - aleation

33

你可以使用对象代替数组吗?这将使文档编写变得更容易。

class Field {

    /**
     * The name of the thing.
     * @var string
     */
    protected $name;

    protected $model;
    protected $width;
    //...

    public function getName() {...}
    public function setName() {...}
    //...
}

class FieldList implements SomeKindOfIterator {

    /**
     * Some fields.
     * @var Field[]
     */
    protected $fields = array();

    /**
     * ...
     */
    public function push(Field $field) {
         $this->fields[] = $field;
    }

    //...
}

然后您可以在需要该类的地方使用类型提示。

/**
 * Do something.
 * @param FieldList $field_list The field.
 */
function doSomething(FieldList $field_list) {...}

2
必须+1并添加,请考虑将其移动到预定义的公共类中,该类具有声明的公共变量,作为关联数组的替代方案。不仅速度更快,在处理大型数据集时还占用更少的内存,您可以在类和属性上使用phpdoc注释和标签生成一些很好的文档。 - Joey T
2
Field[] is the correct notation instead of array[Field] - NDM
1
另外加1是因为它可以让IDE理解它以进行自动完成、警告等操作。其他答案在我看来不具备这个功能。 - Barry Staes

18
Markdown Syntax for Object Notation(MSON)可能是更好的选择。

示例

/**
 * @param array $config
 *   + app (string, required) - app directory name
 *   + view (string, required) - view directory name
 *   + type (enum[string]) - site type
 *     + pc - PC version
 *     + wap - mobile version
 *     + other - other, default value
 *   + table_prefix (string) - database table prefix
 */

1
这与VS Code兼容(可能特定于PHP Intelephense?) - thelr

4
在PHP中,数组更像是匿名结构体。对于任意数据结构,有许多模式验证器可用,但不幸的是,在类型提示中它并不被广泛支持。也许一些常见的模式有插件?问题是只有一个或几个地方有效。你可能会在IDE中得到正确的结果,但运行静态分析时,一切都会变得混乱。需要注意将事物分开,以便如果可能,其他不支持模式的工具(例如通过插件)将简单地忽略它。PHPDoc往往被广泛支持,但也非常有限。通常会有提议,但没有真正的好标准。大多数解决方案都是非标准的、不被广泛支持的、带有局限性的或纯粹表面的(文档)。虽然你可以在PHP中自己定义模式,但IDE缺乏良好的代码桥接,即使是那些带有PHP名称的IDE也是如此。你应该单独定义你的字段结构。你的外部数据结构是伪代码@key fields field[],而不是表示为多维数组。在概念上,令人困惑的是,你可以走得这么远:
@start struct custom
@key \stdClass *
@end struct

@start struct fields
@key string hostname
@key string databaseName
@key string password
@key int port=1433
@key custom|undefined sublevel
@end struct

@start struct connection
@key fields fields
@end struct

最初的回答:或者从C语言中窃取,然后发明一种新的编程语言...
struct connection {
    struct fields {
        string hostname;
        string databaseName;
        string password;
        int port = 1433;
        custom optional sublevel {
            stdClass *;
        };
    };
};

你可以基于结构体发明一种模式,允许平面和嵌套。嵌套应该是默认的,只有需要重用时才定义可访问性。
一种不寻常的方法是使用对象。这不一定涉及使用接口,例如数组访问。在PHP中,对象包装一个属性数组。可以将数组强制转换为对象(没有实现,仅有属性)并返回。
如果你使用对象而不是关联数组($array[$key]与$object->{$key}),那么你可以制作虚拟对象来欺骗IDE...
final class PersonStruct {
    /** @var int */
    public $x;

    /** @var int $y */

    public int $z;
}

在这三个选项中,能否成功取决于所使用的工具。

然后你可以撒谎...

注:Original Answer翻译成"最初的回答"是不必要的,因为它并未在需要翻译的内容中出现。
/** @var PersonStruct $ps */
$ps = (object)['x' => 0, 'y' => 1, 'z' => 2];

/**
 * @param PersonStruct $ps
 * @return PersonStruct
 */
function f(object $ps):object {
    return $ps;
}

/**
 * @param PersonStruct $ps
 * @return PersonStruct
 */
function f(stdClass $ps):stdClass {
    return $ps;
}

这样做的问题在于它意味着将数组转换为对象。这既有性能影响,也会将按值传递更改为按引用传递。哪种更快是有争议的。理论上,数组应该更快,但对象默认引用并且与 JSON 更好地配合使用,与 PHP 不同,后者将对象与数组分开。
对象对于类型提示来说不支持未设置属性非常好,尽管对象中的属性只是 PHP 数组(使用-> {key}而不是[key])。还有其他一些奇怪的事情可能发生。
如果性能是真正的关注点,则可以将 PHP 转换为编译语言。就像您可以扩展接口以使对象可编译一样,您可以对可能使用 OOP 和自动完成完成的所有操作进行相同的操作,但然后可以通过指定其包装的属性来执行类的等效内联,然后使用反射来几乎替换匹配方法的内容,需要一些额外的位(标记要内联或转换为过程,单个属性包装或多个属性包装等)。
这个概念类似于装箱和拆箱。如果您真的疯狂于 SA 支持和广泛支持 IDE(自动完成、检查等)、分析器、工具等,则可能是唯一的方法。

3
我更喜欢这个:
 * @param array $doc
 *          'type'=>Doc::MY_DOC_TYPE,
 *          'created'=>$now,
 *          'modified'=>$now

我只需从代码初始化的位置粘贴进去,快速简便。


1
由于这只是显示而不是指令,且应在文档中保留空格格式,因此我倾向于使用缩进以提高可读性,而不是一堵字符墙。
 * array['fields'] array Defines the feilds to be shown by scaffolding.
 *           [fieldName] array Defines the options for a field, or just enables
 *                             the field if array is not applied.
 *                 ['name'] string Overrides the field name (default is the
 *                                  array key)
 *                 ['model'] string (optional) Overrides the model if the field is
 *                                  a belongsTo assoicated value.
 *                 ['width'] string Defines the width of the field for paginate
 *                                  views.
 *                                  Examples are "100px" or "auto"
 *                 ['align'] string Alignment types for paginate views (left, 
 *                                 right, center)
 *                 ['format'] string Formatting options for paginate fields.
 *                                   Options include 'currency', 'nice',
 *                                  'niceShort', 'timeAgoInWords' or a valid 
 *                                  Date() format)
 *                 ['title'] string Changes the field name shown in views.
 *                 ['desc'] string The description shown in edit/create views.
 *                 ['readonly'] boolean True prevents users from changing the
 *                                 value in edit/create forms.
 *                 ['type'] string Defines the input type used by the Form helper
 *                                  (example 'password')
 *                 ['options'] array Defines a list of string options for drop-
 *                                  down lists.
 *                 ['editor'] boolean If set to True will show a WYSIWYG editor
 *                                  for this field.
 *                 ['default'] string The default value for create forms.

尽管使用实际的PHP数组定义并进行缩进会更加清晰。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接