У нас есть два объекта PHP5, и мы хотим объединить содержимое одного со вторым. Между ними нет понятия подклассов, поэтому решения, описанные в следующем разделе, неприменимы.
Как скопировать объект PHP в объект другого типа
//We have this:
$objectA->a;
$objectA->b;
$objectB->c;
$objectB->d;
//We want the easiest way to get:
$objectC->a;
$objectC->b;
$objectC->c;
$objectC->d;
Примечания:






Вы можете создать другой объект, который отправляет вызовы магических методов базовым объектам. Вот как вы будете обращаться с __get, но чтобы заставить его работать полностью, вам придется переопределить все соответствующие магические методы. Вы, вероятно, найдете синтаксические ошибки, так как я просто ввел их в уме.
class Compositor {
private $obj_a;
private $obj_b;
public function __construct($obj_a, $obj_b) {
$this->obj_a = $obj_a;
$this->obj_b = $obj_b;
}
public function __get($attrib_name) {
if ($this->obj_a->$attrib_name) {
return $this->obj_a->$attrib_name;
} else {
return $this->obj_b->$attrib_name;
}
}
}
Удачи.
Для полной реализации, вероятно, потребуются __isset (), __unset () и реализация интерфейса Interator.
@porneL: что такое интерфейс Interator?
Я бы отредактировал его комментарий, но вы не можете этого сделать. Я думаю, он имеет в виду Итератор
Мне очень нравится ваше решение, Аллен, но, боюсь, это означает, что нам придется переписать все наше приложение, если мы решим его использовать.
Хорошо ... тогда выберите способ, который не требует полной перезаписи.
foreach($objectA as $k => $v) $objectB->$k = $v;
Это быстрее, чем принятый ответ в версиях PHP <7 (по оценкам, на 50% быстрее). Но в PHP> = 7 принятый ответ примерно на 400% быстрее. Смотрите здесь: sandbox.onlinephpfunctions.com/code/…
Как мы можем использовать или получить здесь объединенные данные?
@ramedju В этом примере $objectB содержит объединенные данные.
Вот функция, которая сглаживает объект или массив. Используйте это, только если вы уверены, что ваши ключи уникальны. Если у вас есть ключи с таким же именем, они будут перезаписаны. Вам нужно будет поместить это в класс и заменить «Функции» на имя вашего класса. Наслаждаться...
function flatten($array, $preserve_keys=1, &$out = array(), $isobject=0) {
# Flatten a multidimensional array to one dimension, optionally preserving keys.
#
# $array - the array to flatten
# $preserve_keys - 0 (default) to not preserve keys, 1 to preserve string keys only, 2 to preserve all keys
# $out - internal use argument for recursion
# $isobject - is internally set in order to remember if we're using an object or array
if (is_array($array) || $isobject==1)
foreach($array as $key => $child)
if (is_array($child))
$out = Functions::flatten($child, $preserve_keys, $out, 1); // replace "Functions" with the name of your class
elseif ($preserve_keys + is_string($key) > 1)
$out[$key] = $child;
else
$out[] = $child;
if (is_object($array) || $isobject==2)
if (!is_object($out)){$out = new stdClass();}
foreach($array as $key => $child)
if (is_object($child))
$out = Functions::flatten($child, $preserve_keys, $out, 2); // replace "Functions" with the name of your class
elseif ($preserve_keys + is_string($key) > 1)
$out->$key = $child;
else
$out = $child;
return $out;
}
Я понимаю, что использование общих объектов [stdClass ()] и преобразование их в массивы отвечает на вопрос, но я думал, что Compositor был отличным ответом. Тем не менее, я чувствовал, что он может использовать некоторые улучшения функций и может быть полезным для кого-то еще.
Функции:
Код:
class Compositor {
protected $composite = array();
protected $use_reference;
protected $first_precedence;
/**
* __construct, Constructor
*
* Used to set options.
*
* @param bool $use_reference whether to use a reference (TRUE) or to copy the object (FALSE) [default]
* @param bool $first_precedence whether the first entry takes precedence (TRUE) or last entry takes precedence (FALSE) [default]
*/
public function __construct($use_reference = FALSE, $first_precedence = FALSE) {
// Use a reference
$this->use_reference = $use_reference === TRUE ? TRUE : FALSE;
$this->first_precedence = $first_precedence === TRUE ? TRUE : FALSE;
}
/**
* Merge, used to merge multiple objects stored in an array
*
* This is used to *start* the merge or to merge an array of objects.
* It is not needed to start the merge, but visually is nice.
*
* @param object[]|object $objects array of objects to merge or a single object
* @return object the instance to enable linking
*/
public function & merge() {
$objects = func_get_args();
// Each object
foreach($objects as &$object) $this->with($object);
// Garbage collection
unset($object);
// Return $this instance
return $this;
}
/**
* With, used to merge a singluar object
*
* Used to add an object to the composition
*
* @param object $object an object to merge
* @return object the instance to enable linking
*/
public function & with(&$object) {
// An object
if (is_object($object)) {
// Reference
if ($this->use_reference) {
if ($this->first_precedence) array_push($this->composite, $object);
else array_unshift($this->composite, $object);
}
// Clone
else {
if ($this->first_precedence) array_push($this->composite, clone $object);
else array_unshift($this->composite, clone $object);
}
}
// Return $this instance
return $this;
}
/**
* __get, retrieves the psudo merged object
*
* @param string $name name of the variable in the object
* @return mixed returns a reference to the requested variable
*
*/
public function & __get($name) {
$return = NULL;
foreach($this->composite as &$object) {
if (isset($object->$name)) {
$return =& $object->$name;
break;
}
}
// Garbage collection
unset($object);
return $return;
}
}
Применение:
$obj = new Compositor(use_reference, first_precedence);
$obj->merge([object $object [, object $object [, object $...]]]);
$obj->with([object $object]);
Пример:
$obj1 = new stdClass();
$obj1->a = 'obj1:a';
$obj1->b = 'obj1:b';
$obj1->c = 'obj1:c';
$obj2 = new stdClass();
$obj2->a = 'obj2:a';
$obj2->b = 'obj2:b';
$obj2->d = 'obj2:d';
$obj3 = new Compositor();
$obj3->merge($obj1, $obj2);
$obj1->c = '#obj1:c';
var_dump($obj3->a, $obj3->b, $obj3->c, $obj3->d);
// obj2:a, obj2:b, obj1:c, obj2:d
$obj1->c;
$obj3 = new Compositor(TRUE);
$obj3->merge($obj1)->with($obj2);
$obj1->c = '#obj1:c';
var_dump($obj3->a, $obj3->b, $obj3->c, $obj3->d);
// obj1:a, obj1:b, obj1:c, obj2:d
$obj1->c = 'obj1:c';
$obj3 = new Compositor(FALSE, TRUE);
$obj3->with($obj1)->with($obj2);
$obj1->c = '#obj1:c';
var_dump($obj3->a, $obj3->b, $obj3->c, $obj3->d);
// obj1:a, obj1:b, #obj1:c, obj2:d
$obj1->c = 'obj1:c';
Просто чтобы указать: передача по ссылке во время вызова была помечена как устаревшая в PHP 5.3.0 и удалена в PHP 5.4.0 (что приводило к появлению фатальной ошибки). Чтобы устранить проблему: Замена foreach($objects as &$object) $this->with(&$object); на foreach($objects as &$object) $this->with($object); устраняет проблему. Источник: [php.net/manual/en/language.references.pass.php]
Дополнительно: if ($this->first_precedence) array_push($this->composite, &$object); else array_unshift($this->composite, &$object); следует заменить на if ($this->first_precedence) array_push($this->composite, $object); else array_unshift($this->composite, $object);.
поэтому, чтобы обобщить ваши комментарии, удалите амперсанд (&) из объекта $ внутри: foreach (первый комментарий) ... array_push, array_unshift (второй комментарий)
@Chris Я обновил код, чтобы исправить проблемы в соответствии с комментариями выше.
В коде "Использование" вы неправильно написали Compositor как Compositer.
Если $first_precedence - это boolean, то вы должны просто назначить его, а не назначать результат или TRUE === TRUE, который всегда снова равен: логическому.
If your objects only contain fields (no methods), this works:
$obj_merged = (object) array_merge((array) $obj1, (array) $obj2);
На самом деле это также работает, когда у объектов есть методы. (проверено с PHP 5.3 и 5.6)
Вы также можете использовать array_merge_recursive для глубокого копирования. Вас также может заинтересовать array_replace_recursive. Подробно различия объясняются здесь: brian.serveblog.net/2011/07/31/php-array_replace-vs-array_me rge
Полученный в результате объект будет экземпляром stdclass. Хотя он в некотором смысле «работает» с объектами с методами, в этом случае он фактически разрушает объект (удаляя методы).
Это полезно для возврата нескольких наборов результатов в одной функции (и возврата только объекта с парами ключ-значение.)
Чтобы подтвердить комментарий @Brilliand, это не работает с объектами Laravel в коллекции Laravel.
Использование этого для объединения объектов stdClass, возвращаемых в результате вызова json_decode (). Просто так и работает. Большое спасибо!
Это не сработает, если в объекте есть целочисленный ключ. Рассмотрим следующий пример: $ arr1 = array ('a' => 9, 'b' => 'asd'); $ arr2 = array ('a' => 10, 'd' => 'qwert', 0 => 100, 1 => 200, 4 => 400); $ arr3 = array_merge ($ arr1, $ arr2); эхо (print_r ($ arr3, 1)); Фактический вывод: массив ([a] => 10 [b] => asd [d] => qwert [0] => 100 [1] => 200 [2] => 400). Требуемый вывод: массив ([a] => 10 [b] => asd [d] => qwert [0] => 100 [1] => 200 [4] => 400)
Это только у меня, или это дословная копия ответа, который уже публиковался в течение нескольких месяцев? stackoverflow.com/a/794356/151509
Очень простое решение, учитывая, что у вас есть объекты A и B:
foreach($objB AS $var=>$value){
$objA->$var = $value;
}
Это все. Теперь у вас есть objA со всеми значениями из objB.
Почему бы вам просто не сделать: $ objB = $ objA;
Я бы связал второй объект со свойством первого объекта. Если второй объект является результатом функции или метода, используйте ссылки. Бывший:
//Not the result of a method
$obj1->extra = new Class2();
//The result of a method, for instance a factory class
$obj1->extra =& Factory::getInstance('Class2');
Класс \ArrayObject имеет возможность обмен текущего массива, чтобы отключить исходный Справка. Для этого есть два удобных метода: exchangeArray() и getArrayCopy(). Остальное - простой array_merge() предоставленного объекта с общедоступными свойствами ArrayObjects:
class MergeBase extends ArrayObject
{
public final function merge( Array $toMerge )
{
$this->exchangeArray( array_merge( $this->getArrayCopy(), $toMerge ) );
}
}
Использовать так просто:
$base = new MergeBase();
$base[] = 1;
$base[] = 2;
$toMerge = [ 3,4,5, ];
$base->merge( $toMerge );
На самом деле это должен быть принятый ответ. Единственное, что было бы хорошо, если бы merge($array) действительно запрашивал и \ArrayObject.
Давайте будем простыми!
function copy_properties($from, $to, $fields = null) {
// copies properties/elements (overwrites duplicates)
// can take arrays or objects
// if fields is set (an array), will only copy keys listed in that array
// returns $to with the added/replaced properties/keys
$from_array = is_array($from) ? $from : get_object_vars($from);
foreach($from_array as $key => $val) {
if (!is_array($fields) or in_array($key, $fields)) {
if (is_object($to)) {
$to->$key = $val;
} else {
$to[$key] = $val;
}
}
}
return($to);
}
Если это не ответ на ваш вопрос, это наверняка поможет к ответу. Благодарность за приведенный выше код принадлежит мне :)
Решение Для сохранения как методов, так и свойств из объединенных объектов является создание класса комбинатора, который может
class combinator{
function __construct(){
$this->melt = array_reverse(func_get_args());
// array_reverse is to replicate natural overide
}
public function __call($method,$args){
forEach($this->melt as $o){
if (method_exists($o, $method)){
return call_user_func_array([$o,$method], $args);
//return $o->$method($args);
}
}
}
public function __get($prop){
foreach($this->melt as $o){
if (isset($o->$prop))return $o->$prop;
}
return 'undefined';
}
}
простое использование
class c1{
public $pc1='pc1';
function mc1($a,$b){echo __METHOD__." ".($a+$b);}
}
class c2{
public $pc2='pc2';
function mc2(){echo __CLASS__." ".__METHOD__;}
}
$comb=new combinator(new c1, new c2);
$comb->mc1(1,2);
$comb->non_existing_method(); // silent
echo $comb->pc2;
Это очень умно, снимаю шляпу. Я не думаю, что мне было бы удобно, если бы методы не были определены в результирующем классе объектов.
спасибо? .. за шляпу ... Это было просто для забавы, и я согласен с вами насчет удобства использования, в основном касательно автозаполнения в netbeans или другом редакторе
Чтобы объединить любое количество необработанных объектов
function merge_obj(){
foreach(func_get_args() as $a){
$objects[]=(array)$a;
}
return (object)call_user_func_array('array_merge', $objects);
}
Этот фрагмент кода рекурсивно преобразует эти данные в один тип (массив или объект) без вложенных циклов foreach. Надеюсь, это кому-то поможет!
Когда объект находится в формате массива, вы можете использовать array_merge и при необходимости преобразовать обратно в объект.
abstract class Util {
public static function object_to_array($d) {
if (is_object($d))
$d = get_object_vars($d);
return is_array($d) ? array_map(__METHOD__, $d) : $d;
}
public static function array_to_object($d) {
return is_array($d) ? (object) array_map(__METHOD__, $d) : $d;
}
}
Процедурный способ
function object_to_array($d) {
if (is_object($d))
$d = get_object_vars($d);
return is_array($d) ? array_map(__FUNCTION__, $d) : $d;
}
function array_to_object($d) {
return is_array($d) ? (object) array_map(__FUNCTION__, $d) : $d;
}
Все заслуги: Джейсон Окли
«Объекты содержат довольно много полей, поэтому выполнение foreach будет довольно медленным». - Компьютеры довольно быстрые, «довольно медленные» часто достаточно быстры.