Best way to document Array options in PHPDoc?

PhpCakephpPhpdoc

Php Problem Overview


I'm struggling to write readable and easy to understand documentation that describes the multi-tree structure for Array options that are passed to a function.

Here is an example array structure.

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

There are many possible options for the above array, but this is used as a parameter to a function that understands that structure.

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

I'd like to document how the array should be structured in PHPDoc, but I'm not sure what the correct approach is.

Here is what I have now.

/**
 * 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.
 **/

My problem is that when the HTML document is generated, it's not formatted very nicely. Additionally, I'm not sure the above is clearly explains the array structure.

Is there an alternative approach?

Php Solutions


Solution 1 - Php

This is how I do it instead:

/**
 * 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){}

Think it's quite clean and easy to understand what $params should be.

Solution 2 - Php

I wrote a plugin for phpstorm that allows specifying keys like this:

(basically just a slightly more formalized version of @siannone's format)

/**
 * @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][''];
}

enter image description here

It allows to specify @return keys as well:

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

enter image description here

Solution 3 - Php

Just adding some tabulation will make it look good and easy to understand

/**
 * 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.
 **/

A nested list approach:

<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>

Result:

  • 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.

If you want it to look fancy, with a bit of Css it will make wonders! xd

Solution 4 - Php

Among widely accepted key type documenting formats, I'd like to mention few popular ones here:

Psalm/PHPStan/phan format
/** @param array{foo: string, bar: int} $args */

as a bonus, can also be used for static code analysis with their tools

Wordpress format
/**
 * @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.
 * }
 */

and both are supported by deep-assoc-completion plugin

Solution 5 - Php

Can you use objects instead of arrays? That would make documentation easy.

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;
    }

    //...
}

Then you can use a type hint where the class is required.

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

Solution 6 - Php

Markdown Syntax for Object Notation (MSON) may be a better choice.

example

/**
 * @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
 */

Solution 7 - Php

I kinda like this better:

 * @param array $doc
 *          'type'=>Doc::MY_DOC_TYPE,
 *          'created'=>$now,
 *          'modified'=>$now

I just paste in the code from where it gets initialized, quick and easy.

Solution 8 - Php

Arrays in PHP are really more like anonymous structs.

For arbitrary data structures there are a number of schema validators but unfortunately it's not widely supported in type hinting. Perhaps some common schemes have plugins? The problem is things working in only one or a few places. You might get the right thing working for your IDE but run static analysis and it can all got to hell.

Care needs to be taken to separate things out so that if possible other tools not supporting a scheme, such as via a plugin, will simply ignore it.

PHPDoc tends to be supported everywhere but is also very limited.

There are often proposals but there's no real good standard. Most solutions are non-standard, not widely supported, partial hacks with limitations or purely superficial (documentation).

There are specific implementations in specific places but nothing widespread and dedicated to PHP itself. Though you can make your own schemas in PHP IDE's tend to lack decent code bridges, even those with PHP in the name.

You should define your field struct separately. Your outer data structure is as pseudo code @key fields field[] rather than representing as multi-dimensional arrays. Conceptually and confusingly you can go so far as:

@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

Or steal from C then invent a laguage...

struct connection {
    struct fields {
        string hostname;
        string databaseName;
        string password;
        int port = 1433;
        custom optional sublevel {
            stdClass *;
        };
    };
};

You can invent a schema based on structs to allow both flat and nested. Nested should be the default, things should only be defined to be as accessible as they need to be for reuse.

An unusual approach is to instead use objects. This doesn't have to entail using interfaces such as array access. In PHP an object wraps an array for properties. It's possible to cast an array to an object (without implementation, only properties) and back.

If you use objects instead as an associative array ($array[$key] versus $object->{$key}) then you can make dummy objects to at least fool the IDE...

final class PersonStruct {
    /** @var int */
    public $x;

    /** @var int $y */

    public int $z;
}

Of those three options which may or may not work depends on the tool used.

You can then lie...

/** @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;
}

The problem with this is that it means converting arrays to objects. That have both performance implications and changes pass by value to pass by reference. Which is faster is debatable. Arrays should in theory be faster, though objects have benefits being references by default and work better with JSON which separates object from array unlike PHP.

Objects also don't support a property that might not be set very well in regards to type hinting, even though properties in an object are just a PHP array (use ->{key} instead of [key]). There's the potential for other weird things.

If performance is a real concern, you can turn PHP into a compiled language. In the same way you can extend an interface to make an object compilable you can do the same for where you might do everything with OOP and auto-complete but can then do the equivalent of inlining a class by specifying the property it wraps, then using reflection to pretty much replace uses with the contents of the matching methods, with a few extra bits needed (marking what to inline or convert to procedural, single property to wrap or multiple, etc).

The concept is similar to boxing and unboxing. If you're really insane about SA support and broad support for IDEs (auto-complete, checking, etc), analysers, tools, etc then it might be the only way.

Solution 9 - Php

AS this is purely display rather than a directive, and should retain space formatting within the docs, I'd be inclined to go for readability with indentation rather than a wall of characters:

 * 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.

Though using an actual PHP array definition with indentation is even cleaner

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionReactgularView Question on Stackoverflow
Solution 1 - PhpsiannoneView Answer on Stackoverflow
Solution 2 - PhpKlesunView Answer on Stackoverflow
Solution 3 - PhpaleationView Answer on Stackoverflow
Solution 4 - PhpKlesunView Answer on Stackoverflow
Solution 5 - PhpjjokView Answer on Stackoverflow
Solution 6 - Phpjk2KView Answer on Stackoverflow
Solution 7 - PhpCommaToastView Answer on Stackoverflow
Solution 8 - PhpjgmjgmView Answer on Stackoverflow
Solution 9 - PhpMark BakerView Answer on Stackoverflow