php 对象数组的 PHPDoc 类型提示?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/778564/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 23:49:00  来源:igfitidea点击:

PHPDoc type hinting for array of objects?

phpidephpdocvarhint

提问by Artem Russakovskii

So, in PHPDoc one can specify @varabove the member variable declaration to hint at its type. Then an IDE, for ex. PHPEd, will know what type of object it's working with and will be able to provide a code insight for that variable.

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

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

This works great until I need to do the same to an array of objects to be able to get a proper hint when I iterate through those objects later on.

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

So, is there a way to declare a PHPDoc tag to specify that the member variable is an array of SomeObjs? @vararray is not enough, and @var array(SomeObj)doesn't seem to be valid, for example.

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

采纳答案by Zahymaka

Use:

用:

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

when typehinting inline variables, and

对内联变量进行类型提示时,以及

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

for class properties.

对于类属性。

Previous answer from '09 when PHPDoc (and IDEs like Zend Studio and Netbeans) didn't have that option:

09 年 PHPDoc(以及 Zend Studio 和 Netbeans 等 IDE)没有该选项时的上一个答案:

The best you can do is say,

你能做的最好的事情就是说,

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

I do that a lot in Zend Studio. Don't know about other editors, but it ought to work.

我在 Zend Studio 中经常这样做。不知道其他编辑器,但它应该工作。

回答by Nishi

In the PhpStorm IDE from JetBrains, you can use /** @var SomeObj[] */, e.g.:

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

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

The phpdoc documentationrecommends this method:

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

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[]

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

例子: @return int[]

回答by user1491819

Netbeans hints:

Netbeans 提示:

You get code completion on $users[0]->and for $this->for an array of User classes.

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

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

You also can see the type of the array in a list of class members when you do completion of $this->...

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

回答by Highmastdon

To specify a variable is an array of objects:

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

$needles = getAllNeedles();
/* @var $needles Needle[] */
$needles[1]->...                        //codehinting works

This works in Netbeans 7.2 (I'm using it)

这适用于 Netbeans 7.2(我正在使用它)

Works also with:

也适用于:

$needles = getAllNeedles();
/* @var $needles Needle[] */
foreach ($needles as $needle) {
    $needle->...                        //codehinting works
}

Therefore use of declaration inside the foreachis not necessary.

因此foreach不需要在里面使用声明。

回答by Gerard Roche

PSR-5: PHPDocproposes a form of Generics-style notation.

PSR-5:PHPDoc提出了一种泛型风格的表示法。

Syntax

句法

Type[]
Type<Type>
Type<Type[, Type]...>
Type<Type[|Type]...>

Values in a Collection MAY even be another array and even another Collection.

集合中的值甚至可能是另一个数组甚至另一个集合。

Type<Type<Type>>
Type<Type<Type[, Type]...>>
Type<Type<Type[|Type]...>>

Examples

例子

<?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> */

Note: If you are expecting an IDE to do code assist then it's another question about if the IDE supports PHPDoc Generic-style collections notation.

注意:如果您希望 IDE 执行代码辅助,那么另一个问题是关于 IDE 是否支持 PHPDoc 通用样式集合表示法。

From my answer to this question.

从我对这个问题的回答来看。

回答by DanielaWaranie

I prefer to read and write clean code - as outlined in "Clean Code" by Robert C. Martin. When following his credo you should not require the developer (as user of your API) to know the (internal) structure of your array.

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

The API user may ask: Is that an array with one dimension only? Are the objects spread around on all levels of a multi dimensional array? How many nested loops (foreach, etc.) do i need to access all objects? What type of objects are "stored" in that array?

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

As you outlined you want to use that array (which contains objects) as a one dimensional array.

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

As outlined by Nishi you can use:

正如 Nishi 所概述的,您可以使用:

/**
 * @return SomeObj[]
 */

for that.

为了那个原因。

But again: be aware - this is not a standard docblock notation. This notation was introduced by some IDE producers.

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

Okay, okay, as a developer you know that "[]" is tied to an array in PHP. But what do a "something[]" mean in normal PHP context? "[]" means: create new element within "something". The new element could be everything. But what you want to express is: array of objects with the same type and it′s exact type. As you can see, the IDE producer introduces a new context. A new context you had to learn. A new context other PHP developers had to learn (to understand your docblocks). Bad style (!).

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

Because your array do have one dimension you maybe want to call that "array of objects" a "list". Be aware that "list" has a very special meaning in other programming languages. It would be mutch better to call it "collection" for example.

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

Remember: you use a programming language that enables you all options of OOP. Use a class instead of an array and make your class traversable like an array. E.g.:

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

class orderCollection implements ArrayIterator

Or if you want to store the internal objects on different levels within an multi dimensional array/object structure:

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

class orderCollection implements RecursiveArrayIterator

This solution replaces your array by an object of type "orderCollection", but do not enable code completion within your IDE so far. Okay. Next step:

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

Implement the methods that are introduced by the interface with docblocks - particular:

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

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

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

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

Do not forget to use type hinting for:

不要忘记使用类型提示:

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

This solution stops introducing a lot of:

这个解决方案不再引入很多:

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

all over your code files (e.g. within loops), as Zahymaka confirmed with her/his answer. Your API user is not forced to introduce that docblocks, to have code completion. To have @return on only one place reduces the redundancy (@var) as mutch as possible. Sprinkle "docBlocks with @var" would make your code worst readable.

正如 Zahymaka 用她/他的回答所证实的那样,遍及您的代码文件(例如在循环内)。您的 API 用户不会被迫引入该文档块来完成代码。只在一处使用 @return 会尽可能减少冗余 (@var)。撒上“带有@var 的docBlocks”会使您的代码可读性最差。

Finaly you are done. Looks hard to achive? Looks like taking a sledgehammer to crack a nut? Not realy, since you are familiar with that interfaces and with clean code. Remember: your source code is written once / read many.

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

If code completion of your IDE do not work with this approach, switch to a better one (e.g. IntelliJ IDEA, PhpStorm, Netbeans) or file a feature request on the issue tracker of your IDE producer.

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

Thanks to Christian Weiss (from Germany) for being my trainer and for teaching me such a great stuff. PS: Meet me and him on XING.

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

回答by d.raev

In NetBeans 7.0 (may be lower too) you could declare the the return type "array with Text objects " just as @return Textand the code hinting will work:

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

Edit:updated the example with @Bob Fanger suggestion

编辑:使用@Bob Fanger 建议更新示例

/**
 * get all Tests
 *
 * @return Test|Array $tests
 */
public function getAllTexts(){
    return array(new Test(), new Test());
}

and just use it:

只需使用它:

$tests =  $controller->getAllTests();
//$tests->         //codehinting works!
//$tests[0]->      //codehinting works!

foreach($tests as $text){
    //$test->      //codehinting works!
}

It is not perfect but it is better then just to leave it just "mixed", which brings no value.

它并不完美,但最好只是让它“混合”,这不会带来任何价值。

CONS is you are allowed to tread the array as Text Object which will throw errors.

缺点是您可以将数组视为会引发错误的文本对象。

回答by Erick Robertson

Use array[type]in Zend Studio.

array[type]在 Zend Studio 中使用。

In Zend Studio, array[MyClass]or array[int]or even array[array[MyClass]]work great.

在Zend Studio的,array[MyClass]或者array[int]甚至是array[array[MyClass]]工作的高度。

回答by Pavel

As DanielaWaranie mentioned in her answer - there is a way to specify the type of $item when you iterating over $items in $collectionObject: Add @return MyEntitiesClassNameto current()and rest of the Iteratorand ArrayAccess-methods which return values.

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

Boom!No need in /** @var SomeObj[] $collectionObj */over foreach, and works right with collection object, no need to return collection with specific method described as @return SomeObj[].

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

I suspect not all IDE support it but it works perfectly fine in PhpStorm, which makes me happier.

我怀疑并非所有 IDE 都支持它,但它在 PhpStorm 中运行良好,这让我更开心。

Example:

例子:

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
}

What useful i was going to add posting this answer

我要添加发布此答案有什么用

In my case current()and rest of interface-methods are implemented in Abstract-collection class and I do not know what kind of entities will eventually be stored in collection.

在我的例子中current(),其余的interface-methods 是在Abstract-collection 类中实现的,我不知道最终会在集合中存储什么样的实体。

So here is the trick: Do not specify return type in abstract class, instead use PhpDoc instuction @methodin description of specific collection class.

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

Example:

例子:

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

}

Now, usage of classes:

现在,类的使用:

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

Once again: I suspect not all IDE support it, but PhpStorm does. Try yours, post in comment the results!

再一次:我怀疑并非所有 IDE 都支持它,但 PhpStorm 支持。试试你的,在评论中发布结果!

回答by e_i_pi

I know I'm late to the party, but I've been working on this problem recently. I hope someone sees this because the accepted answer, although correct, is notthe best way you can do this. Not in PHPStorm at least, I haven't tested NetBeans though.

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

The best way involves extending the ArrayIterator class rather than using native array types. This allows you to type hint at a class-level rather than at an instance-level, meaning you only have to PHPDoc once, not throughout your code (which is not only messy and violates DRY, but can also be problematic when it comes to refactoring - PHPStorm has a habit of missing PHPDoc when refactoring)

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

See code below:

见下面的代码:

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

The key here is the PHPDoc @method MyObj current()overriding the return type inherited from ArrayIterator (which is mixed). The inclusion of this PHPDoc means that when we iterate over the class properties using foreach($this as $myObj), we then get code completion when referring to the variable $myObj->...

这里的关键是 PHPDoc@method MyObj current()覆盖了从 ArrayIterator 继承的返回类型(即mixed)。包含此 PHPDoc 意味着当我们使用 迭代类属性时foreach($this as $myObj),我们会在引用变量时获得代码完成$myObj->...

To me, this is the neatest way to achieve this (at least until PHP introduces Typed Arrays, if they ever do), as we're declaring the iterator type in the iterable class, not on instances of the class scattered throughout the code.

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

I haven't shown here the complete solution for extending ArrayIterator, so if you use this technique, you may also want to:

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

  • Include other class-level PHPDoc as required, for methods such as offsetGet($index)and next()
  • Move the sanity check is_a($object, MyObj::class)from the constructor into a private method
  • Call this (now private) sanity check from method overrides such as offsetSet($index, $newval)and append($value)
  • 根据需要包含其他类级别的 PHPDoc,用于诸如offsetGet($index)next()
  • 将健全性检查is_a($object, MyObj::class)从构造函数移到私有方法中
  • 从方法覆盖调用此(现在是私有的)健全性检查,例如offsetSet($index, $newval)append($value)