Назначение объектов 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;