Qual é o melhor método para mesclar dois objects PHP?

Temos dois objects PHP5 e gostaria de mesclar o conteúdo de um para o segundo. Não há noção de subclasss entre elas, portanto as soluções descritas no tópico a seguir não podem ser aplicadas.

Como você copia um object PHP em um tipo de object diferente

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

Observações:

  • Estes são objects, não classs.
  • Os objects contêm muitos campos, então um foreach seria bastante lento.
  • Até agora, consideramos a transformação de objects A e B em arrays, em seguida, fundindo-os usando array_merge () antes de re-transformar em um object, mas não podemos dizer que estamos orgulhosos se isso.

Se os seus objects apenas contêm campos (sem methods), isso funciona:

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

Isso realmente também funciona quando objects possuem methods. (testado com PHP 5.3 e 5.6)

Se os seus objects apenas contêm campos (sem methods), isso funciona:

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

Você pode criar outro object que envia chamadas para methods mágicos para os objects subjacentes. Veja como você lida com __get , mas para que ele funcione totalmente, você precisaria replace todos os methods mágicos relevantes. Você provavelmente encontrará erros de syntax desde que eu acabei de entrar no topo da minha cabeça.

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

Boa sorte.

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

Eu entendo que usar os objects genéricos [stdClass ()] e lançá-los como arrays responde a pergunta, mas pensei que o Compositor era uma ótima resposta. No entanto, senti que poderia usar alguns aprimoramentos de resources e poderia ser útil para outra pessoa.

Características:

  • Especificar referência ou clone
  • Especifique a primeira ou última input para ter precedência
  • Múltiplos (mais de dois) mesclagem de objects com semelhança de syntax ao array_merge
  • Método de binding: $ obj-> f1 () -> f2 () -> f3 () …
  • Composites dynamics : $ obj-> merge (…) / * trabalho aqui * / $ obj-> merge (…)

Código:

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

Uso:

 $obj = new Compositor(use_reference, first_precedence); $obj->merge([object $object [, object $object [, object $...]]]); $obj->with([object $object]); 

Exemplo:

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

Uma solução muito simples, considerando-se o object A e B:

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

Isso é tudo. Agora você tem objA com todos os valores de objB.

uma solução Para preservar, os methods e as propriedades dos objects fundidos são criar uma class de combinadores que possa

  • pegue qualquer número de objects em __construct
  • acesse qualquer método usando __call
  • acesse qualquer propriedade usando __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'; } } 

uso simples

 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; 

Eu iria com o link do segundo object em uma propriedade do primeiro object. Se o segundo object é o resultado de uma function ou método, use referências. 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'); 

A class \ArrayObject tem a possibilidade de trocar a matriz atual para desconectar a referência original. Para fazer isso, ele vem com dois methods úteis: exchangeArray() e getArrayCopy() . O resto é simples array_merge() do object fornecido com as propriedades públicas do ArrayObject s:

 class MergeBase extends ArrayObject { public final function merge( Array $toMerge ) { $this->exchangeArray( array_merge( $this->getArrayCopy(), $toMerge ) ); } } 

O uso é tão fácil quanto isso:

  $base = new MergeBase(); $base[] = 1; $base[] = 2; $toMerge = [ 3,4,5, ]; $base->merge( $toMerge ); 

Para fundir qualquer número de objects em bruto

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

Aqui está uma function que alisará um object ou matriz. Use isso somente se tiver certeza de que suas chaves são únicas. Se você tiver chaves com o mesmo nome, elas serão substituídas. Você precisará colocar isso em uma class e replace “Funções” pelo nome da sua class. Apreciar…

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

Vamos manter isso simples!

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

Se isso não responder a sua pergunta, certamente ajudará na resposta. O crédito para o código acima vai para mim mesmo 🙂