-
-
Save mvriel/3823010 to your computer and use it in GitHub Desktop.
<?php | |
/** | |
* @template <T> The type of the individual elements | |
*/ | |
class ArrayCollection implements IteratorAggregate | |
{ | |
private $elements; | |
/** | |
* @param array<T> $elements | |
*/ | |
public function __construct(array $elements) | |
{ | |
$this->elements = $elements; | |
} | |
/** | |
* @return Iterator<T> | |
*/ | |
public function getIterator() | |
{ | |
return new ArrayIterator($this->elements); | |
} | |
} | |
// usage | |
/** @type ArrayCollection<Foo> $col */ | |
$col = new ArrayCollection(); | |
foreach ($col as $elem) { | |
// $elem is instance of Foo here | |
} | |
?> | |
<?php | |
class ArrayCollection implements IteratorAggregate | |
{ | |
private $elements; | |
/** | |
* @param mixed[] $elements | |
*/ | |
public function __construct(array $elements) | |
{ | |
$this->elements = $elements; | |
} | |
/** | |
* @return ArrayIterator | |
*/ | |
public function getIterator() | |
{ | |
return new ArrayIterator($this->elements); | |
} | |
} | |
// usage | |
/** @type ArrayCollection|Foo[] $col */ | |
$col = new ArrayCollection(); | |
foreach ($col as $elem) { | |
// $elem is instance of Foo here | |
} | |
?> |
Here is a sample that might make more sense regarding Johannes proposal:
Let's have a look at the SplObjectStorage class, that provides a map where keys can be objects. (http://www.php.net/manual/en/class.splobjectstorage.php)
/** @type SplObjectStorage<MyKeyObject, MyValueObject> */
$splObjectStorage = new SplObjectStorage();
// No need to cast $current into a MyKeyObject
$current = $splObjectStorage->current();
// No need to cast $object into a MyValueObject
$object = $splObjectStorage->offsetGet($key);
I like Johannes proposal because I find it very powerful.
However, let's admit that 90% of the time, we are dealing with arrays, not ArrayCollection or SplObjectStorage, so we should be sure that the use of the @type annotation with the array is rock solid.
For this matter, one thing we do not capture is the difference between an indexed array and a hashmap. PHP does not make any difference between those 2 constructs, and yet, they are completely different and the documentation should tell the user whether the array is numerically indexed, or is a map.
A proposal would be to use 2 keys. For instance:
/** @type array<int, Object> A numerically indexed array of Objects */
$arr1 = array();
/** @type array<string, Object> A map of Objects */
$arr2 = array();
Now, this seems to be a bit of an overkill compared to the more simple proposal from Mike...
In an ideal world, we should also be able to write:
/** @type array<Object> An array of Objects (we don't know about the key) */
But that would not suit well with the @template annotation....
So I'm a bit at a loss here... Any idea on how to make this both powerful and simple to use?
I see no problem with supporting:
@type array<object> a list of objects, we don't care about the type
@type array<string,object> a map of strings to objects
The @template proposals is not directly related to arrays and more about allowing to write generic classes in userland while still getting the best autocompletion/static analysis that is possible, and btw, you could have more than one @template annotation on your class if your class needs to fill in more holes (like key, and value in a map as in your \SplObjectStorage example).
I wanted to post here that I have not abandoned the discussion but I have been at PHPNW12 the past weekend and am still catching up. A proper response follows somewhere in the upcoming week
Could these ideas be successfully condensed into just this one syntax:
@type (class name or primitive type 'array')<(optional key type, ) (required member type) (description)
I have been thinking quite a bit about this proposal in the past time and to be honest, it is starting to grow on me. Though I am not so sure about the @template
tag since that would only serve a purpose for a Collection object that only intends to be used with a specific set of values (that and I don't think the name @template
is intuitive).
Some concerns that I still have:
- How to deal with generics where the value may be of several types? Use the or operator (|) as has been done so far?
- We should also support multi-dimensional notations
I think we sould keep the or operator for working with several types. But what do you mean by "multi-dimensional notations"?
Coming from a Java perspective, I would love some kind of Generics support in PHP and/or PHPDOC. 👍
There are two reasons I can think of why I'd prefer the first alternative:
Foo[]
suggests that$col
is an array; typically, you will have more methods on theArrayCollection
class likefirst()
for example. Calling first on an array could be flagged by such tools.ArrayCollection<Foo>
style you only need to declare the type once. This will become more obvious if you consider that theArrayCollection
likely has more than one method which depends on the type that it contains.Obviously, the @template annotation is not supported by IDEs yet. After all, it does not exist yet, so this is not suprising :). However, since most IDEs are written in Java (eclipse, netbeans, PHPStorm) and have type inference engines for Java which support this feature, it should be fairly easy for them to add support for such types in PHP as well. Additionally, I guess if we write a good standard that is used by most major libraries, they will be so happy that they do not have to support so many variations anymore, that they will implement this in no time :)