合并两个 PHP 对象的最佳方法是什么?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/455700/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What is the best method to merge two PHP objects?
提问by Veynom
We have two PHP5 objects and would like to merge the content of one into the second. There are no notion of subclasses between them so the solutions described in the following topic cannot apply.
我们有两个 PHP5 对象,希望将其中一个的内容合并到第二个中。它们之间没有子类的概念,因此以下主题中描述的解决方案不适用。
How do you copy a PHP object into a different object type
//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;
Remarks:
评论:
- These are objects, not classes.
- The objects contain quite a lot of fields so a foreachwould be quite slow.
- So far we consider transforming objects A and B into arrays then merging them using array_merge()before re-transforming into an object but we can't say we are proud if this.
- 这些是对象,而不是类。
- 这些对象包含相当多的字段,因此foreach会很慢。
- 到目前为止,我们考虑将对象 A 和 B 转换为数组,然后在重新转换为对象之前使用array_merge()合并它们,但我们不能说我们为此感到自豪。
回答by flochtililoch
If your objects only contain fields (no methods), this works:
如果您的对象仅包含字段(无方法),则此方法有效:
$obj_merged = (object) array_merge((array) $obj1, (array) $obj2);
This actually also works when objects have methods. (tested with PHP 5.3 and 5.6)
当对象有方法时,这实际上也有效。(用 PHP 5.3 和 5.6 测试)
回答by Allain Lalonde
You could create another object that dispatches calls to magic methods to the underlying objects. Here's how you'd handle __get, but to get it working fully you'd have to override all the relevant magic methods. You'll probably find syntax errors since I just entered it off the top of my head.
您可以创建另一个对象,将魔术方法调用分派给底层对象。这是您如何处理__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;
}
}
}
Good luck.
祝你好运。
回答by Kornel
foreach($objectA as $k => $v) $objectB->$k = $v;
回答by Ryan Schumacher
I understand that using the generic objects [stdClass()] and casting them as arrays answers the question, but I thought the Compositor was a great answer. Yet I felt it could use some feature enhancements and might be useful for someone else.
我知道使用通用对象 [stdClass()] 并将它们转换为数组可以回答这个问题,但我认为合成器是一个很好的答案。但是我觉得它可以使用一些功能增强,并且可能对其他人有用。
Features:
特征:
- Specify reference or clone
- Specify first or last entry to take precedence
- Multiple (more than two) object merging with syntax similarity to array_merge
- Method linking: $obj->f1()->f2()->f3()...
- Dynamiccomposites: $obj->merge(...) /* work here */ $obj->merge(...)
- 指定引用或克隆
- 指定第一个或最后一个条目优先
- 多个(多于两个)对象合并,语法与 array_merge 相似
- 方法链接:$obj->f1()->f2()->f3()...
- 动态复合: $obj->merge(...) /* 在这里工作 */ $obj->merge(...)
Code:
代码:
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;
}
}
Usage:
用法:
$obj = new Compositor(use_reference, first_precedence);
$obj->merge([object $object [, object $object [, object $...]]]);
$obj->with([object $object]);
Example:
例子:
$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';
回答by J?natas Eridani
A very simple solution considering you have object A and B:
考虑到您有对象 A 和 B,一个非常简单的解决方案:
foreach($objB AS $var=>$value){
$objA->$var = $value;
}
That's all. You now have objA with all values from objB.
就这样。您现在拥有 objA 和来自 objB 的所有值。
回答by Corelmax
The \ArrayObjectclass has the possibility to exchangethe current array to disconnect the original reference. To do so, it comes with two handy methods: exchangeArray()and getArrayCopy(). The rest is plain simple array_merge()of the provided object with the ArrayObjects public properties:
所述\ArrayObject类具有这样的可能性交换当前的阵列,以断开原始参考。为此,它提供了两个方便的方法:exchangeArray()和getArrayCopy()。剩下的很简单array_merge(),提供的对象具有ArrayObjects 公共属性:
class MergeBase extends ArrayObject
{
public final function merge( Array $toMerge )
{
$this->exchangeArray( array_merge( $this->getArrayCopy(), $toMerge ) );
}
}
The usage is as easy as this:
用法很简单:
$base = new MergeBase();
$base[] = 1;
$base[] = 2;
$toMerge = [ 3,4,5, ];
$base->merge( $toMerge );
回答by bortunac
a solution To preserve,both methods and properties from merged onjects is to create a combinator class that can
一个解决方案要保留来自合并对象的方法和属性,是创建一个组合器类,它可以
- take any number of objects on __construct
- access any method using __call
- accsess any property using __get
- 在 __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';
}
}
simple use
简单使用
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;
回答by bortunac
To merge any number of raw objects
合并任意数量的原始对象
function merge_obj(){
foreach(func_get_args() as $a){
$objects[]=(array)$a;
}
return (object)call_user_func_array('array_merge', $objects);
}
回答by Adrian
I would go with linking the second object into a property of the first object. If the second object is the result of a function or method, use references. Ex:
我会去将第二个对象链接到第一个对象的属性。如果第二个对象是函数或方法的结果,请使用引用。前任:
//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');
回答by Rolf
Let's keep it simple!
让我们保持简单!
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);
}
If that doesn't answer your question, it will surely help towards the answer. Credit for the code above goes to myself :)
如果这不能回答你的问题,它肯定会帮助你找到答案。以上代码归功于我自己:)

