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 сравнивает вещи, он рекурсивно пересекает структуры (массивы тоже).