4

What's the best way to document the elements of an array when it is a parameter to a method? For example, using the PHPDoc headers, I might have something like:

@param array $data

What this doesn't tell me is what elements are mandatory in the array, and what are optional elements. I imagine this should go in the explanation of the method. Something like:

array: $data
============ 
int     $id      Required 
name    $string  Required 
town    $string  Optional
DatsunBing
  • 8,684
  • 17
  • 87
  • 172
  • 4
    Wouldn't you want to use a well documented and well defined object instead of an anonymous array in such a case? In the object, you could document all methods and give hints whether they're required or not. – stef77 Jul 22 '13 at 08:39

3 Answers3

4
/**
 * @param array $data
 * 
 * @var $data[id] int, required
 * @var $data[name] string, required
 * @var $data[town] string, required
 */

This example of using doctrine and zf2 example:

/**
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue
* @Form\Attributes({"type":"hidden"})
*/
protected $id;

/**
* @ORM\Column(type="string")
* @Form\Type("Zend\Form\Element\Text")
* @Form\Required({"required":"true"})
* @Form\Filter({"name":"StripTags"})
* @Form\Filter({"name":"StringTrim"})
* @Form\Validator({"name":"StringLength", "options":{"min":"5"}})
* @Form\Options({"label":"First name"})
*/
protected $firstName;
Haver
  • 443
  • 2
  • 11
2

To answer the question there is no formal way, try to use a way that you think is most intuitive. I do something similar:

/**
 * @param array $data [ int $id, string $name, string $town ]
 */

However I wouldn't use this notation for parameters but rather as return values. In your case I would extract the method arguments to an object and pass that into the method instead:

/**
 * @param User $user
 */
public function myMethod( User $user )
{ 
    //... 
}

The reason for this is the User object exposes it's properties as an API to other developers, self documenting code!

The other method would be to separate out the array elements into arguments like so:

/**
 * @param int $id
 * @param string $name
 * @param string $town
 */
public function myMethod( $id, $name, $town )
{
    //...
}

3 arguments is just about passable but you should start looking for a way to refactor it, like my first suggestion. 4 arguments is generally agreed to be messy and you refactor it.

David Normington
  • 849
  • 8
  • 15
2

If you have such a complex array with constraints for every single member, I wouldn't use an anonymous array but rather a well defined object. With an array, you can never be sure what it holds, that's somewhat like passing "Object" in e.g. Java, which you rarely would consider a good choice.

However, there is the possibility of a little hinting, when you array contains objects of a certain type as explained here, but that's not a really good answer to your question.

If you really need the parameter as an array, you might document it the way you proposed in the method's description; however, if you use an object as parameter, you'd have additional support in modern IDEs (IntelliSense and so on).

EDIT: I mean, for me the question would be "why would I want to use an anonymous array instead of a self defined type" - and besides simplicity (which will backfire as technical debt later if you maintain and extend your code), I cannot think of a reason, especially compared to what you gain in using a user defined type (self documented code, constraints visible and made explicit by standard methods and so on).

If you just need a dump of data, you might want to go with a simple array, but since you're already thinking about optional and required keys, that screams for a user defined type.

EDIT2: Regarding your comment about if your already have an array as source: I'm not sure whether you need to pass it on as an array or do "mapping" operations as soon as you receive the array (e.g. as $_POST or as return value from some third party library or PHP internal functions or such).

I suppose one could argue that it's not the model's business to interpret data generated by views (e.g. HTML forms which POST data), but rather the controller's resonsibility to react accordingly to the input and transfer the model in the appropriate state. What I mean by this is that you could do something like this if you receive e.g. an array as $_POST:

$customer = new Customer();
$customer->setId($_POST['id']);
$customer->setName($_POST['name']);
$customer->setTown($_POST['town']);

And handle errors as soon as you access the $customer, e.g. throwing exceptions if the name is not set (i.e. $_POST['name'] was empty or such). This way, you use the source array to call setters on the object instead of e.g. passing the array to a factory like Customer::buildByHttpPostData(array $data) and thereby delegating the knowledge of the view's details (names of HTML input tags and such).

Bottom line is, there's no "standard" way to declare required or optional array keys, and of course you can describe those constraints in the method description, but perhaps you can circumvent this by keeping with supported ways like PHPDoc comments on setters or getters.

Of course, there may be better ways to approach the problem, and perhaps somebody comes up with a better answer how to handle it.

Community
  • 1
  • 1
stef77
  • 1,000
  • 5
  • 19
  • Thanks stef77, but what if the source is an array, e.g. a $_POST? At some point the array needs to loaded into an object, and so it will need to be documented at that point. – DatsunBing Jul 22 '13 at 22:08
  • Please see my edited answer which tries to address your point... – stef77 Jul 23 '13 at 11:40