【问题标题】:PHPDoc type hinting for array of objects?对象数组的PHPDoc类型提示?
【发布时间】:2010-10-21 04:24:44
【问题描述】:

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

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

这很好用,直到我需要对一组对象执行相同的操作才能在以后遍历这些对象时获得正确的提示。

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

【问题讨论】:

标签: php ide phpdoc var hint


【解决方案1】:

如果您使用 PHPStorm 2021.2+,您也可以使用以下语法(数组形状):

@property array{name: string, content: string}[] $files

@var array{name: string, content: string}[] $files

【讨论】:

    【解决方案2】:

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

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

    phpdoc documentation 推荐这种方法:

    指定包含单个类型,类型定义通知读者每个数组元素的类型。给定数组的元素只有一个类型。

    示例:@return int[]

    【讨论】:

    • 我刚刚下载并在过去一周一直在使用 phpstorm。击败 Aptana(这对免费来说非常棒)。这正是我一直在寻找的。实际上,这和你对 JavaScript 做的方式一样,我应该猜到了
    • 这在 Netbeans 中不起作用,我很失望。 Jetbrains 制作了一些非常不错的工具。
    • 我们可以使用/** @var SomeObj[]|array */ 使注解与NetBeans 兼容吗?
    • @fishbone @Keyo 现在可以在 Netbeans 中使用(至少在 7.1 nightly build 中,可能更早),尽管您似乎需要使用临时变量(错误?)。提示 foreach(getSomeObjects() as $obj) 不起作用,但它适用于 $objs = getSomeObjects(); foreach($objs as $obj)
    • 如果有 @var Obj[string] 用于关联数组会很好。
    【解决方案3】:

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

    Boom! 不需要在/** @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 类中实现,我不知道最终会在集合中存储什么样的实体。

    所以这里的诀窍是:不要在抽象类中指定返回类型,而是在特定集合类的描述中使用 PhpDoc 指令@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 支持。试试你的,在评论中发布结果!

    【讨论】:

    • 将它推到那么远的凭证,但不幸的是,我仍然可以决定自己专门收集一个集合来替换好的旧 java 泛型类型......呸'
    • 谢谢。如何输入提示静态方法?
    【解决方案4】:

    用途:

    /* @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 中经常这样做。不知道其他编辑器,但它应该可以工作。

    【讨论】:

    • 这是有道理的,但它不适用于 PHPEd 5.2。我能想到的唯一可行的方法是 foreach ($Objs as /** @var Test */$Obj),这非常丑陋。 :(
    • 这在 NetBeans 6.7 中有效(我认为它有问题,因为当您点击 ctrl-space 时您会得到一个 ? 类型,但它能够自动完成对象的成员/方法)。
    • 注意在 Netbeans 7 中,你只有一个星号似乎很重要——/** @var $Obj Test */ 不起作用。
    • @contrebis:“@var”是一个有效的文档块标签。因此,即使您的 IDE 在 docblock "/** ... /" 中不支持它并且仅在 "/ ...*/" 中支持 "@var" - 请,请不要更改正确的文档块。向 IDE 的错误跟踪器提交问题,以使您的 IDE 符合标准。想象一下您的开发团队/外部开发人员/社区使用不同的 IDE。保持现状,为未来做好准备。
    • /** @var TYPE $variable_name */ 是正确的语法;不要颠倒类型和变量名的顺序(如前面在 cmets 中建议的那样),因为这在所有情况下都不起作用。
    【解决方案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!
    }
    

    它并不完美,但总比让它“混合”更好,这没有任何价值。

    缺点是您可以将数组作为文本对象进行处理,这将引发错误。

    【讨论】:

    • 我使用“@return array|Test Some description”。这会触发相同的行为,但更具解释性。
    • 这是一个解决方法,而不是解决方案。你在这里说的是“这个函数可能返回一个'Test'类型的对象,或者一个数组”。但是,从技术上讲,它并没有告诉您有关数组中可能包含什么的任何信息。
    【解决方案6】:

    我知道我迟到了,但我最近一直在解决这个问题。我希望有人看到这一点,因为接受的答案虽然正确,但 不是 你可以做到这一点的最佳方式。至少不在 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-&gt;... 时获得代码完成

    对我来说,这是实现这一点的最巧妙的方法(至少在 PHP 引入类型化数组之前,如果他们曾经这样做的话),因为我们在可迭代类中声明迭代器类型,而不是在散布在各处的类的实例上声明代码。

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

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

    【讨论】:

    • 非常好的和干净的解决方案! :)
    【解决方案7】:

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

    来自我对this question的回答。

    【讨论】:

    【解决方案8】:

    Netbeans 提示:

    您可以在 $users[0]-&gt;$this-&gt; 上获得用户类数组的代码完成。

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

    当你完成$this-&gt;...时,你还可以在类成员列表中看到数组的类型

    【讨论】:

    • 也适用于 PhpStorm 9 EAP: /** * @var UserInterface[] */ var $users = []; // 实现接口的 Objs 数组
    • 我已经在 NetBeans IDE 8.0.2 中尝试过,但我从我目前所在的课程中得到了建议。
    • 也可以在 Eclipse 4.6.3 中使用(idk 引入了什么版本支持,但它的工作原理,以及我现在正在使用的)
    • 很遗憾,由于某种原因,在使用array_pop() 或类似功能后,这不起作用。似乎 Netbeans 没有意识到这些函数返回输入数组的单个元素。
    【解决方案9】:

    在 Zend Studio 中使用 array[type]

    在 Zend Studio 中,array[MyClass]array[int] 甚至 array[array[MyClass]] 工作得很好。

    【讨论】:

      【解决方案10】:

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

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

      【讨论】:

      • 在我看来,这个解决方案比公认的答案更干净,因为您可以多次使用 foreach 并且类型提示将继续工作,而无需每次都使用新的 /* @var $Obj Test */ 注释。
      • 我在这里看到两个问题:1. 正确的 phpdoc 以 /** 开头 2. 正确的格式是 @var &lt;data-type&gt; &lt;variable-name&gt;
      • @Christian 1:主要问题不是 phpdoc,而是 typehinting 2:正确的格式不像你说的那样,即使根据其他答案。事实上,我看到您的评论存在 2 个问题,我想知道您为什么要以正确的格式做出自己的答案
      • 1. 类型提示适用于 phpdoc...如果您不使用 docblock,您的 IDE 将不会尝试猜测您在一些随机注释中写的内容。 2. 正确的格式,正如其他一些答案也所说的是我上面指定的; 变量名前的数据类型3. 我没有写另一个答案,因为这个问题不需要另一个答案,我宁愿不只是编辑你的代码。
      • 虽然这可行,但自动完成(输入 /**&lt;space&gt; 并将扩展以包含下一个变量名)需要变量名之前的类型,因此 /** @var Needle[] $needles */ (PHPStorm 2021.1)
      【解决方案11】:

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

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

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

      如 Nishi 所述,您可以使用:

      /**
       * @return SomeObj[]
       */
      

      为此。

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

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

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

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

      class orderCollection implements ArrayIterator
      

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

      class orderCollection implements RecursiveArrayIterator
      

      此解决方案将您的数组替换为“orderCollection”类型的对象,但目前尚未在您的 IDE 中启用代码完成功能。好的。下一步:

      使用文档块实现接口引入的方法 - 特别是:

      /**
       * [...]
       * @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 用户不必引入该文档块来完成代码。让@return 只在一个地方尽可能减少冗余(@var)。使用“带有@var 的docBlocks”会使您的代码可读性变差。

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

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

      感谢 Christian Weiss(来自德国)担任我的教练并教给我这么棒的东西。 PS:在 XING 上认识我和他。

      【讨论】:

      • 这看起来像“正确”的方式,但我不能让它与 Netbeans 一起工作。做了一个小例子:imgur.com/fJ9Qsro
      • 也许在 2012 年这不是一个标准,但 now 它被描述为 phpDoc 的内置功能。
      • @Wirone 看起来 phpDocumentor 将此添加到其手册中,作为对 ide 生产者的反应。即使您拥有广泛的工具支持,但这并不意味着它是最佳实践。它开始在越来越多的项目中传播 SomeObj[],类似于几年前的 require、require_once、include 和 include_once。随着自动加载该语句的外观下降到 5% 以下。希望 SomeObj[] 在未来 2 年内下降到相同的速度,以支持上述方法。
      • 我不明白为什么?这是非常简单明了的符号。当您看到SomeObj[] 时,您知道它是SomeObj 实例的二维数组,然后您就知道如何处理它了。我不认为它不遵循“干净代码”的信条。
      • 这应该是答案。不过,并非所有 IDE 都支持 @return &lt;className&gt;current() 的所有方法。 PhpStorm 支持,所以它帮助了我很多。谢谢老哥!
      【解决方案12】:
      <?php foreach($this->models as /** @var Model_Object_WheelModel */ $model): ?>
          <?php
          // Type hinting now works:
          $model->getImage();
          ?>
      <?php endforeach; ?>
      

      【讨论】:

      【解决方案13】:

      我发现了一些有效的方法,它可以挽救生命!

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

      【讨论】:

      • 唯一的问题是引入了额外的代码来执行,这些代码仅供您的 IDE 使用。最好在 cmets 中定义类型提示。
      • 哇,这很好用。你最终会得到额外的代码,但它似乎是无害的。我要开始做: $x instanceof Y; // 类型提示
      • 切换到基于文档块或检查为您提供代码完成的 IDE。如果您不想切换 IDE 文件,请在 IDE 的问题跟踪器上请求功能。
      • 如果在类型不正确的情况下引发异常,则它可用于运行时类型检查。如果...
      【解决方案14】:

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

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

      【讨论】:

      • 我喜欢 C/C++ 的一件事是它实际上将类型跟踪到这个级别。那将是一个非常令人愉快的斜坡。
      • 受 Netbeans 7.2 支持(至少我使用的是那个版本),但稍作调整:/* @var $foo Foo[] */。刚刚在下面写了一个答案。这也可以在foreach(){} 循环中使用
      猜你喜欢
      • 2016-08-30
      • 2016-10-18
      • 2017-09-09
      • 2023-04-08
      • 2013-12-30
      • 2014-01-12
      • 2012-11-18
      • 2018-01-20
      相关资源
      最近更新 更多