PHPDoc类型提示对象数组?


417

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

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

这非常有用,直到我以后需要遍历这些对象时才需要对一个对象数组进行相同操作才能获得正确的提示。

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


2
Netbeans 6.8开发人员博客中有一些参考资料,表明IDE现在已经足够聪明,可以推断出数组成员的类型:blogs.sun.com/netbeansphp/entry/php_templates_improved
John Carter,2009年

3
@therefromhere:您的链接已断开。我认为新的网址是:blogs.oracle.com/netbeansphp/entry/php_templates_improved
DanielaWaranie 2012年

对于像我这样的人,路过并寻找答案:如果您使用PHPStorm,请查看投票率最高的答案:它具有特定的提示!stackoverflow.com/a/1763425/1356098(例如,这并不意味着它应该是OP的答案,因为他要求的是PHPEd)
Erenor Paz

Answers:


337

采用:

/* @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中做了很多。不了解其他编辑器,但是应该可以。


3
这很有道理,但不适用于PHPEd 5.2。我唯一能想到的就是foreach($ Objs as / ** @var Test * / $ Obj),这太丑陋了。:(
Artem Russakovskii 09年

14
请注意,在Netbeans 7中,只有一个星号似乎很重要- /** @var $Obj Test */不起作用。
contrebis 2011年

3
@contrebis:“ @ var”是有效的文档块标签。因此,即使您的IDE在文档块“ / ** ... /”中不支持它,并且在“ / ... * /”中支持“ @var”,也请不要更改正确的文档块。向您的IDE的错误跟踪器提交问题,以使您的IDE符合标准。想象一下,您的开发团队/外部开发人员/社区使用不同的IDE。保持现状,为未来做好准备。
DanielaWaranie

181
确保您在下面看! 我几乎没有向下滚动-会是个大错误!!! 许多IDE将支持更好的语法!(提示:@var Object[] $objects说“ $ objects”是Object实例的数组。)
Thom Porter

10
/** @var TYPE $variable_name */是正确的语法;不要颠倒类型和变量名的顺序(如注释前面所建议的那样),因为这在所有情况下均不起作用。
srcspider

893

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

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

PHPDoc的文档建议使用此方法:

如果指定的类型包含单个类型,则Type定义会将每个数组元素的类型通知读者。然后,对于给定数组,仅预期将一个Type作为元素。

例: @return int[]


10
我刚刚下载并在过去一周内一直在使用phpstorm。击败Aptana(非常适合免费)。这正是我想要的。实际上,这与您为JavaScript
Juan Mendes 2010年

3
谢啦!这正是我想要的。PHPStorm非常棒。
Erik Schierboom 2011年

5
令我失望的是,这在Netbeans中不起作用。Jetbrains做一些很好的工具。
Keyo

10
@fishbone @Keyo现在可以在Netbeans中运行(至少在7.1夜间版本中,也许更早),尽管似乎您需要使用一个临时变量(一个错误?)。提示foreach(getSomeObjects() as $obj)不起作用,但提示有效$objs = getSomeObjects(); foreach($objs as $obj)
John Carter 2012年

2
@var Obj[string]对于关联数组会很好。
donquixote

59

Netbeans提示:

你得到的代码完成的$users[0]->$this->用户类的数组。

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

完成以下操作后,您还可以在类成员列表中查看数组的类型 $this->...


4
在PhpStorm 9 EAP中也可以使用:/ ** * @var UserInterface [] * / var $ users = []; //实现接口的Objs数组
Ronan

我已经试过了在NetBeans IDE 8.0.2,但是我从类建议,我目前是。
沃伊切赫Jasinski的

也可以在Eclipse 4.6.3中使用(idk引入了什么版本的支持,但是它的工作
原理

不幸的是,array_pop()由于某种原因,使用或类似功能后,此功能将无法使用。似乎Netbeans没有意识到那些函数返回输入数组的单个元素。
威廉W

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不必在中使用声明。


2
在我看来,此解决方案比公认的答案更干净,因为您可以多次使用foreach,并且/* @var $Obj Test */每次使用类型提示时都将继续使用新的注释。
亨利

我在这里看到两个问题:1.正确的phpdoc以/** 2.开头正确的格式是@var <data-type> <variable-name>
Christian

@Christian 1:主要问题不是phpdoc,而是键入提示2:即使根据其他答案,正确的格式也不像您说的那样。事实上,我发现您的评论有2个问题,我想知道为什么您要用正确的格式做出自己的回答
Highmastdon

1. Typehinting可与phpdoc一起使用...如果您不使用docblock,您的IDE将不会尝试猜测您在随机注释中写的内容。2.正确的格式,正如我在上面指定的其他答案一样;变量名之前的数据类型3.我没有写另一个答案,因为该问题不需要另一个答案,我宁愿不只是编辑您的代码。
基督徒

24

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

从我对这个问题的回答


通用标记法已从 PSR-5中删除
最早

11

我更喜欢阅读和编写简洁的代码-如Robert C. Martin在“简洁的代码”中所述。当遵循他的信条时,您不应该要求开发人员(以您的API用户身份)知道数组的(内部)结构。

API用户可能会问:这是只有一维的数组吗?对象是否散布在多维数组的所有层上?访问所有对象需要多少个嵌套循环(foreach等)?该阵列中“存储”了哪些类型的对象?

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

如Nishi所述,您可以使用:

/**
 * @return SomeObj[]
 */

为了那个原因。

但同样要注意:这不是标准的docblock表示法。这种表示法是由某些IDE生产商引入的。

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

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

请记住:您使用的编程语言可以启用OOP的所有选项。使用类而不是数组,使您的类像数组一样可遍历。例如:

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用户不会被强制引入该文档块来完成代码。仅在一个位置上返回@会尽可能减少冗余(@var)。撒上带有“ @var”的“ docBlocks”会使您的代码可读性最差。

最后,您完成了。看起来很难达到?看起来像是用大锤砸开螺母?并非如此,因为您熟悉该接口和简洁的代码。请记住:您的源代码只编写一次/读很多次。

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

感谢Christian Weiss(来自德国)成为我的培训师,并教给我这么棒的东西。PS:在XING上认识我和他。


这看起来像“正确”的方式,但是我无法使其与Netbeans一起使用。举一个小例子:imgur.com/fJ9Qsro
fehrlich 2013年

2
也许在2012年这不是“标准”,但是现在它被描述为phpDoc的内置功能。
Wirone 2014年

@Wirone看起来phpDocumentor将此添加到其手册中是对ide生产者的反应。即使您拥有广泛的工具支持,也并不意味着这是最佳实践。它开始使SomeObj []散布在越来越多的项目中,类似于几年前所做的require,require_once,include和include_once。使用自动加载时,该语句的外观下降到5%以下。希望SomeObj []在未来2年内下降到相同的速率,以支持上述方法。
DanielaWaranie 2014年

1
我不明白为什么?这是非常简单明了的符号。当您看到SomeObj[]它时,便知道它是一个二维SomeObj实例数组,然后您知道如何处理它。我认为它不会遵循“干净的代码”信条。
Wirone 2014年

这应该是答案。但是,并非所有@return <className>for current()和所有人都支持IDE 。PhpStorm支持,因此对我有很大帮助。谢了哥们!
帕维尔


5

在NetBeans 7.0(可能也更低)中,您可以声明返回类型“带有文本对象的数组”,就像这样@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允许您将数组作为Text Object踩,这将引发错误。


1
我使用“ @return array |测试一些描述”。这会触发相同的行为,但更具解释性。
Bob Fanger 2013年

1
这是一种解决方法,而不是解决方案。您在这里所说的是“此函数可能返回'Test'类型的对象或数组”。但是,从技术上讲,它不会告诉您有关阵列中可能包含的内容的任何信息。
拜森2015年

5

作为DanielaWaranie在她的答复中提到-有指定$项目类型的方式,当你遍历在$ $ collectionObject项目:添加@return MyEntitiesClassNamecurrent()休息的IteratorArrayAccess -方法,其返回值。

繁荣!无需全部/** @var SomeObj[] $collectionObj */结束foreach,并且可以正确地与collection对象一起使用,无需使用描述为的特定方法返回collection @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类中实现的,我不知道最终将哪种类型的实体存储在collection中。

因此,这里有个窍门:不要在抽象类中指定返回类型,而应@method在特定集合类的描述中使用PhpDoc指令。

例:

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支持。试试看,发表评论并发表评论!


券推那么远,但不幸的是我仍然可以解决自己专门的集合来代替良好的旧Java泛型类型....呸”
Sebas

谢谢。您如何键入提示静态方法?
Yevgeniy Afanasyev

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引入类型数组之前,如果有的话),因为我们是在可迭代类中声明迭代器类型,而不是在遍及代码的类实例中声明迭代器类型。

我没有在此处显示扩展ArrayIterator的完整解决方案,因此,如果您使用此技术,则可能还需要:

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

非常干净的解决方案!:)
MarkoŠutija19年

2

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

就个人而言,我曾经有一段时间用来@var Foo[]表示“一系列Foo”,但IDE并不支持它。


5
我最喜欢C / C ++的一件事是,它实际上将类型的跟踪保持在这一水平。滑倒那将是一个非常令人愉快的斜坡。
Brilliand

2
受Netbeans 7.2(至少是我使用的版本)支持,但有一点调整,即:/* @var $foo Foo[] */。刚刚在下面写了一个答案。也可以在foreach(){}循环内部使用
Highmastdon


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

11
唯一的问题是引入了要执行的其他代码,这些代码仅由您的IDE纯粹使用。最好在注释中定义类型提示。
本·罗维2010年

1
哇,效果很好。您最终将获得其他代码,但这似乎是无害的。我将开始做:$ x instanceof Y; // typehint
Igor Nadj

3
切换到一个可基于文档块或检查使代码完成的IDE。如果您不想切换IDE文件,请在IDE的问题跟踪器上进行功能请求。
DanielaWaranie 2012年

1
如果在类型不正确的情况下引发异常,则对于运行时类型检查很有用。如果...
lilbyrdie 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.