Nous avons deux objets PHP5 et aimerions fusionner le contenu d'un dans le second. Il n'y a pas de notion de sous-classes entre elles et les solutions décrites dans la rubrique suivante ne peuvent donc pas s'appliquer.
Comment copier un objet PHP dans un type d'objet différent
//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;
Remarques:
Si vos objets ne contiennent que des champs (pas de méthodes), cela fonctionne:
$obj_merged = (object) array_merge((array) $obj1, (array) $obj2);
Cela fonctionne aussi lorsque les objets ont des méthodes. (testé avec PHP 5.3 et 5.6)
Si vos objets ne contiennent que des champs (pas de méthodes), cela fonctionne:
$obj_merged = (object) array_merge((array) $obj1, (array) $obj2);
Vous pouvez créer un autre objet qui distribue des appels de méthodes magiques aux objets sous-jacents. Voici comment vous manipuleriez __get
, mais pour le faire fonctionner correctement, vous devrez remplacer toutes les méthodes magiques pertinentes. Vous trouverez probablement des erreurs de syntaxe puisque je viens de les saisir par cœur.
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;
}
}
}
Bonne chance.
foreach($objectA as $k => $v) $objectB->$k = $v;
Je comprends que l’utilisation des objets génériques [stdClass ()] et leur conversion en tant que tableaux répondent à la question, mais j’ai pensé que le Compositor était une excellente réponse. Pourtant, j’ai pensé qu’il pourrait utiliser certaines améliorations et pourrait être utile pour quelqu'un d’autre.
Caractéristiques:
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]);
Exemple:
$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';
Une solution très simple considérant que vous avez les objets A et B:
foreach($objB AS $var=>$value){
$objA->$var = $value;
}
C'est tout. Vous avez maintenant objA avec toutes les valeurs de objB.
une solution Pour préserver les méthodes et les propriétés des objets fusionnés, vous devez créer une classe de combinateur
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';
}
}
utilisation simple
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;
Pour fusionner un nombre quelconque d'objets bruts
function merge_obj(){
foreach(func_get_args() as $a){
$objects[]=(array)$a;
}
return (object)call_user_func_array('array_merge', $objects);
}
La classe \ArrayObject
a la possibilité de échanger le tableau actuel pour déconnecter l'original référence. Pour ce faire, il existe deux méthodes pratiques: exchangeArray()
et getArrayCopy()
. Le reste est simplement simple array_merge()
de l'objet fourni avec les propriétés publiques ArrayObject
s:
class MergeBase extends ArrayObject
{
public final function merge( Array $toMerge )
{
$this->exchangeArray( array_merge( $this->getArrayCopy(), $toMerge ) );
}
}
L'utilisation est aussi simple que cela:
$base = new MergeBase();
$base[] = 1;
$base[] = 2;
$toMerge = [ 3,4,5, ];
$base->merge( $toMerge );
J'irais avec lier le deuxième objet dans une propriété du premier objet. Si le deuxième objet est le résultat d'une fonction ou d'une méthode, utilisez des références. 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');
Voici une fonction qui va aplatir un objet ou un tableau. Utilisez-le uniquement si vous êtes sûr que vos clés sont uniques. Si vous avez des clés du même nom, elles seront écrasées. Vous devrez placer ceci dans une classe et remplacer "Fonctions" par le nom de votre classe. Prendre plaisir...
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;
}
Restons 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);
}
Si cela ne répond pas à votre question, cela nous aidera sûrement à trouver la réponse… .. Le crédit pour le code ci-dessus va à moi :)
Si vous avez besoin de fusionner plusieurs niveaux parce que certains objets sont imbriqués dans les propriétés requises, voici une solution:
$arr1 = [
"head"=>[
"title"=>"Tie1",
3,
5
],
2,
3
];
$arr2 = [
3,
4,
5,
"head"=>[
"title"=>"Tie2",
2,
4,
5
]
];
function object_merge($obj1, $obj2){
$newVal = $obj1;
foreach($obj2 as $key => $val){
if(is_array($val)) $newVal[$key] = object_merge($obj1[$key], $val);
elseif(is_string($key)) {
$newVal[$key] = $val;
}
else $newVal[] = $val;
}
return array_unique($newVal, SORT_REGULAR);
}
$newArr = object_merge($arr1, $arr2);
print_r($newArr);
Array
(
[head] => Array
(
[title] => Tie2
[0] => 3
[1] => 5
[2] => 2
[3] => 4
)
[0] => 2
[1] => 3
[3] => 4
[4] => 5
)
Cet extrait de code convertira récursivement ces données en un seul type (tableau ou objet) sans les boucles foreach imbriquées. J'espère que ça aide quelqu'un!
Une fois qu'un objet est au format tableau, vous pouvez utiliser array_merge et le reconvertir en objet si nécessaire.
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;
}
}
Manière procédurale
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;
}
Tout le crédit va à: Jason Oakley