What is the best method to merge two PHP objects?
我们有两个php5对象,希望将一个对象的内容合并到第二个对象中。它们之间没有子类的概念,因此下列主题中描述的解决方案不能应用。
如何将PHP对象复制到其他对象类型
1 2 3 4 5 6 7 8 9 10 11 | //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()将它们合并,但我们不能说我们为此感到骄傲。
If your objects only contain fields (no methods), this works:
1 |
当对象有方法时,这实际上也是有效的。(用php 5.3和5.6测试)
如果您的对象只包含字段(没有方法),则可以:
1 |
您可以创建另一个对象,将对基础对象的magic方法的调用分派给它。下面是您处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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; } } } |
祝你好运。
1 | foreach($objectA as $k => $v) $objectB->$k = $v; |
我知道使用通用对象[stdclass()]并将它们转换为数组可以回答这个问题,但我认为排序器是一个很好的答案。不过,我觉得它可以使用一些功能增强,可能对其他人有用。
特征:
- 指定引用或克隆
- 指定要优先的第一个或最后一个条目
- 多个(两个以上)对象合并,语法类似于数组合并
- 方法链接:$obj->f1()->f2()->f3()…
- 动态合成:$obj->merge(…)/*在这里工作*/$obj->merge(…)
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 | 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; } } |
用途:
1 2 3 | $obj = new Compositor(use_reference, first_precedence); $obj->merge([object $object [, object $object [, object $...]]]); $obj->with([object $object]); |
例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | $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:
1 2 3 | foreach($objB AS $var=>$value){ $objA->$var = $value; } |
这就是全部。现在您有了obja,其中包含了objb中的所有值。
要保留来自合并对象的方法和属性,一个解决方案是创建一个可以
- 在结构上获取任意数量的对象
- 使用调用访问任何方法
- 使用"获取"评估任何属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 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'; } } |
简单使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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; |
合并任意数量的原始对象
1 2 3 4 5 6 | function merge_obj(){ foreach(func_get_args() as $a){ $objects[]=(array)$a; } return (object)call_user_func_array('array_merge', $objects); } |
1 2 3 4 5 6 7 | class MergeBase extends ArrayObject { public final function merge( Array $toMerge ) { $this->exchangeArray( array_merge( $this->getArrayCopy(), $toMerge ) ); } } |
使用起来很简单:
1 2 3 4 5 6 7 8 | $base = new MergeBase(); $base[] = 1; $base[] = 2; $toMerge = [ 3,4,5, ]; $base->merge( $toMerge ); |
我将继续将第二个对象链接到第一个对象的属性中。如果第二个对象是函数或方法的结果,请使用引用。前任:
1 2 3 4 5 | //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'); |
这段代码将递归地将数据转换为单个类型(数组或对象),而不使用嵌套的foreach循环。希望它能帮助别人!
一旦一个对象是数组格式,你可以使用数组合并和转换回对象,如果需要的话。
1 2 3 4 5 6 7 8 9 10 11 12 |
程序方式
1 2 3 4 5 6 7 8 9 10 |
全部功劳归:杰森·奥克利
让我们保持简单!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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); } |
如果这不能回答你的问题,那肯定会有助于回答问题。以上代码归我所有:)
下面是一个将展平对象或数组的函数。只有在您确定您的钥匙是唯一的时候才使用这个。如果您有相同名称的密钥,它们将被覆盖。您需要将它放在类中,并用类的名称替换"函数"。享受…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | 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; } |