In_array на объектах с круговыми ссылками

Я создаю массив объектов. Мне нужно, чтобы этот массив содержал только один экземпляр данного объекта, имеющий несколько ссылок на один и тот же объект, должен вызывать исключение. Для этого я использую следующий код:

public function addField ($name, iface\Node $field)
{
    // Prevent the same field being added multiple times
    if (!in_array ($field, $this -> fields))
    {
        $this -> fields [$name] = $field;
        $field -> setParent ($this);
    }
    else
    {
        throw new \InvalidArgumentException ('This field cannot be added to this group');
    }
    return ($this);
}

Это начало приводить к проблемам, когда я начал внедрять объекты, реализующие интерфейс Node, поскольку они могут включать в себя циклические ссылки (они содержат коллекцию своих дочерних узлов, причем каждый ребенок имеет ссылку на своего родителя). Попытка добавить поле может привести к возникновению следующей ошибки:

PHP Неустранимая ошибка: уровень вложенности слишком глубокий - рекурсивная зависимость?

Я подозреваю, что PHP пытается пересечь весь массив объектов, а не просто сравнивать ссылки на объекты, чтобы увидеть, сохраняют ли они одно и то же значение и поэтому указывают на один и тот же объект.

Мне нужно in_array, чтобы просто сравнить ссылки на объекты, которые он хранит, с ссылкой на объект. Это предотвратило бы попытку пересечения всего дерева объектов и столкновения с проблемой рекурсии.

Есть ли способ сделать это?

Ответ 1

Оказывается, ответ необычайно прост. Похоже, что по умолчанию in_array выполняет нестрогое сравнение (эквивалентное операции ==) при проверке стога сена для иглы. Это означает, что он проверяет, что все свойства равны, что означает, что он начинает перемещаться по графу объектов, и это может вызвать у вас проблемы, если у вас есть круговые ссылки на этом графике.

Функция in_array имеет строгий режим, однако, насколько я могу судить, эквивалент операции ===. Кажется, это заставляет его проверять ссылки, чтобы увидеть, указывают ли они на один и тот же объект вместо сравнения всех свойств.

Просто измените код на:

if (!in_array ($field, $this -> fields, true))

заставляет метод вести себя так, как я хотел, чтобы он вел себя без него, вызывая ошибку рекурсии.

Я должен сказать, что я немного удивлен тем, что PHP не делает этот режим по умолчанию. С другой стороны, я думаю, я действительно не должен удивляться тому, что слабое печатание PHP снова вызвало у меня проблему.:)

Ответ 2

Я бы просто использовал SplObjectStorage или spl_object_hash.

И вы правы, когда php сравнивает вещи, он рекурсивно пересекает структуры (массивы тоже).