关于php:PHPDoc类型提示对象数组?

PHPDoc type hinting for array of objects?

因此,在phpdoc中,可以在成员变量声明上方指定@var,以提示其类型。然后,一个IDE,例如phped,将知道它使用的对象类型,并能够为该变量提供代码洞察。

1
2
3
4
5
6
7
<?php
  class Test
  {
    /** @var SomeObj */
    private $someObjInstance;
  }
?>

这非常有效,直到我需要对对象数组执行相同的操作,以便在稍后迭代这些对象时能够获得适当的提示。

那么,是否有一种方法来声明phpdoc标记,以指定成员变量是SomeObj的数组?例如,@var数组不够,@var array(SomeObj)似乎无效。


在phpstorm jetbrains IDE中,你可以使用/** @var SomeObj[] */,例如: </P >

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提示:

您可以在$users[0]->$this->上完成一系列用户类的代码。

1
2
3
4
/**
 * @var User[]
 */

var $users = array();

完成$this->...时,还可以在类成员列表中看到数组的类型。


要指定变量,请使用对象数组:

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
}

因此,不需要在foreach中使用声明。


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
/**
 * [...]
 * @return Order
 */

orderCollection::current()

/**
 * [...]
 * @return integer E.g. database identifier of the order
 */

orderCollection::key()

/**
 * [...]
 * @return Order
 */

orderCollection::offsetGet()

不要忘记使用类型提示:

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的类型:将@return MyEntitiesClassName添加到current()中,并将返回值的IteratorArrayAccess方法的其余部分添加。

繁荣!不需要在foreach上使用/** @var SomeObj[] $collectionObj */,与收款对象配合使用,不需要使用@return SomeObj[]所述的具体方法返回收款。

我怀疑不是所有的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
}

我要加上什么有用的文章发表这个答案

在我的例子中,current()interface的其余方法是在Abstract集合类中实现的,我不知道最终将在集合中存储哪种实体。

这里有一个技巧:不要在抽象类中指定返回类型,而是在特定集合类的描述中使用phpdocinstruction@method

例子:

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中(也可能更低),您可以像@return Text那样声明返回类型"array with text objects",并且代码提示将起作用:

编辑:用@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工作室使用array[type]

在Zend工作室,array[MyClass]array[int]甚至array[array[MyClass]]都非常好。


我知道我参加晚会迟到了,但我最近一直在解决这个问题。我希望有人看到这一点,因为接受的答案虽然正确,但不是最好的方法。至少在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 @method MyObj current(),它覆盖从arrayIterator继承的返回类型(即mixed)。这个phpDoc的包含意味着当我们使用foreach($this as $myObj)迭代类属性时,当引用变量$myObj->...时,我们得到代码完成。

对我来说,这是实现这一点的最新方法(至少在PHP引入类型化数组之前,如果它们引入类型化数组的话),就像我们在可ITerable类中声明迭代器类型一样,而不是在分散在整个代码中的类的实例上。

这里我没有给出扩展arrayIterator的完整解决方案,因此如果使用此技术,您可能还需要:

  • 包括其他需要的类级phpDoc,用于方法,如offsetGet($index)next()
  • 将健全性检查is_a($object, MyObj::class)从构造函数移到私有方法中
  • 从方法重写(如offsetSet($index, $newval)append($value)调用此(现在是私有的)健全性检查


这是@var问题可以是一个单一的denote型不包含一个复杂的公式。如果你有一个语法的方法"阵列foo",为什么不停止,有和没有添加一个语法的方法"的阵列的阵列,这包含2 foo"和"三杆"吗?我明白一个列表的元素是一般性的,严格讲来或许全更多的比说的,但它是一个滑滑的斜坡。 </P >

personally,I have some时代以前的@var Foo[]signify"阵列"foo",但它的SUPPORTED用IDE"页。 </P >


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();
}