PHPDoc类型提示对象数组?

时间:2009-04-22 18:29:13

标签: php ide phpdoc var hint

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

<?php
  class Test
  {
    /** @var SomeObj */
    private $someObjInstance;
  }
?>

这很有效,直到我需要对一个对象数组做同样的操作才能在稍后迭代这些对象时获得正确的提示。

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

13 个答案:

答案 0 :(得分:877)

在JetBrains的PhpStorm IDE中,您可以使用/** @var SomeObj[] */,例如:

/**
 * @return SomeObj[]
 */
function getSomeObjects() {...}

phpdoc documentation推荐使用此方法:

  

指定包含单个类型,Type定义通知读者每个数组元素的类型。然后只需要一个Type作为给定数组的元素。

     

示例:@return int[]

答案 1 :(得分:296)

使用:

/* @var $objs Test[] */
foreach ($objs as $obj) {
    // Typehinting will occur after typing $obj->
}

当键入内联变量时,

class A {
    /** @var Test[] */
    private $items;
}

用于类属性。

从09年PHPDoc(以及像Zend Studio和Netbeans这样的IDE)没有这个选项时的回答:

你能做的最好就是说,

foreach ($Objs as $Obj)
{
    /* @var $Obj Test */
    // You should be able to get hinting after the preceding line if you type $Obj->
}

我在Zend Studio中做了很多。不知道其他编辑,但它应该工作。

答案 2 :(得分:57)

Netbeans提示:

对于一组User类,您可以在$users[0]->$this->上获得代码。

/**
 * @var User[]
 */
var $users = array();

完成$this->...

后,您还可以在班级成员列表中看到数组的类型

答案 3 :(得分:29)

指定变量是一个对象数组:

$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内使用声明。

答案 4 :(得分:19)

PSR-5: PHPDoc提出了一种泛型风格的符号。

语法

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通用样式集合表示法的另一个问题。

从我对this question的回答。

答案 5 :(得分:12)

我更喜欢阅读和编写干净的代码 - 正如Robert C. Martin在“清洁代码”中所述。 遵循他的信条,您不应要求开发人员(作为您的API的用户)知道您的阵列的(内部)结构。

API用户可能会问:这是一个只有一个维度的数组吗?对象是否在多维数组的所有级别上展开?我需要访问所有对象多少嵌套循环(foreach等)?在该数组中“存储”了什么类型的对象?

如您所述,您希望将该数组(包含对象)用作一维数组。

如Nishi所述,您可以使用:

/**
 * @return SomeObj[]
 */

为此。

但是再次:请注意 - 这不是标准的docblock表示法。这种表示法是由一些IDE生产者引入的。

好的,好吧,作为开发人员,你知道“[]”与PHP中的数组相关联。但在普通的PHP上下文中,“某些东西[]”是什么意思呢? “[]”表示:在“某事物”中创建新元素。新元素可能就是一切。但是你要表达的是:具有相同类型和确切类型的对象数组。如您所见,IDE生产者引入了新的上下文。您必须学习的新环境。其他PHP开发人员必须学习的新上下文(了解您的docblock)。糟糕的风格(!)。

因为您的数组确实有一个维度,您可能希望将“对象数组”称为“列表”。请注意,“list”在其他编程语言中具有非常特殊的含义。例如,将它称为“集合”会更好。

请记住:您使用的编程语言可以为您提供OOP的所有选项。 使用类而不是数组,使类可以像数组一样遍历。 E.g:

class orderCollection implements ArrayIterator

或者,如果要将内部对象存储在多维数组/对象结构中的不同级别上:

class orderCollection implements RecursiveArrayIterator

此解决方案用“orderCollection”类型的对象替换您的数组,但到目前为止还没有在IDE中启用代码完成。好的。下一步:

实现具有docblocks的接口引入的方法 - 特别是:

/**
 * [...]
 * @return Order
 */
orderCollection::current()

/**
 * [...]
 * @return integer E.g. database identifier of the order
 */
orderCollection::key()

/**
 * [...]
 * @return Order
 */
orderCollection::offsetGet()

不要忘记使用类型提示:

orderCollection::append(Order $order)
orderCollection::offsetSet(Order $order)

此解决方案停止引入大量内容:

/** @var $key ... */
/** @var $value ... */

遍布你的代码文件(例如在循环内),正如Zahymaka用她/他的答案确认的那样。您的API用户不必强制引入该docblock,以完成代码。仅在一个位置上使用@return会将冗余(@var)减少为尽可能多的变量。使用@var“撒上”docBlocks会使您的代码更难读。

最终你完成了。看起来难以实现?看起来像拿大锤来破解坚果?不是真的,因为你熟悉那些接口和干净的代码。请记住:您的源代码是一次写入/多次读取。

如果IDE的代码完成无法使用此方法,请切换到更好的方法(例如IntelliJ IDEA,PhpStorm,Netbeans)或在IDE生产者的问题跟踪器上提交功能请求。

感谢Christian Weiss(来自德国)成为我的教练并教我如此出色的东西。 PS:在XING上和我见面。

答案 6 :(得分:5)

在NetBeans 7.0中(也可能更低),您可以将返回类型“带有Text对象的数组”声明为@return Text,代码提示将起作用:

修改:使用@Bob Fanger建议更新了示例

/**
 * 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!
}

它并不完美,但它只是让它只是“混合”,这没有任何价值。

CONS是允许你将数组作为文本对象进行操作,这会引发错误。

答案 7 :(得分:5)

正如DanielaWaranie在她的回答中提到的 - 当你在$ collectionObject中迭代$ items时,有一种方法可以指定$ item的类型:将@return MyEntitiesClassName添加到current()以及{{1}的其余部分}和Iterator - 返回值的方法。

繁荣! ArrayAccess不需要/** @var SomeObj[] $collectionObj */,并且可以使用集合对象,无需使用描述为foreach的特定方法返回集合。

我怀疑不是所有的IDE都支持它,但它在PhpStorm中运行得非常好,这让我更开心。

示例:

@return SomeObj[]

我将添加发布此答案的有用信息

在我的情况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 - 集合类中实现,我不知道哪种实体最终会存储在集合中。

所以这就是诀窍:不要在抽象类中指定返回类型,而是在特定集合类的描述中使用PhpDoc instuction Abstract

示例:

@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...

}

再一次:我怀疑不是所有的IDE都支持它,但PhpStorm却支持它。试试你的,发表评论结果!

答案 8 :(得分:4)

在Zend Studio中使用array[type]

在Zend Studio中,array[MyClass]array[int]甚至array[array[MyClass]]效果很好。

答案 9 :(得分:3)

我知道我迟到了,但最近我一直在研究这个问题。我希望有人看到这个,因为接受的答案虽然正确,但是你能做到这一点的最好方法。至少在PHPStorm中,我还没有测试过NetBeans。

最好的方法是扩展ArrayIterator类,而不是使用本机数组类型。这允许您在类级别而不是在实例级别键入提示,这意味着您只需要PHPDoc一次,而不是整个代码(这不仅是凌乱而且违反DRY,但在涉及到时也可能有问题重构 - PHPStorm在重构时有丢失PHPDoc的习惯)

见下面的代码:

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)。包含这个PHPDoc意味着当我们使用foreach($this as $myObj)迭代类属性时,我们在引用变量$myObj->...时获得代码完成

对我来说,这是实现这一目标的最好方法(至少在PHP引入Typed Arrays之前,如果他们这样做的话),因为我们在可迭代类中声明了迭代器类型,而不是在整个类中散布的类的实例代码。

我没有在这里展示扩展ArrayIterator的完整解决方案,所以如果你使用这种技术,你可能还想:

  • 根据需要包含其他类级别的PHPDoc,用于offsetGet($index)next()等方法
  • 将完整性检查is_a($object, MyObj::class)从构造函数移动到私有方法
  • offsetSet($index, $newval)append($value)
  • 等方法覆盖中调用此(现在是私有的)健全性检查

答案 10 :(得分:2)

问题是@var只能表示一种类型 - 不包含复杂的公式。如果你有一个“数组Foo”的语法,为什么停在那里而不添加“数组数组,包含2个Foo和3个Bar”的语法?我知道元素列表可能比这更通用,但它是一个滑坡。

就个人而言,我有时会使用@var Foo[]来表示“Foo的数组”,但IDE并不支持它。

答案 11 :(得分:1)

<?php foreach($this->models as /** @var Model_Object_WheelModel */ $model): ?>
    <?php
    // Type hinting now works:
    $model->getImage();
    ?>
<?php endforeach; ?>

答案 12 :(得分:-5)

我发现了一些有效的东西,它可以拯救生命!

private $userList = array();
$userList = User::fetchAll(); // now $userList is an array of User objects
foreach ($userList as $user) {
   $user instanceof User;
   echo $user->getName();
}