PHPDoc type hinting for array of objects?
因此,在phpdoc中,可以在成员变量声明上方指定
1 2 3 4 5 6 7 | <?php class Test { /** @var SomeObj */ private $someObjInstance; } ?> |
这非常有效,直到我需要对对象数组执行相同的操作,以便在稍后迭代这些对象时能够获得适当的提示。
那么,是否有一种方法来声明phpdoc标记,以指定成员变量是
在phpstorm jetbrains IDE中,你可以使用
1 2 3 4 | /** * @return SomeObj[] */ function getSomeObjects() {...} |
在phpdoc文件recommends这个方法: </P >
specified containing a single type, the Type definition informs the reader of the type of each array element. Only one Type is then expected as element for a given array.
Example:
@return int[]
使用: </P >
1 2 3 4 | /* @var $objs Test[] */ foreach ($objs as $obj) { // Typehinting will occur after typing $obj-> } |
当typehinting直列和变量, </P >
1 2 3 4 | class A { /** @var Test[] */ private $items; } |
方法级特性。 </P >
以前的答案从09年当phpdoc(和Zend Studio IDE状和NetBeans)不需要说的选项: </P >
最好你能做的是说, </P >
1 2 3 4 5 | foreach ($Objs as $Obj) { /* @var $Obj Test */ // You should be able to get hinting after the preceding line if you type $Obj-> } |
我做的那一批在Zend Studio。不知道什么其他编辑的,但它应该工作。 </P >
NETBeOs提示:
您可以在
1 2 3 4 |
完成
要指定变量,请使用对象数组:
1 2 3 | $needles = getAllNeedles(); /* @var $needles Needle[] */ $needles[1]->... //codehinting works |
这在NetBeans 7.2中有效(我正在使用它)
也可与以下人员合作:
1 2 3 4 5 | $needles = getAllNeedles(); /* @var $needles Needle[] */ foreach ($needles as $needle) { $needle->... //codehinting works } |
因此,不需要在
PSR-5:phpDoc提出了一种泛型样式表示法的形式。
句法1 2 3 4 | Type[] Type<Type> Type<Type[, Type]...> Type<Type[|Type]...> |
集合中的值甚至可以是另一个数组,甚至是另一个集合。
1 2 3 | Type<Type<Type>> Type<Type<Type[, Type]...>> Type<Type<Type[|Type]...>> |
实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <?php $x = [new Name()]; /* @var $x Name[] */ $y = new Collection([new Name()]); /* @var $y Collection<Name> */ $a = new Collection(); $a[] = new Model_User(); $a->resetChanges(); $a[0]->name ="George"; $a->echoChanges(); /* @var $a Collection<Model_User> */ |
注意:如果您希望一个IDE执行代码辅助,那么关于该IDE是否支持phpdoc泛型样式集合表示法的另一个问题就是。
从我对这个问题的回答。
我更喜欢读写干净的代码——正如罗伯特·C·马丁在《干净的代码》中所描述的那样。当遵循他的信条时,您不应该要求开发人员(作为API的用户)了解数组的(内部)结构。
API用户可能会问:这是一个只有一个维度的数组吗?物体是否分散在多维数组的所有层次上?我需要多少个嵌套循环(foreach等)来访问所有对象?该数组中"存储"了什么类型的对象?
如您所述,您希望将该数组(包含对象)用作一维数组。
如Nishi所述,您可以使用:
1 2 3 | /** * @return SomeObj[] */ |
为此。
但再次强调:请注意-这不是标准的docblock符号。这个符号是由一些IDE生产者引入的。
好吧,好吧,作为一个开发人员,您知道"[]"与PHP中的数组绑定在一起。但是在普通的PHP上下文中,"something[]"是什么意思呢?"[]"的意思是:在"某物"中创建新元素。新的元素可能就是一切。但您要表达的是:具有相同类型和确切类型的对象数组。如您所见,IDE生产者引入了一个新的上下文。你必须学习的新环境。其他PHP开发人员必须学习一个新的上下文(为了理解您的文档块)。糟糕的风格(!).
因为您的数组确实有一个维度,所以您可能希望将该"对象数组"称为"列表"。请注意,"list"在其他编程语言中有非常特殊的含义。比如说,把它叫做"收藏"就更好了。
记住:您使用的编程语言使您能够使用OOP的所有选项。使用类而不是数组,并使类像数组一样可遍历。例如。:
1 | class orderCollection implements ArrayIterator |
或者,如果要将内部对象存储在多维数组/对象结构中的不同级别上:
1 | class orderCollection implements RecursiveArrayIterator |
此解决方案将数组替换为"orderCollection"类型的对象,但目前为止不在IDE中启用代码完成功能。可以。下一步:
实现通过与DocBlocks接口引入的方法-特别是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
不要忘记使用类型提示:
1 2 | orderCollection::append(Order $order) orderCollection::offsetSet(Order $order) |
这个解决方案不再引入很多:
1 2 | /** @var $key ... */ /** @var $value ... */ |
所有的代码文件(例如在循环中),Zahymaka用她/他的答案证实了这一点。您的api用户不必强制引入那个docblock来完成代码。只有一个地方有@return会尽可能减少冗余(@var)。撒上"docblocks with@var"会使代码可读性最差。
最后你完成了。看起来很难实现?看起来像用大锤敲碎坚果?不真实,因为您熟悉这些接口和干净的代码。记住:您的源代码是一次性编写/多次读取的。
如果您的IDE的代码完成不适用于此方法,请切换到更好的方法(例如intellij-ide a、phpstorm、netbeans)或在您的IDE生产者的问题跟踪程序上提交功能请求。
感谢克里斯蒂安·韦斯(来自德国)作为我的教练,教我这么好的东西。附言:星上见我和他。
正如Danielawaranie在她的回答中提到的,当您迭代$collectionobject中的$items时,有一种方法可以指定$item的类型:将
繁荣!不需要在
我怀疑不是所有的IDE都支持它,但是它在phpstorm中工作得很好,这让我更快乐。
例子:
1 2 3 4 5 6 7 8 9 10 11 | Class MyCollection implements Countable, Iterator, ArrayAccess { /** * @return User */ public function current() { return $this->items[$this->cursor]; } //... implement rest of the required `interface` methods and your custom } |
我要加上什么有用的文章发表这个答案
在我的例子中,
这里有一个技巧:不要在抽象类中指定返回类型,而是在特定集合类的描述中使用phpdocinstruction
例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | Class User { function printLogin() { echo $this->login; } } Abstract Class MyCollection implements Countable, Iterator, ArrayAccess { protected $items = []; public function current() { return $this->items[$this->cursor]; } //... implement rest of the required `interface` methods and your custom //... abstract methods which will be shared among child-classes } /** * @method User current() * ...rest of methods (for ArrayAccess) if needed */ Class UserCollection extends MyCollection { function add(User $user) { $this->items[] = $user; } // User collection specific methods... } |
现在,类的用法:
1 2 3 4 5 6 7 8 9 | $collection = new UserCollection(); $collection->add(new User(1)); $collection->add(new User(2)); $collection->add(new User(3)); foreach ($collection as $user) { // IDE should `recognize` method `printLogin()` here! $user->printLogin(); } |
再一次:我怀疑不是所有的IDE都支持它,但是phpsorm支持。试试你的,发表评论结果!
在Netbeans 7.0中(也可能更低),您可以像
编辑:用@bob fanger suggestion更新示例
1 2 3 4 5 6 7 8 | /** * get all Tests * * @return Test|Array $tests */ public function getAllTexts(){ return array(new Test(), new Test()); } |
就用它吧:
1 2 3 4 5 6 7 | $tests = $controller->getAllTests(); //$tests-> //codehinting works! //$tests[0]-> //codehinting works! foreach($tests as $text){ //$test-> //codehinting works! } |
它不是完美的,但最好是让它只是"混合",这没有任何价值。
缺点是允许您将数组作为将引发错误的文本对象进行遍历。
在Zend工作室使用
在Zend工作室,
我知道我参加晚会迟到了,但我最近一直在解决这个问题。我希望有人看到这一点,因为接受的答案虽然正确,但不是最好的方法。至少在phpsorm中没有,不过我还没有测试过netbeans。
最好的方法是扩展arrayIterator类,而不是使用本机数组类型。这允许您在类级别而不是实例级别键入提示,这意味着您只需要phpDoc一次,而不是在整个代码中(这不仅混乱而且违反了dry,而且在重构时也会有问题-phpsorm在重构时有丢失phpDoc的习惯)
见下面的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | class MyObj { private $val; public function __construct($val) { $this->val = $val; } public function getter() { return $this->val; } } /** * @method MyObj current() */ class MyObjCollection extends ArrayIterator { public function __construct(Array $array = []) { foreach($array as $object) { if(!is_a($object, MyObj::class)) { throw new Exception('Invalid object passed to ' . __METHOD__ . ', expected type ' . MyObj::class); } } parent::__construct($array); } public function echoContents() { foreach($this as $key => $myObj) { echo $key . ': ' . $myObj->getter() . ''; } } } $myObjCollection = new MyObjCollection([ new MyObj(1), new MyObj('foo'), new MyObj('blah'), new MyObj(23), new MyObj(array()) ]); $myObjCollection->echoContents(); |
这里的关键是phpdoc
对我来说,这是实现这一点的最新方法(至少在PHP引入类型化数组之前,如果它们引入类型化数组的话),就像我们在可ITerable类中声明迭代器类型一样,而不是在分散在整个代码中的类的实例上。
这里我没有给出扩展arrayIterator的完整解决方案,因此如果使用此技术,您可能还需要:
- 包括其他需要的类级phpDoc,用于方法,如
offsetGet($index) 和next() 。 - 将健全性检查
is_a($object, MyObj::class) 从构造函数移到私有方法中 - 从方法重写(如
offsetSet($index, $newval) 和append($value) 调用此(现在是私有的)健全性检查
这是
personally,I have some时代以前的
1 2 3 4 5 6 | <?php foreach($this->models as /** @var Model_Object_WheelModel */ $model): ?> <?php // Type hinting now works: $model->getImage(); ?> <?php endforeach; ?> |
"我已经发现的东西这是工作,它可以拯救生命。 </P >
1 2 3 4 5 6 | private $userList = array(); $userList = User::fetchAll(); // now $userList is an array of User objects foreach ($userList as $user) { $user instanceof User; echo $user->getName(); } |