Какой лучший способ объединить два объекта PHP? - PullRequest
208 голосов
/ 18 января 2009

У нас есть два объекта 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 () перед повторным преобразованием в объект, но мы не можем сказать, что гордимся этим.

Ответы [ 13 ]

402 голосов
/ 23 декабря 2009

Если ваши объекты содержат только поля (без методов), это работает:

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

Это на самом деле также работает, когда у объектов есть методы. (протестировано с PHP 5.3 и 5.6)

49 голосов
/ 27 апреля 2009

Если ваши объекты содержат только поля (без методов), это работает:

$obj_merged = (object) array_merge((array) $obj1, (array) $obj2);
27 голосов
/ 18 января 2009

Вы можете создать другой объект, который отправляет вызовы магических методов базовым объектам. Вот как вы будете обрабатывать __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;
    }
  }
}

Удачи.

23 голосов
/ 18 января 2009
foreach($objectA as $k => $v) $objectB->$k = $v;
10 голосов
/ 28 мая 2009

Я понимаю, что использование универсальных объектов [stdClass ()] и приведение их в виде массивов отвечает на вопрос, но я подумал, что Compositor - отличный ответ. Тем не менее, я чувствовал, что он может использовать некоторые улучшения функций и может быть полезен для кого-то еще.

Особенности:

  • Укажите ссылку или клон
  • Укажите первую или последнюю запись, чтобы иметь приоритет
  • Объединение нескольких (более двух) объектов с синтаксическим сходством с array_merge
  • Связывание метода: $ obj-> f1 () -> f2 () -> f3 () ...
  • Динамические композиты: $ obj-> merge (...) / * работа здесь * / $ obj-> merge (...)

Код:

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';
5 голосов
/ 01 июня 2010

Очень простое решение, учитывая, что у вас есть объекты A и B:

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

Вот и все. Теперь у вас есть objA со всеми значениями из objB.

2 голосов
/ 08 июня 2015

решение Для сохранения как методов, так и свойств объединенных объектов необходимо создать класс комбинатора, который может

  • взять любое количество объектов в __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;
1 голос
/ 10 октября 2016

Для объединения любого количества необработанных объектов

function merge_obj(){
    foreach(func_get_args() as $a){
        $objects[]=(array)$a;
    }
    return (object)call_user_func_array('array_merge', $objects);
}
1 голос
/ 13 августа 2012

Класс \ArrayObject имеет возможность обменивать текущий массив, чтобы отключить исходную ссылку . Для этого он поставляется с двумя удобными методами: exchangeArray() и getArrayCopy(). Остальное просто array_merge() предоставленного объекта с ArrayObject открытыми свойствами:

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 );
1 голос
/ 07 декабря 2010

Я бы пошел со связыванием второго объекта в свойство первого объекта. Если второй объект является результатом функции или метода, используйте ссылки. Пример:

//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');
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...