Похоже, что в PHP объекты передаются по ссылке. Даже операторы присваивания не создают копию объекта.
Вот простое, надуманное доказательство:
<?php
class A {
public $b;
}
function set_b($obj) { $obj->b = "after"; }
$a = new A();
$a->b = "before";
$c = $a; //i would especially expect this to create a copy.
set_b($a);
print $a->b; //i would expect this to show 'before'
print $c->b; //i would ESPECIALLY expect this to show 'before'
?>
В обоих случаях для печати я получаю "после"
Итак, как мне передать $ а в set_b () по значению, а не по ссылке?
Нет, пока не нужно было его использовать.
(object) ((array) $objectA) может дать вам те же желаемые результаты с лучшей производительностью, чем при использовании clone $objectA или new stdClass.
Re «Кажется, даже операторы присваивания не создают копию объекта». - Надеюсь, что нет! Если бы они это сделали, результатом больше не было бы объектно-ориентированного языка (для всех практических целей).






Согласно документам (http://ca3.php.net/language.oop5.cloning):
$a = clone $b;
Согласно документу, он только клонирует атрибуты, которые не являются объектами, а атрибуты объекта копируются только по ссылке. Это означает, что это не глубокая копия.
В PHP 5+ объекты передаются по ссылке. В PHP 4 они передаются по значению (поэтому во время выполнения он передавался по ссылке, которая устарела).
Вы можете использовать оператор clone в PHP5 для копирования объектов:
$objectB = clone $objectA;
Кроме того, это просто объекты, которые передаются по ссылке, а не все, как вы сказали в своем вопросе ...
Просто хочу добавить ко всем, кто это читает, что при клонировании будет сохраняться ссылка на исходный объект. Выполнение запросов MySQL с использованием клонированного объекта может привести к непредсказуемым результатам из-за этого, поскольку выполнение может происходить не линейно.
Чтобы исправить распространенное заблуждение (я думаю, что даже документация PHP ошибается!) Объекты PHP 5 не «передаются по ссылке». Как и в Java, они имеют уровень косвенности дополнительный - переменная указывает на «указатель объекта», а он указывает на объект. Таким образом, две переменные могут указывать на один и тот же объект без, являющийся ссылками на одно и то же значение. Это можно увидеть из этого примера: $a = new stdClass; $b =& $a; $a = 42; var_export($b); здесь $b - это ссылка на Переменная$a; если вы замените =& на нормальный =, это будет ссылка нет, которая по-прежнему будет указывать на исходный объект.
Передача времени выполнения по ссылке - плохая идея, потому что из-за этого эффект вызова функции зависит от реализации функции, а не от спецификации. Это не имеет ничего общего с передачей по значению по умолчанию.
@Alex Не могли бы вы уточнить свой комментарий? (Либо здесь, либо где-нибудь еще.) Ваша точка зрения немного неясна, ИМО.
@ChrisMiddleton Подумайте о терминах C или C++: если вы клонировали ссылку на объект, который свободен, вне области видимости или освобожден, то ваша клонированная ссылка становится недействительной. Таким образом, вы май получаете неопределенное поведение в зависимости от того, что произошло с исходным объектом, на который вы держите ссылку посредством клонирования.
@Alex Вы это проверяли? Я только что проверил возврат клона из функции (где оригинал выходит за рамки и не имеет проблем). php -r 'class Foo {} function getFoo () { $f = new Foo(); $f->bar = 5; return clone $f; } $f = getFoo(); var_dump($f);' Вы уверены, что не думаете о назначении по ссылке ($f2 = &$f1)?
@ChrisMiddleton Я тестировал это давным-давно (PHP 5.4) в классе ORM и обнаружил, что это зависит от реализации класса. Clone, кажется, делает неглубокую копию, поэтому, если вы работаете с чем-то простым, все будет в порядке. Боюсь, что ничем не могу помочь, так как с тех пор, как я столкнулся с этой проблемой, прошло более двух лет. Объекты в PHP по сути являются указателями, поэтому, если исходный объект освобожден, вы столкнетесь с этой проблемой, упомянутой ранее.
Друг получает ошибку синтаксического анализа PHP, когда делает это, например: $new_date = (clone $date_start)->subDays(1); Он не работает с (), если я их удалю, я получаю другую ошибку. Дело в том, что мы используем тот же самый php 7.2.3, и мой работает нормально. Есть идеи? Искал везде ..
@ Ælex - re "при клонировании будет сохранена ссылка на исходный объект" - Точнее, клонирование делает копию мелкий свойств объекта. Если объект содержит другие объекты, то эти вложенные объекты теперь будут иметь две ссылки на них. Иногда это именно то, что нужно, иногда это проблема. Чтобы решить эту проблему, класс объекта должен реализовать __clone(), как показано в ответе Станислава, и при необходимости исправить каждое поле.
Ответы обычно можно найти в книгах по Java.
клонирование: Если вы не переопределяете метод клонирования, по умолчанию используется неглубокая копия. Если ваши объекты имеют только примитивные переменные-члены, все в порядке. Но в безтиповом языке с другим объектом в качестве переменных-членов это головная боль.
сериализация / десериализация
$new_object = unserialize(serialize($your_object))
Таким образом достигается глубокая копия с большими затратами, зависящими от сложности объекта.
+1 отличный, отличный, отличный способ сделать ГЛУБОКУЮ копию на PHP, тоже очень просто. Позвольте мне вместо этого спросить вас о стандартной мелкой копии, предлагаемой ключевым словом PHP clone, вы сказали, что копируются только примитивные переменные-члены: считаются ли массивы / строки PHP примитивными переменными-членами, поэтому они копируются, я прав?
Для всех, кто это понимает: «неглубокая» копия ($a = clone $b, без задействованных магических методов __clone()) эквивалентна просмотру каждого свойства объекта $b в терминах и присвоению этому же свойству нового члена того же объекта. класс, используя =. Свойства, являющиеся объектами, не получат cloned, как и объекты внутри массива; то же самое касается переменных, связанных ссылкой; все остальное - это просто значение и копируется так же, как и при любом назначении.
Идеальный! json_decode (json_encode ($ obj)); не клонировать частные / защищенные свойства и любой метод ... десериализовать (сериализовать, а не клонировать методы тоже ...
Потрясающий! Я наконец избавился от ошибки PhpStorm; Call to method __clone from invalid context :)
Друг получает ошибку синтаксического анализа PHP, когда делает это, например: $new_date = (clone $date_start)->subDays(1); Он не работает с (), если я их удалю, я получаю другую ошибку. Дело в том, что мы используем тот же самый php 7.2.3, и мой работает нормально. Есть идеи? Искал везде ..
Мы с коллегой постоянно сталкивались с этим. Клонирование не очень хорошо работает с более сложным объектом (постоянно возникают проблемы с обновлением неправильного элемента в памяти), но десериализация / сериализация работает. :) Спасибо за спасение!
Это добавляет много накладных расходов времени выполнения. Вы сериализуете объект в строку, а затем анализируете эту строку обратно в новую переменную. Хотя это делает то, что вы собираетесь делать, это происходит очень медленно. Вы не только конвертируете весь свой объект в строку и обратно, используя время и память, вы также нарушаете возможный механизм CopyOnWrite PHP. Гораздо лучший способ - правильно реализовать метод __clone, как предлагает stackoverflow.com/a/186191/1614903 ниже. См. phpinternalsbook.com/php5/zval/memory_management.html для более подробного объяснения.
Согласно предыдущему комментарию, если у вас есть другой объект в качестве переменной-члена, выполните следующие действия:
class MyClass {
private $someObject;
public function __construct() {
$this->someObject = new SomeClass();
}
public function __clone() {
$this->someObject = clone $this->someObject;
}
}
Теперь можно делать клонирование:
$bar = new MyClass();
$foo = clone $bar;
Если вы хотите полностью скопировать свойства объекта в другом экземпляре, вы можете использовать этот метод:
Сериализуйте его в JSON, а затем десериализуйте обратно в Object.
Хм, я бы избегал этого как черт.
Этот код помогает клонировать методы
class Foo{
private $run=10;
public $foo=array(2,array(2,8));
public function hoo(){return 5;}
public function __clone(){
$this->boo=function(){$this->hoo();};
}
}
$obj=new Foo;
$news= clone $obj;
var_dump($news->hoo());
этот код немного бесполезен, он будет работать, даже если вы удалите метод __clone :)
Я проводил некоторое тестирование и получил следующее:
class A {
public $property;
}
function set_property($obj) {
$obj->property = "after";
var_dump($obj);
}
$a = new A();
$a->property = "before";
// Creates a new Object from $a. Like "new A();"
$b = new $a;
// Makes a Copy of var $a, not referenced.
$c = clone $a;
set_property($a);
// object(A)#1 (1) { ["property"]=> string(5) "after" }
var_dump($a); // Because function set_property get by reference
// object(A)#1 (1) { ["property"]=> string(5) "after" }
var_dump($b);
// object(A)#2 (1) { ["property"]=> NULL }
var_dump($c);
// object(A)#3 (1) { ["property"]=> string(6) "before" }
// Now creates a new obj A and passes to the function by clone (will copied)
$d = new A();
$d->property = "before";
set_property(clone $d); // A new variable was created from $d, and not made a reference
// object(A)#5 (1) { ["property"]=> string(5) "after" }
var_dump($d);
// object(A)#4 (1) { ["property"]=> string(6) "before" }
?>
Просто для пояснения, что PHP использует копию при записи, поэтому в основном все является ссылкой, пока вы ее не измените, но для объектов вам нужно использовать clone и магический метод __clone (), как в принятом ответе.
В этом примере мы создадим класс iPhone и сделаем его точную копию с помощью клонирование
class iPhone {
public $name;
public $email;
public function __construct($n, $e) {
$this->name = $n;
$this->email = $e;
}
}
$main = new iPhone('Dark', '[email protected]');
$copy = clone $main;
// if you want to print both objects, just write this
echo "<pre>"; print_r($main); echo "</pre>";
echo "<pre>"; print_r($copy); echo "</pre>";
Очень мало случаев, когда вам действительно нужно такое поведение. Итак, если вы обнаружите, что часто используете его, то, возможно, есть что-то более фундаментальное неправильное в том, как вы пишете свой код?