Я строю массив объектов. Мне нужно, чтобы этот массив содержал только один экземпляр данного объекта, имеющий несколько ссылок на один и тот же объект, должен вызывать исключение. Для этого я использую следующий код:
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); }
Это начало приводить к проблемам, когда я начал внедрять объекты, реализующие интерфейс узла, поскольку они могут включать в себя циклические ссылки (они содержат коллекцию своих дочерних узлов, причем каждый ребенок имеет ссылку на своего родителя). Попытка добавить поле может привести к возникновению следующей ошибки:
PHP Неустранимая ошибка: уровень вложенности слишком глубокий – рекурсивная зависимость?
Я подозреваю, что PHP пытается пересечь весь массив объектов, а не просто сравнивать ссылки на объекты, чтобы увидеть, сохраняют ли они одно и то же значение и поэтому указывают на один и тот же объект.
Мне нужно in_array, чтобы просто сравнить ссылки на объекты, которые он хранит, с ссылкой на объект. Это предотвратило бы попытку пересечения всего дерева объектов и столкновения с проблемой рекурсии.
Есть ли способ сделать это?
Оказывается, ответ необычайно прост. Похоже, что по умолчанию in_array выполняет нестрогое сравнение (эквивалентное операции ==) при проверке стога сена для иглы. Это означает, что он проверяет, что все свойства равны, что означает, что он начинает перемещаться по графу объектов, и это может вызвать у вас проблемы, если у вас есть круговые ссылки на этом графике.
Однако функция in_array имеет строгий режим, который, насколько я могу судить, эквивалентен операции ===. Кажется, это заставляет его проверять ссылки, чтобы увидеть, указывают ли они на один и тот же объект вместо сравнения всех свойств.
Просто изменив код на:
if (!in_array ($field, $this -> fields, true))
заставляет метод вести себя так, как я хотел, чтобы он вел себя без него, вызвав ошибку рекурсии.
Я должен сказать, что я немного удивлен тем, что PHP не делает этот режим по умолчанию. С другой стороны, я думаю, я действительно не должен удивляться тому, что слабое печатание PHP снова вызвало у меня проблему. 🙂
Я бы просто использовал SplObjectStorage
или spl_object_hash
.
И вы правы, когда php сравнивает вещи, они рекурсивно пересекают структуры (массивы тоже).