处理对象数组和智能感知

Handling array of object and intellisense

我发现很多时候我将自定义对象数组传递给函数进行处理。在我的函数中,我在访问内部 properties/methods.

之前检查以确保它是正确的 class

因为我正在使用一个数组,据我所知 PHP 没有任何类型化的通用列表,所以我不能使用类型提示,所以我的 IDE 中的智能感知不起作用并且代码检查抛出警告。

我遇到了一个旧的 post,它给出了加入这行代码的想法,以便在开发时让智能感知工作:

if (false) $myObj = new MyObject();

所以我最终得到了一个看起来像这样的函数:

function ProcessObjectArray(array $arrayOfObject)
{
    foreach ($arrayOfObject as $key => $myObj) {
        if (get_class($myObj) == 'MyNamespace\MyObject') {
            if (false) $myObj = new MyObject();
            // I can now access intellisense for MyObject in here

        } else {
            trigger_error('is not right object');
        }
    }
}

这似乎有点奇怪,所以我想知道这是否表明我没有以最佳方式处理对象数组,或者是否有更好的方式让我的智能感知工作。我查看了 arrayobject 接口,看看是否可以创建一个 class 实现 arrayobject 来保存类型化列表。虽然将所有验证放在其构造函数中或附加类似函数非常好,但我无法使用智能感知,因为内部容器仍然只是一个标准数组。同样使用 get_class 似乎不太好,因为如果重命名 class 名称或命名空间,那么 IDE 重构功能不会选择此引用(好吧,PHPStorm 不会至少,我正在使用它)。

我当然不需要 intellisense,但这个 hack 的怪异让我想知道我是否有正确的方法来处理 PHP 中的 OOP,并认为我可能会遗漏一些东西。这是正确的做法还是我错过了什么?

如果您要走这条路,我会避免使用原生数组。我会让 classes 包含某种类型的数组。这样,您可以在 class 的构造函数中验证数组内容,然后您可以对对象使用实际类型提示,PHP 允许。

使用 phpstorm,当您添加注释时,它会正确地为您完成代码。

/**
 * @param MyNamespace\MyObject[] $arrayOfObject
 */
function ProcessObjectArray(array $arrayOfObject)
{
    foreach ($arrayOfObject as $key => $myObj) {
        if (get_class($myObj) == 'MyNamespace\MyObject') {
            if (false) $myObj = new MyObject();
            // I can now access intellisense for MyObject in here

        } else {
            trigger_error('is not right object');
        }
    }
}

然而,智能感知不会阻止您传递不正确的对象,它只会帮助您在编码时防止传递不正确的对象。

也许您可能希望考虑使用集合而不是使用通用数组。

有很多示例,但这里有一个简单的示例,您可以对其进行扩展

class Collection 
{
    /** @var array */
    private $items = [];

    /**
     * @param MyNamespace\MyObject $obj
     * @return $this
     */
    public function addItem(MyNamespace\MyObject $obj) {
        $this->items[] = $obj;
        return $this;
    }

    /**
     * @param $index
     * @return $this
     */
    public function deleteItem($index) {
        unset($this->items[$index]);
        return $this;
    }

    /**
     * @param $index
     * @return MyNamespace\MyObject|null
     */
    public function getItem($index) {
        return array_key_exists($index, $this->items) ? $this->items[$index] : null;
    }

    /**
     * @return MyNamespace\MyObject[]
     */
    public function getItems() {
        return $this->items;
    }

    /**
     * @return int
     */
    public function count() {
        return sizeOf($this->items);
    }
}

这是一个如何运作的例子:

$collection = new \Collection;

$obj = new MyNamespace\MyObject;

$collection->addItem($obj);

foreach($collection->getItems() as $item) {
    // this will already know that $item is MyNamespace\MyObject because of the annotation on the getItems() method
    $item->doSomething();
}
class MyCollection implements \Iterator
{
    /**
     * @var CollectionElement[]
     */
    private $elements = [];


    public function addElement(CollectionElement $element)
    {
        $this->elements[] = $element;
    }

    /**
     * @return CollectionElement
     */
    public function current()
    {
        return current($this->elements);
    }

    //... Rest of the Iterator implementation
}

这样您就无法向 collection 添加 CollectionElement 以外的任何内容。之后:

foreach($collection as $element){
    /** @var CollectionElement $element */
    $element->//here you will have autocompletion
}

您不需要检查任何内容,因为您的 collection 永远不会包含您不期望的任何内容。