合并两个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 ()将它们合并,但如果这样做,我们不能说我们感到自豪。
211318 次浏览

您可以创建另一个对象,将对魔术方法的调用分派给底层对象。这里是你如何处理__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;

这是一个将对象或数组平展的函数。只有在确定密钥是唯一的情况下才使用此方法。如果有相同名称的键,它们将被覆盖。您需要将其放在一个类中,并将“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;
}

我知道使用通用对象[stdClass()]并将它们转换为数组来回答这个问题,但我认为排字器是一个很好的答案。但我觉得它可以进行一些功能增强,可能对其他人有用。

特点:

  • 指定引用或克隆
  • 指定第一个或最后一个条目优先
  • 多个(两个以上)对象合并,语法类似于array_merge
  • 方法链接:$obj->f1()->f2()->f3()…
  • 动态复合材料:obj - >合并(…)/ *在这里工作* / obj - >合并(…)

代码:

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

如果你的对象只包含字段(没有方法),这是可行的:

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

当对象具有方法时,这实际上也适用。(使用PHP 5.3和5.6进行测试)

考虑到对象A和B,有一个非常简单的解决方案:

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

这是所有。你现在有了objA和objB的所有值。

我会把第二个对象链接到第一个对象的属性中。如果第二个对象是函数或方法的结果,请使用引用。例:

//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()。剩下的就是所提供对象的__abc4公共属性的简单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 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);
}
如果这不能回答你的问题,它肯定会帮助你找到答案。 以上代码归我所有:)

为了保存合并对象的方法和属性,可以创建一个组合子类

  • 在__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;

合并任意数量的原始对象

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

这段代码将递归地将该数据转换为单一类型(数组或对象),而不需要嵌套的foreach循环。希望它能帮助到一些人!

一旦一个对象变成数组格式,你就可以使用array_merge并在需要的时候转换回Object。

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

所有功劳归于杰森·奥克利