Клонирование — это операция, состоящая в дублировании структуры данных, обычно во избежание проблемы наложения псевдонимов из-за того, что другой код изменяет один и тот же экземпляр непоследовательными способами.
В PHP клонирование может быть выполнено несколькими способами, а в некоторых случаях его можно полностью избежать.
По значению, по ссылке или по обработчику?
Примитивные структуры данных PHP, такие как все скаляры (целые числа, строки, логические значения) и массивы, передаются по значению между функциями и методами. Это не означает, что они копируются каждый раз в новую память — система копирования при записи — но концептуально вы можете вести себя так, как будто это правда. Таким образом, вам никогда не потребуется клонировать скаляр или массив скаляров, поскольку они будут переданы за пределы текущей области только с копией, которая не может повлиять на исходное значение.
Есть два исключения из этого механизма. Когда вам нужен клон для немедленного * уничтожения вычислений, нет необходимости в специальных структурах:
public function myMethod() { $values = $this->arrayOfIntegers; $values[] = rand(1, 42); return $values; // $this->arrayOfIntegers is still the same for the next round }
Более того, некоторые нативные функции принимают параметры только по ссылке:
$values = $this->arrayOfIntegers; sort($values);
И как таковые, эти функции должны быть переданы копии исходных структур данных, если вы не хотите, чтобы они были изменены. Вы можете узнать, имеет ли функция PHP доступ к параметрам по ссылке в ее документации, где они будут указаны с помощью & :
int preg_match ( string$pattern
, string$subject
[, array&$matches
[, int $flags
= 0 [, int $offset
= 0 ]]])
ресурс
proc_open (
строка
$cmd
,массив
$descriptorspec
,массив
&$pipes
[,строка
$cwd
[,массив
$env
[,массив
$other_options
]]])Но в отличие от sort () и его братьев и сестер, большинство этих функций полностью перезаписывают свои выходные аргументы, поэтому им никогда не передаются структуры данных, а вместо этого пустые массивы.
Объекты передаются обработчиком в PHP 5.x: то, что копируется между функциями, это просто указатель на объект. Поэтому, если вы передаете объект ArrayObject или другой объект, его можно изменить с помощью кода, который имеет ссылку на него либо в качестве параметра метода, либо в закрытом поле.
Клон ключевое слово
PHP имеет конструкцию для выполнения мелкого клонирования:
<?php $object = new stdClass; $object->collaborator = new stdClass; $clone = clone $object; var_dump($clone === $object); var_dump($clone->collaborator === $object->collaborator); $ php shallow_cloning.php bool(false) bool(true)
клон только дублирует объект, который вы передаете ему, но не остальную часть графа, к которому он прикреплен. Если объект содержит скаляры, они дублируются, поскольку они копируются по значению; но другие содержащиеся объекты тоже не клонируются по умолчанию .
Вы можете определить магический метод __clone () для выполнения клонирования внутренних объектов. Этот метод вызывается для клонированного объекта после того, как он был создан с использованием стратегии по умолчанию, и должен клонировать то, что все еще указывает на исходные поля экземпляра:
public function __clone() { $this->collaborator = clone $this->collaborator; }
Класс $ this- >laborator также может иметь метод __clone () и т. Д. Вплоть до остальной части графа объектов.
Этот механизм является дорогостоящим, но наиболее гибким, поскольку дает вам полный контроль над тем, что дублировать и чем делиться; например, вы можете решить полностью клонировать объект A и его соавтора B, но не соединение с базой данных, которое B содержит в качестве ссылки на поле.
Сериализация
Если вы просто хотите выполнить полное клонирование графа небольших объектов, сериализация — это самый быстрый механизм (с точки зрения программиста, а не из измерений производительности).
<?php $object = new stdClass; $object->collaborator = new stdClass; $clone = unserialize(serialize($object)); var_dump($clone === $object); var_dump($clone->collaborator === $object->collaborator); $ php serialization.php bool(false) bool(false)
Сериализация преобразует граф объектов в строку и обратно в объекты PHP с теми же внутренними частными состояниями и ссылками на объекты, что и в исходных экземплярах. Он также имеет дело с циклами в небольшом объектном графе, но большие графы объектов не следует полностью клонировать с помощью этого метода: на этот раз мы действительно дублируем память, когда появляются новые экземпляры.
Существует также снижение производительности для сериализации для каждой выполняемой вами операции клонирования: убедитесь, что вы профилировали, чтобы убедиться, что вы не клонируете сам Zend Framework в каждом процессе PHP вашего приложения. Кроме того, некоторые объекты даже не могут быть сериализованы (например, соединения PDO).
Радикальный вывод
Наиболее радикальным решением проблемы клонирования является проектирование с неизменяемыми объектами значений:
class A { private $b; public function __construct(B $b) { $this->b = $b; } // no other method modifies $b, which has the same structure. }
Объекты-значения могут передаваться без клонирования из-за их неизменности, что делает их безопасными от наложения имен. Они генерируют новый объект, когда их просят изменить себя, поэтому накладные расходы ленивают при модификации, а не при передаче. Также обратите внимание, что массивы объектов-значений можно передавать без клонирования, поскольку они копируются, пока их содержимое является неизменным.