PHPDoctypes提示对象的数组?
所以,在PHPDoc中,可以在成员variables声明之上指定@var
来提示其types。 然后IDE,为前。 PHPEd,将知道它正在使用什么types的对象,并将能够提供该variables的代码洞察力。
<?php class Test { /** @var SomeObj */ private $someObjInstance; } ?>
这很好,直到我需要做一样的对象数组,以便能够得到适当的提示,当我迭代这些对象后来。
那么,有没有办法声明一个PHPDoc标签来指定成员variables是SomeObj
的数组? @var
数组是不够的, @var array(SomeObj)
似乎不是有效的,例如。
你可以做的最好的就是说,
foreach ($Objs as $Obj) { /* @var $Obj Test */ // You should be able to get hinting after the preceding line if you type $Obj-> }
我在Zend Studio中做了很多。 不知道其他编辑,但它应该工作。
在JetBrains的PhpStorm IDE中,可以使用/** @var SomeObj[] */
,例如:
/** * @return SomeObj[] */ function getSomeObjects() {...}
phpdoc文档推荐这个方法:
指定包含单个types,Type定义通知读者每个数组元素的types。 然后,只有一个types被期望作为给定数组的元素。
例如:
@return int[]
Netbeans暗示:
您可以在$users[0]->
上获得代码完成,为$this->
获得一个User类数组。
/** * @var User[] */ var $users = array();
当你完成$this->...
时,你也可以在类成员列表中看到数组的types$this->...
指定一个variables是一个对象数组:
$needles = getAllNeedles(); /* @var $needles Needle[] */ $needles[1]->... //codehinting works
这在Netbeans 7.2(我正在使用它)
也适用于:
$needles = getAllNeedles(); /* @var $needles Needle[] */ foreach ($needles as $needle) { $needle->... //codehinting works }
因此,在foreach
使用声明是没有必要的。
PSR-5:PHPDoc提出了一种generics风格的表示法。
句法
Type[] Type<Type> Type<Type[, Type]...> Type<Type[|Type]...>
集合中的值可能甚至是另一个数组,甚至另一个集合。
Type<Type<Type>> Type<Type<Type[, Type]...>> Type<Type<Type[|Type]...>>
例子
<?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·马丁(Robert C. Martin)的“清洁代码”(Clean Code)所述。 遵守他的信条时,您不应该要求开发人员(作为您的API的用户)了解您的arrays的(内部)结构。
API用户可能会问:这是一个只有一维的数组吗? 对象是否在multidimensional array的各个层次上传播? 我需要访问所有对象多less个嵌套循环(foreach等)? 什么types的对象被“存储”在该数组中?
正如你所概述的,你想使用该数组(包含对象)作为一维数组。
正如Nishi所述,您可以使用:
/** * @return SomeObj[] */
为了那个原因。
但是,请注意 – 这不是标准的docblock记法。 这个符号是由一些IDE生产者引入的。
好吧,好吧,作为一名开发人员,您知道“[]”绑定到PHP中的数组。 但是在正常的PHP环境中,“something []”意味着什么? “[]”是指:在“某物”内创build新的元素。 新元素可能是一切。 但是你想要expression的是:具有相同types的对象的数组,它是确切的types。 如您所见,IDE生产者引入了新的上下文。 一个新的环境,你必须学习。 其他PHP开发人员必须学习的新上下文(了解您的docblocks)。 不好的风格(!)。
因为你的数组有一个维度,所以你可能想把这个“数组对象”称为“列表”。 请注意,“列表”在其他编程语言中有非常特殊的含义。 例如,将其称为“集合”会更好。
请记住:您使用了一种编程语言,使您可以使用OOP的所有选项。 使用一个类而不是一个数组,让你的类可以像数组一样遍历。 例如:
class orderCollection implements ArrayIterator
或者,如果要将内部对象存储在multidimensional array/对象结构中的不同层上,请执行以下操作:
class orderCollection implements RecursiveArrayIterator
这个解决scheme用一个types为“orderCollection”的对象replace你的数组,但是到目前为止你的IDE中不要启用代码完成。 好的。 下一步:
用docblocks实现接口引入的方法 – 特别是:
/** * [...] * @return Order */ orderCollection::current() /** * [...] * @return integer Eg database identifier of the order */ orderCollection::key() /** * [...] * @return Order */ orderCollection::offsetGet()
不要忘记使用types提示:
orderCollection::append(Order $order) orderCollection::offsetSet(Order $order)
这个解决scheme停止引入了很多:
/** @var $key ... */ /** @var $value ... */
遍及你的代码文件(例如循环内),就像Zahymaka用她/他的答案所证实的那样。 您的API用户不会被迫引入该文件块,以便完成代码。 只有一个地方有@返回尽可能减less冗余(@var)。 洒“与@var docBlocks”会使你的代码最糟糕的可读性。
最后你完成了。 看起来很难达到? 看起来像拿大锤打破坚果? 不是真的,因为你熟悉那个接口和干净的代码。 记住:你的源代码被写入一次/阅读许多。
如果您的IDE的代码完成无法使用此方法,请切换到更好的(例如IntelliJ IDEA,PhpStorm,Netbeans)或在IDE生产者的问题跟踪器上提出function请求。
感谢Christian Weiss(来自德国)成为我的教练,教给我这么棒的东西。 PS:在XING见我和他。
在NetBeans 7.0中(也可能更低),您可以像@return Text
一样声明返回types“Text对象的数组”,代码提示将会工作:
编辑:更新与@Bob Fangerbuild议的例子
/** * get all Tests * * @return Test|Array $tests */ public function getAllTexts(){ return array(new Test(), new Test()); }
只是使用它:
$tests = $controller->getAllTests(); //$tests-> //codehinting works! //$tests[0]-> //codehinting works! foreach($tests as $text){ //$test-> //codehinting works! }
这不是完美的,但只是离开它只是“混合”,女巫没有带来任何价值。
缺点是你是被允许践踏arrays作为文本对象女巫会抛出错误。
正如DanielaWaranie在她的回答中提到的那样 – 在$ collectionObject中迭代$ items时,有一种方法可以指定$ item的types:将@return MyEntitiesClassName
添加到current()
以及其他返回值的Iterator
和ArrayAccess
方法。
繁荣! 没有必要在/** @var SomeObj[] $collectionObj */
foreach
,并且正确地使用集合对象,不需要使用特定方法返回集合,如@return SomeObj[]
。
我怀疑并不是所有的IDE都支持它,但是它在PhpStorm中工作得很好,这让我更开心。
例:
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 }
我将添加张贴这个答案什么有用的
在我的情况下, current()
和interface
方法的其余部分在Abstract
-collection类中实现,我不知道最终将在集合中存储什么样的实体。
所以这里有一个诀窍:不要在抽象类中指定返回types,而@method
在描述特定的集合类时使用PhpDoc的instuction @method
。
例:
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... }
现在,使用类:
$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都支持它,但是PhpStorm却支持它。 尝试你的,发表评论的结果!
在Zend Studio中使用array[type]
。
在Zend Studio中, array[MyClass]
或者array[int]
或者甚至array[array[MyClass]]
都很array[array[MyClass]]
。
问题是@var
只能表示一个types – 不包含复杂的公式。 如果你有一个“Foo数组”的语法,为什么要在那里停下来,而不是添加一个“arrays数组,包含2 Foo的和三个酒吧”的语法? 我明白,一个元素列表可能比这个更通用,但这是一个滑坡。
就我个人而言,我曾多次使用@var Foo[]
来表示“Foo的数组”,但IDE不支持它。
<?php foreach($this->models as /** @var Model_Object_WheelModel */ $model): ?> <?php // Type hinting now works: $model->getImage(); ?> <?php endforeach; ?>
我知道我迟到了,但最近我一直在解决这个问题。 我希望有人看到这个,因为接受的答案虽然是正确的,但并不是你能做到这一点的最好方法。 至less在PHPStorm中,我还没有testing过NetBeans。
最好的方法是扩展ArrayIterator类,而不是使用本地数组types。 这允许您在类级而不是实例级提示提示,这意味着您只需要PHPDoc一次,而不是在整个代码中(这不仅是凌乱和违反DRY,而且在涉及到时也可能有问题重构 – PHPStorm在重构时有一个缺lessPHPDoc的习惯)
见下面的代码:
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() . '<br>'; } } } $myObjCollection = new MyObjCollection([ new MyObj(1), new MyObj('foo'), new MyObj('blah'), new MyObj(23), new MyObj(array()) ]); $myObjCollection->echoContents();
这里的关键是PHPDoc @method MyObj current()
覆盖从ArrayIterator( mixed
)inheritance的返回types。 包含这个PHPDoc意味着当我们使用foreach($this as $myObj)
迭代类属性时,我们在引用variables$myObj->...
时获得代码完成$myObj->...
对我来说,这是实现这一点的最好方法(至less在PHP引入Typed Arrays之前,如果他们曾经这样做的话),因为我们在迭代类中声明了迭代器types,而不是遍布整个代码的类的实例。
我没有在这里展示扩展ArrayIterator的完整解决scheme,所以如果你使用这种技术,你可能还想:
- 对于像
offsetGet($index)
和next()
这样的方法,根据需要包含其他类级的PHPDoc。 - 从构造函数移动完整性检查
is_a($object, MyObj::class)
到一个私有方法 - 调用这个(现在私人)健全检查方法重写,如
offsetSet($index, $newval)
和append($value)
我发现了一些正在工作的东西,它可以挽救生命!
private $userList = array(); $userList = User::fetchAll(); // now $userList is an array of User objects foreach ($userList as $user) { $user instanceof User; echo $user->getName(); }