Quelle est la meilleure méthode pour fusionner deux objets PHP?

nous avons deux objets PHP5 et souhaitons fusionner le contenu de L'un dans l'autre. Il n'y a pas de notion de sous-classes entre elles, de sorte que les solutions décrites dans le sujet suivant ne peuvent pas s'appliquer.

comment copier un objet PHP dans un autre type d'objet

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

  • ce sont des objets, pas des classes.
  • les objets contiennent beaucoup de champs donc un foreach serait assez lent.
  • jusqu'à présent, nous envisageons de transformer les objets A et B en tableaux, puis de les fusionner en utilisant array_merge () avant de les re-transformer en un objet, mais nous ne pouvons pas dire que nous en sommes fiers.
188
demandé sur Community 2009-01-18 21:55:18

12 réponses

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 quand les objets ont des méthodes. (testé avec PHP 5.3 et 5.6)

361
répondu flochtililoch 2015-12-09 12:58:33

si vos objets ne contiennent que des Champs (pas de méthodes), cela fonctionne:

$obj_merged = (object) array_merge((array) $obj1, (array) $obj2);
47
répondu 2009-06-05 14:00:36

vous pouvez créer un autre objet qui envoie des appels aux méthodes magiques vers les objets sous-jacents. Voici comment vous pouvez gérer __get , mais pour qu'il fonctionne pleinement vous devez remplacer toutes les méthodes magiques. Vous trouverez probablement des erreurs de syntaxe puisque je viens de Les entrer dans ma tête.

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.

25
répondu Allain Lalonde 2018-06-15 20:33:56
foreach($objectA as $k => $v) $objectB->$k = $v;
21
répondu Kornel 2009-01-18 19:18:15

je comprends que l'utilisation des objets génériques [stdClass()] et leur moulage comme tableaux répond à la question, mais j'ai pensé que le compositeur était une bonne réponse. Pourtant, j'ai senti qu'il pourrait utiliser certaines améliorations de fonctionnalité et pourrait être utile pour quelqu'un d'autre.

Dispose:

  • spécifier la référence ou le clone
  • spécifier la première ou la dernière entrée à avoir priorité
  • objet Multiple (plus de deux) fusion avec syntaxe similaire à array_merge
  • méthode de liaison: $ obj - > f1 ()->f2 () - >f3 ()...
  • Dynamique composites: $obj->fusionner les(...) / travail* ici */ $obj->fusionner les(...)

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

Utilisation:

$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';
10
répondu Ryan Schumacher 2015-09-27 02:58:52

une solution très simple considérant que vous avez l'objet A et B:

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

C'est tout. Vous avez maintenant objA avec toutes les valeurs de objB.

5
répondu Jônatas Eridani 2010-06-01 18:45:37

une solution pour préserver, à la fois les méthodes et les propriétés des objets fusionnés est de créer une classe combinatrice qui peut

  • prenez n'importe quel nombre d'objets sur le __construct
  • accéder à toute méthode utilisant _ _ call
  • accsess toute propriété à l'aide de __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';
    } 
}

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;
2
répondu bortunac 2016-10-10 10:05:12

je dirais de relier le second 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, utiliser 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');
1
répondu Adrian 2010-12-07 10:18:05

la classe \ArrayObject a la possibilité de échanger le réseau courant pour déconnecter l'original référence . Pour ce faire, il est livré avec deux méthodes pratiques: exchangeArray() et getArrayCopy() . Le reste est simple array_merge() de l'objet fourni avec le ArrayObject propriétés publiques:

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

l'usage est aussi simple que ceci:

 $base = new MergeBase();

 $base[] = 1;
 $base[] = 2;

 $toMerge = [ 3,4,5, ];

 $base->merge( $toMerge );
1
répondu Corelmax 2016-09-23 07:54:14

Pour fusionner n'importe quel nombre d'objets bruts

function merge_obj(){
    foreach(func_get_args() as $a){
        $objects[]=(array)$a;
    }
    return (object)call_user_func_array('array_merge', $objects);
}
1
répondu bortunac 2016-10-10 13:08:56

Voici une fonction qui aplatira un objet ou un tableau. Utilisez ceci seulement si vous êtes sûr que vos clés sont uniques. Si vous avez des clés avec le même nom, elles seront écrasées. Vous devrez le placer dans une classe et remplacer "fonctions" par le nom de votre classe. Profiter...

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;
}
0
répondu 2009-05-15 16:23:01

soyons 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 aidera sûrement vers la réponse. Le crédit pour le code ci-dessus va à moi-même:)

0
répondu Rolf 2014-09-02 20:10:14