Как clone
объект в PHP со всеми данными?
Итак, чтобы уточнить: «со всеми данными» означает, что мне нужно, чтобы любые объекты внутри экземпляра были действительно скопированы. Например, если у вас есть:
class Bar
{
public $indicator;
public function __construct($x)
{
$this->indicator = $x;
}
}
class Foo
{
protected $prop;
public function __construct(Bar $prop)
{
$this->prop = $prop;
}
public function getProp()
{
return $this->prop;
}
}
$objX = new Foo(new Bar('one'));
$objY = clone $objX;
$objY->getProp()->indicator = 'two';
//two, since cloned object still tracks references inside it:
echo $objX->getProp()->indicator;
Скрипку для кода выше вы можете найти Вот.
И это нормально ..
Потому что стандарт __clone()
скопирует только основной объект:
Когда объект клонируется, PHP 5 выполнит поверхностную копию всех
свойства объекта. Любые свойства, которые являются ссылками на другие
переменные, останутся ссылками.
Таким образом, он не будет отслеживать все сущности внутри, так как будет очень медленным. Но сейчас мне нужно это сделать.
Вариант использования: когда я тестирую свой код, я предоставляю хранилище значений для вызываемого сопоставителя для phpunit. Это хранилище, однако, представляет собой простую карту между именем метода, значениями аргументов и полученным значением. Все хорошо, если только мне не нужно использовать одно и то же значение карты дважды — потому что на следующей итерации оно может быть изменено проверенным кодом. И это проблема.
Наивный подход
Пока что я сделал это:
public static function cloneArrayContainer(array $data)
{
$result = [];
foreach ($data as $key => $value) {
if (is_object($value)) {
$result[$key] = self::cloneObjectContainer($value);
} else if (is_array($value)) {
$result[$key] = self::cloneArrayContainer($value);
} else {
$result[$key] = $value;
}
}
return $result;
}
public static function cloneObjectContainer($object)
{
$result = clone $object;
$reflection = new \ReflectionObject($object);
foreach ($reflection->getProperties() as $property) {
$property->setAccessible(true);
$value = $property->getValue($result);
if (is_object($value)) {
$property->setValue($result, self::cloneObjectContainer($value));
} elseif (is_array($value)) {
$property->setValue($result, self::cloneArrayContainer($value));
}
}
return $result;
}
в одном из помощников заглушки. Но это не удалось для циклических ссылок (очевидно), чего, к сожалению, нельзя избежать в моем случае, поскольку они являются действительными (и востребованными бизнес-логикой) объектами для моего приложения.
Есть идеи?
Я пробовал также unserialize()
/ serialize()
— который потерпел неудачу, и, кроме того, у меня могут быть вызываемые объекты / ресурсы внутри моих объектов, таким образом, подход не соответствует моему случаю. В настоящее время я думаю о пересмотре самой структуры (я имею в виду тесты), но это будет последнее, что означает, что клонирование не удалось.
Задача ещё не решена.
Других решений пока нет …