Intereting Posts
PHP: require () и иерархии классов Symfony2 – Как использовать переменные Entity в CallbackValidator? Как я могу получить адрес электронной почты пользователя и имя пользователя из кнопки входа в facebook? Лучший / простой способ анализа параметров конфигурации в Sh / Bash и php Laravel 5 с максимальной нагрузкой Kohana ORM и проверка, имеющие проблемы Symfony2: невозможно смоделировать HTTP-аутентификацию в функциональном тесте Автоматический выход из системы Zend после бездействия Насколько эффективно определяется PHP? Возможно ли динамически перезагрузить PHP-код во время работы скрипта? Преобразование var_dump массива обратно в переменную массива Как получить консоль Linux $ COLUMNS и $ ROWS из PHP cli? stream_socket_enable_crypto () Ошибка в почтовой программе php Простые тесты ZF2 для контроллера с использованием ZfcUser Как создать уникальный идентификатор заказа (только для отображения пользователя) с фактическим идентификатором заказа?

Каков наилучший способ слияния двух объектов PHP?

У нас есть два объекта 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; 

Примечания:

  • Это объекты, а не классы.
  • Объекты содержат довольно много полей, поэтому foreach будет довольно медленным.
  • До сих пор мы рассматриваем преобразование объектов A и B в массивы, а затем слияние их с помощью array_merge () перед преобразованием в объект, но мы не можем сказать, что мы гордимся этим.

Если ваши объекты содержат только поля (без методов), это работает:

 $obj_merged = (object) array_merge((array) $obj1, (array) $obj2); 

Это также работает, когда объекты имеют методы. (тестируется с PHP 5.3 и 5.6)

Если ваши объекты содержат только поля (без методов), это работает:

 $obj_merged = (object) array_merge((array) $obj1, (array) $obj2); 

Вы можете создать другой объект, который отправляет вызовы магическим методам к базовым объектам. Вот как вы будете обращаться с __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; } } } 

Удачи.

 foreach($objectA as $k => $v) $objectB->$k = $v; 

Я понимаю, что использование общих объектов [stdClass ()] и их литье в качестве массивов отвечает на вопрос, но я думал, что композитор был отличным ответом. Тем не менее, я чувствовал, что он может использовать некоторые улучшения функций и может быть полезен для кого-то другого.

Особенности:

  • Укажите ссылку или клон
  • Укажите приоритет первой или последней записи
  • Объединение нескольких (более двух) объектов с синтаксическим сходством с array_merge
  • Метод связывания: $ obj-> f1 () -> f2 () -> f3 () …
  • Динамические композиты: $ obj-> merge (…) / * работают здесь * / $ obj-> merge (…)

Код:

 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; } } с 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; } } с 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'; 

Очень простое решение, учитывая, что у вас есть объекты A и B:

 foreach($objB AS $var=>$value){ $objA->$var = $value; } 

Это все. Теперь у вас есть objA со всеми значениями из objB.

решение. Чтобы сохранить, как методы, так и свойства из объединенных onjects – создать класс комбинаторов, который может

  • взять любое количество объектов на __construct
  • доступ к любому методу с помощью __call
  • accsess любое свойство, используя __get

 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; 

Я хотел бы связать второй объект с собственностью первого объекта. Если второй объект является результатом функции или метода, используйте ссылки. Пример:

 //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() предоставленного объекта с публичными свойствами ArrayObject s:

 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 ); 

Чтобы объединить любое количество необработанных объектов

 function merge_obj(){ foreach(func_get_args() as $a){ $objects[]=(array)$a; } return (object)call_user_func_array('array_merge', $objects); } 

Вот функция, которая сглаживает объект или массив. Используйте это, только если вы уверены, что ваши ключи уникальны. Если у вас есть ключи с тем же именем, они будут перезаписаны. Вам нужно будет поместить это в класс и заменить «Функции» на имя вашего класса. Наслаждаться…

 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; } 

Давайте будем простыми!

 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); } 

Если это не ответит на ваш вопрос, это наверняка поможет в ответе. Кредит за код выше идет мне 🙂