Tuesday, December 22, 2009

Asking the community: a standard for @return array

It would have certainly happened to you to define a phpDocumentor annotation on a function or a method:
<?php
/**
 * @param string $a   name your parameter better than this one
 * @return boolean
 */
function doSomething($a)
{
    // code... 
}
These annotations are parsed by phpDocumentor to automatically produce Api documentation in various formats, such as html and pdf.
It is also true that you can specify a class name as a data type:
<?php
/**
 * @return Zend_Form_Element
 */
function doSomething()
{
    // code... 
}
Since this is a widely employed standard for php frameworks, I decided to rely on @return annotations as the mean to define domain model relationships in my project NakedPhp. This is not different from the relationships phpDocumentor infers to generate links between html documents: for instance the Zend_Form_Element return type definition would be printed as a link to its actual Api documentation html page, to allow fast navigation.
But what happens when you want to specify that a methods return an array or a collection of elements?
<?php
/**
 * @return array
 */
function doSomething()
{
    // code... 
}
Not very clear, as the question that arises is "What is the type of array elements?"
Note that Php is a dynamic language and arrays can be heterogenous, but very often they contain elements of the same type for the sake of consistency; consider for example an array of Zend_Form_Element instances: even if they are different elements they share a common superclass whose methods you can call without fear.
Note also that Php lacks a real collection class or interface, and even if a generic one is provided by a framework, the annotation would not be much clear.
/**
 * @return Doctrine\Common\Collections\Collection
 */
or:
/**
 * @return ArrayObject
 */
At least in the former case you know that there are homogeneous elements in the returned collection, but the situation is the same.
Since arrays and collections are used as dumb containers, the first thing you will do on an array is to iterate on it, and then you will need to know what is the class of the contained elements to find out which methods to call, or which methods accept this kind of elements.
Of course you can do something like this:
/**
 * @return array   of @see Zend_Form_Element
 */
But this is not a standard, and different developers would use different annotations:
/**
 * @return array   this contains Zend_Form_Element instances
 */
/**
 * @return array   of Zend_Form_Element 
 */
These annotations would be parsed by phpDocumentor, but the class name would be mangled in a string and not manageable anymore. It's like scraping a blog versus using a feed.
PHPLint documentation says it recognizes annotations like array[K]E, as in this example:
/**
 * @return array[string]Zend_Form_Element 
 */
They also say that phpDocumentor already support it, but there is no trace of that in its own documentation:
The datatype should be a valid PHP type (int, string, bool, etc), a class name for the type of object returned, or simply "mixed".
The original Naked Objects implementation is written in Java and takes advantage of generics (not available in Php):
/**
 * @return List<FormElement>
 */
When javadoc or Naked Objects parse annotations, they know instantly the collection elements type, thanks to a reasonable standard that imitates the language syntax: I would be glad to do the same in Php, but there is no syntax to refer to.
I turn thus to the community, which comprehends millions of talented developers. My question is: how would you specify @return annotations for containers of elements in a way to include the elements type? I hope to grasp a de facto standard, which I can then require to follow in NakedPhp applications.

16 comments:

Anonymous said...

Have you tried the PHPLint syntax to see what comes out if it is run through PHPDocumentor?

AdamB said...

In PDT project @return SomeClass[] syntax was proposed, but ticket is still open:
https://bugs.eclipse.org/bugs/show_bug.cgi?id=170968

Avi Block said...

Think it's time for generics in PHP?

Markus said...
This comment has been removed by the author.
Markus said...

i always use String[] or MyType[]

James Dempster said...

I'm very glad this has been brought up.

I feel the example you have shown from PHPLint seems to be the best solution as it shows how the data is stored in the array (indexed or assoc).

This is something worth bringing to larger communities like PEAR and ZF?

Robin said...

I usually go for the
@return array string
to indicate an array of strings
or in case of an array of Zend_Form objects:
@return array Zend_Form

clean and simple?

Giorgio said...

I do not think generics would be a good addition to php since they solve typical problems of static languages (which php isn't), such as casting and type safety.
Obviously the PHPLint syntax does not work with phpDocumentor, but it's probably the best option since arrays and iterators in php are commonly used as maps and it permits to see the type of keys. It also defines defaults in case someone write array[]string.
"@return SomeClass[]" lacks the key type.
The syntax "@return array Zend_Form" is ambiguos imho, because in the documentation you will see:
return: Zend_Form
access: public
array methodName()
which is confusing.
The problem regards not only array but also all Traversable objects. Here something like "@return Iterator[integer]SomeClass" could be used?

JulienP said...

You can easily make a collection of typed elements by extending SPLObjectStorage.

PHP should add some mecanism to support generics, at least for object types (not talking about casts, just the check of the type at insertion in the collection), something like new SplCollection('ArrayObject')->attach(new ArrayObject);

Anonymous said...

I will use (inconsistently) things like:
@return array(ObjectType)
@return array(key => object)
@return array(Key => array(ObjectType))

And for associative arrays with a specific format e.g. startdate, enddate:
@return array(key1, key2)

Giorgio said...

SplObjectStorage, like many Spl classes, is a structure for improving performance; php lacks Collection/List/Map interfaces like Java ones.

Anonymous said...

I'm the sort of guy who loves to try unprecedented stuff. Currently I am manufacturing my personal photovoltaic panels. I am making it all alone without the aid of my staff. I'm using the internet as the only way to acheive this. I ran across a truly brilliant website that explains how to contruct pv panels and wind generators. The place explains all the steps required to solar panel construction.

I am not exactly sure bout how precise the information given there iz. If some people over here who have experience with these works can have a peak and give your feedback in the page it would be awesome and I'd highly treasure it, cauze I really enjoy solar panel construction.

Tnx for reading this. U people are great.

Beowulf said...

In the case of class attributes, I will use the following:

/**
* @var array|objName
*/

That way the documentation will correctly show the variable as an array, while the IDE will correctly display auto-completion options for the objects within the array.

Giorgio said...

I didn't know that! Which IDE are we talking about?

Beowulf said...

I'm currently using NetBeans 7.0.1.

Lumbendil said...

@Beowulf that syntax, to me (and phpDocumentor) means that the return is either an array or an instance of objName, which is significantly different from what you're meaning.

ShareThis