ChatGPT解决这个技术问题 Extra ChatGPT

对象数组的PHPDoc类型提示?

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

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

这很有效,直到我需要对一组对象做同样的事情,以便以后在遍历这些对象时能够获得正确的提示。

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

在这个 Netbeans 6.8 开发博客中有一些参考资料表明 IDE 现在已经足够智能,可以推断出数组成员的类型:blogs.sun.com/netbeansphp/entry/php_templates_improved
@therefromhere:您的链接已损坏。我认为新网址是:blogs.oracle.com/netbeansphp/entry/php_templates_improved

P
Paul

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

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

phpdoc documentation 推荐这种方法:

指定包含单个类型,类型定义通知读者每个数组元素的类型。然后,只期望一个 Type 作为给定数组的元素。示例:@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] 用于关联数组会很好。
Z
Zahymaka

利用:

/* @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”是一个有效的 docblock 标签。因此,即使您的 IDE 在 docblock "/** ... /" 中不支持它并且仅在 "/ ...*/" 中支持 "@var" - 请不要更改正确的 docblock。向 IDE 的错误跟踪器提交问题,以使您的 IDE 符合标准。想象一下您的开发团队/外部开发人员/社区使用不同的 IDE。保持现状,为未来做好准备。
/** @var TYPE $variable_name */ 是正确的语法;不要颠倒类型和变量名的顺序(如评论前面所建议的那样),因为这在所有情况下都不起作用。
p
pmaruszczyk

Netbeans 提示:

您在 $users[0]->$this-> 上为一组用户类获得代码完成。

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

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


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

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

$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 <data-type> <variable-name>
@Christian 1:主要问题不是 phpdoc,而是 typehinting 2:正确的格式不像你说的那样,即使根据其他答案。事实上,我看到你的评论有 2 个问题,我想知道你为什么要用正确的格式做出自己的答案
1. Typehinting 与 phpdoc 一起工作...如果您不使用 docblock,您的 IDE 将不会尝试猜测您在一些随机注释中写的内容。 2.正确的格式,正如其他一些答案所说的那样,是我上面指定的;变量名之前的数据类型。 3.我没有写另一个答案,因为这个问题不需要另一个答案,我宁愿不只是编辑你的代码。
虽然这可行,但自动完成(类型 /**<space> 并将扩展以包含下一个变量名称)需要变量名称之前的类型,因此 /** @var Needle[] $needles */ (PHPStorm 2021.1)
C
Community

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 的回答。


通用符号 was removed from PSR-5
D
DanielaWaranie

我更喜欢阅读和编写干净的代码 - 正如 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 中启用代码完成。好的。下一步:

使用 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 用户不必引入该文档块来完成代码。让@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 <className>current() 的所有方法。 PhpStorm 支持,所以它帮助了我很多。谢了哥们!
r
reformed

在 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 一些描述”。这会触发相同的行为,但更具解释性。
这是一种解决方法,而不是解决方案。你在这里说的是“这个函数可能返回一个'Test'类型的对象,或者一个数组”。但是,从技术上讲,它并没有告诉您有关数组中可能包含什么的任何信息。
E
Erick Robertson

在 Zend Studio 中使用 array[type]

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


j
jgmjgm

正如 DanielaWaranie 在她的回答中提到的那样 - 当您迭代 $collectionObject 中的 $items 时,有一种方法可以指定 $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 泛型类型.... 呸'
谢谢你。你如何输入一个静态方法?
M
Max

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

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

或者

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

e
e_i_pi

我知道我迟到了,但我最近一直在解决这个问题。我希望有人看到这一点,因为接受的答案虽然正确,但并不是您可以做到这一点的最佳方式。至少不在 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) 等方法覆盖调用这个(现在是私有的)完整性检查


t
troelskn

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

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


我喜欢 C/C++ 的一件事是它实际上将类型跟踪到这个级别。那将是一个非常愉快的斜坡滑下。
Netbeans 7.2 支持(至少这是我使用的版本),但稍作调整:/* @var $foo Foo[] */。刚刚在下面写了一个答案。这也可以在 foreach(){} 循环中使用
S
Scott Hovestadt
<?php foreach($this->models as /** @var Model_Object_WheelModel */ $model): ?>
    <?php
    // Type hinting now works:
    $model->getImage();
    ?>
<?php endforeach; ?>

这是非常丑陋的。当你开始这样编程时,告别干净的代码。
而是通过定义数组的内容来查看我的答案:stackoverflow.com/a/14110784/431967
e
eupho

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

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 使用。最好在注释中定义类型提示。
哇,这很好用。你最终会得到额外的代码,但它似乎是无害的。我要开始做: $x instanceof Y; // 类型提示
切换到基于 docblocks 或检查为您提供代码完成的 IDE。如果您不想切换 IDE 文件,请在 IDE 的问题跟踪器上请求功能。
如果在类型不正确的情况下引发异常,则它对于运行时类型检查很有用。如果...