Назначение объектов PHP и клонирование
Я знаю, что это описано в php docs, но я смутился с этой проблемой.
Из php-документов:
$instance = new SimpleClass();
$assigned = $instance;
$reference =& $instance;
$instance->var = '$assigned will have this value';
$instance = null; // $instance and $reference become null
var_dump($instance);
var_dump($reference);
var_dump($assigned);
?>
Вышеприведенный пример выводит:
NULL
NULL
object(SimpleClass)#1 (1) {
["var"]=>
string(30) "$assigned will have this value"
}
ОК, поэтому я вижу, что $assigned
сохранило исходный объект ($instance
), присвоенный null
, поэтому, очевидно, $assigned
не является ссылкой, а копией экземпляра $.
Так в чем разница между
$assigned = $instance
и
$assigned = clone $instance
Ответы
Ответ 1
Объекты являются абстрактными данными в памяти. Переменная всегда содержит ссылку на эти данные в памяти. Представьте, что $foo = new Bar
создает экземпляр объекта Bar
где-то в памяти, присваивает ему некоторый идентификатор #42
, и $foo
теперь держит этот #42
как ссылку на этот объект. Присвоение этой ссылки другим переменным по ссылке или обычно работает так же, как и с любыми другими значениями. Многие переменные могут содержать копию этой ссылки, но все они указывают на один и тот же объект.
clone
явно создает копию самого объекта, а не только ссылки, указывающей на объект.
$foo = new Bar; // $foo holds a reference to an instance of Bar
$bar = $foo; // $bar holds a copy of the reference to the instance of Bar
$baz =& $foo; // $baz references the same reference to the instance of Bar as $foo
Только не путайте "ссылку" как в =&
с "ссылкой" как в идентификаторе объекта.
$blarg = clone $foo; // the instance of Bar that $foo referenced was copied
// into a new instance of Bar and $blarg now holds a reference
// to that new instance
Ответ 2
Разница между
$assigned = $instance
а также
$assigned = clone $instance
в первом случае вы присваиваете ссылку на уже существующий объект, а во втором создаете новый объект и присваиваете его переменной.
Более того, когда вы используете ключевое слово clone, вы можете использовать магический метод __clone(), который дает вам лучший контроль над клонированием объектов. Из руководства по PHP:
После завершения клонирования, если определен метод __clone(), будет вызван вновь созданный метод объекта __clone(), чтобы разрешить любые необходимые свойства, которые необходимо изменить.
Из руководства:
Ссылка PHP - это псевдоним, который позволяет двум разным переменным записывать одно и то же значение. Начиная с PHP 5, переменная объекта больше не содержит сам объект в качестве значения. Он содержит только идентификатор объекта, который позволяет методам доступа к объекту найти фактический объект. Когда объект отправляется с помощью аргумента, возвращается или присваивается другой переменной, различные переменные не являются псевдонимами: они содержат копию идентификатора, который указывает на тот же объект.
Позвольте мне привести вам живой пример
$dateA = new \Datetime('2017-04-04');
$dateB = $dateA; // $dateB references exactly the same object as $dateA
$dateB->modify('+1 day');
var_dump($dateA->format('Y-m-d')); //string(10) "2017-04-05"
var_dump($dateB->format('Y-m-d')); //string(10) "2017-04-05"
// $dateA is still modified by the code above so it has 2017-04-05
$dateC = clone $dateA; // we clone $dateA so it a new object
$dateC->modify('+1 day');
var_dump($dateA->format('Y-m-d')); // string(10) "2017-04-05"
var_dump($dateC->format('Y-m-d')); // string(10) "2017-04-06"
//примечание для даты и времени, я рекомендую использовать DatetimeImmutable вместо Datetime
РЕДАКТИРОВАТЬ: внутренние типы
// create 2 integer variables $a and $b
$a = 1;
$b = 1;
// create a new $c variable and assign the *value* of $a to that variable
$c = $a;
// create a new $d variable and assign a reference to $b variable
$d = &$b;
// increment $b, $c and $d variables
$b++;
$c++;
$d++;
echo $a; // 1
echo $b; // 3
echo $c; // 2
echo $d; // 3
поскольку $ d ссылается на $ b, когда мы увеличиваем его значение, оно также изменит значение $ b.
Разница между внутренними объектами, такими как строки, int, float и т.д., Заключается в том, что они передаются по значению, а объекты передаются по умолчанию через ссылку.
Примечание: вы не можете использовать клон с внутренними объектами.
Ответ 3
$assigned = $instance
Выше присваивается $instance to $assign, самый основной присваивающий.
$assigned = clone $instance
Это для клонирования объектов. Назначьте копию объекта $instance to $assign.
Без клона, $assign и $instance имеют одинаковый идентификатор объекта, что означает, что они указывают на один и тот же объект.
Ответ 4
PHP не управляет объектами так же, как управляет другими типами данных. Строка (или целое число, логическое значение, float или массив) непосредственно сохраняется в переменной. Когда значение переменной присваивается другой переменной, значение копируется 1 в новую переменную.
Например:
$x = array('a');
$y = $x;
// $x and $y are different and unrelated variables; they do not share anything
$y[] = 'b';
print_r($y);
// Array
// (
// [0] => a
// [1] => b
// )
print_r($x);
// Array
// (
// [0] => a
// )
Как PHP обрабатывает назначение объектов?
С другой стороны, объекты обрабатываются PHP с использованием уникальных идентификаторов. Когда объект присваивается переменной, идентификатор сохраняется в переменной, а не фактическом объекте.
Когда значение переменной присваивается другой переменной, идентификатор копируется, а не сам объект. Это заставляет две переменные указывать на один и тот же объект.
Используя ваш пример, значения переменных $instance
и $assigned
равны, оба они содержат идентификатор одного и того же объекта. $reference
, с другой стороны, является ссылкой, то есть псевдоним (другое имя) переменной $assigned
. Вот почему оператор $instance = null;
очищает содержимое переменных $reference
и $assigned
, но не влияет на переменную $instance
, а также объект, чей идентификатор хранится в нем.
Перед настройкой $reference
на null
можно использовать любые из $instance
, $assigned
или reference
для доступа к объекту SimpleClass
, созданному в первой строке вашего примера. F.e:
$instance = new SimpleClass();
$assigned = $instance;
$instance->var = '$assigned will have this value';
echo($instance->var);
// It prints:
// $assigned will have this value
// $assigned is also modified because it is the same object
echo($assigned->var);
// It prints:
// $assigned will have this value
Подробнее о Объектах и ссылках PHP в документации.
Что происходит, когда $assigned = clone $instance
?
Оператор clone
создает дубликат его операнда. Он создает новый объект и инициализирует все его свойства, присваивая им значения свойств исходного объекта. Это означает, что если объект clone содержит объекты как свойства, эти свойства дублируются простым назначением, а не клонированием. 2
$assigned = clone $instance;
После этого оператора $assigned
содержит другое значение, чем $instance
, потому что теперь они хранят идентификаторы разных объектов. Будучи разными объектами, изменения $instance
больше не влияют на $assigned
.
$instance = new SimpleClass();
$instance->var = '$instance has this value';
$assigned = clone $instance;
echo($assigned->var);
// It prints:
// $instance has this value
$assigned->var = '$assigned has a different value';
echo($assigned->var);
// It prints:
// $assigned has a different value
// $instance is not modified
echo($instance->var);
// It prints:
// $instance has this value
1 Это не совсем так. Для целей оптимизации массивы не копируются до тех пор, пока они не будут изменены (копирование при записи). Тем не менее, это подробная информация о реализации, и для целей этого обсуждения хорошо рассмотреть все значения, за исключением того, что объекты копируются, когда они назначены новой переменной.
2 Это также называется "мелким" клонированием. Чтобы получить "глубокий" клон (настоящий дубликат, который не имеет ничего общего с оригиналом), класс клонированного объекта, который имеет объекты как свойства, должен реализовать __clone()
магический метод и клонировать свойства исходного объекта. Кроме того, классы этих свойств должны реализовывать __clone()
и т.д.
Ответ 5
Ну, в основном эти переменные - не что иное, как указатели на пространство памяти, где находится объект. Если вы сохраните значение указателя в другом указателе, а затем reset исходный указатель, ничего не произойдет с областью памяти, на которую оба они указывали.
Ответ 6
Сохранение короткого и простого здесь:
Здесь $reference
похож на псевдоним объекта $instance
. Если изначально $assignment
и $reference
указывают на пространство данных для объекта $instance
.
И когда есть изменение значения, которое указывает на $instance
, оно будет изменено повсюду.
Но когда $instance = null
, здесь мы делаем $instance
, чтобы указать на null
, и поскольку $reference
является псевдонимом so:
$reference → $instance → null....
В то время как $assignment
по-прежнему содержит указатель на пространство данных для объекта, созданного $instance
, но теперь $instance
больше не требуется то же самое.
Ответ 7
Он просто создает совершенно новый объект, который сохраняет свойства копируемого объекта. Он выполняет глубокую копию:
$assigned = $instance;
Он делает мелкую копию при копировании объекта из одного в другой:
$assigned = clone $instance;