什么是合并两个PHP对象的最佳方法?

我们有两个PHP5对象,并希望合并一个的内容到第二个。 它们之间没有子类的概念,因此下面的主题中描述的解决scheme不适用。

如何将PHP对象复制到不同的对象types中

//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.6testing)

如果你的对象只包含字段(没有方法),这个工作:

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

您可以创build另一个对象,将调用魔术方法的调用分派给底层对象。 以下是你如何处理__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()]并将它们转换为数组可以回答这个问题,但我认为Compositor是一个很好的答案。 然而,我觉得它可以使用一些function增强,可能对别人有用。

特征:

  • 指定参考或克隆
  • 指定第一个或最后一个条目优先
  • 多个(多于两个)对象与array_merge的语法相似性合并
  • 连接方法:$ obj-> f1() – > f2() – > f3()…
  • dynamic合成:$ 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; } } 

用法:

 $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'; 

一个非常简单的解决scheme考虑你有对象A和B:

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

就这样。 你现在有objB所有的值objB。

一个解决scheme为了保存,来自合并对象的方法和属性都是创build一个combinator类

  • 采取__construct上的任何数量的对象
  • 使用__call访问任何方法
  • 使用__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() 。 剩下的就是使用ArrayObject的公共属性提供的对象的简单array_merge()

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

这是一个将平坦化对象或数组的函数。 只有在确定您的密钥是唯一的情况下才可以使用。 如果你有相同的名字,他们将被覆盖。 你需要把它放在一个类中,用你的类的名字replace“Functions”。 请享用…

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

如果这不能回答你的问题,那肯定会对答案有所帮助。 信贷上面的代码去自己:)