Как лучше всего объединить два объекта 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;

Примечания:

  • Это объекты, а не классы.
  • Объекты содержат довольно много полей, поэтому для каждого будет довольно медленным.
  • Пока мы рассматриваем преобразование объектов A и B в массивы, а затем их объединение с помощью array_merge () перед повторным преобразованием в объект, но мы не можем сказать, что гордимся этим.

«Объекты содержат довольно много полей, поэтому выполнение foreach будет довольно медленным». - Компьютеры довольно быстрые, «довольно медленные» часто достаточно быстры.

Sean McSomething 28.04.2009 03:57
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Оживление вашего приложения Laravel: Понимание режима обслуживания
Оживление вашего приложения Laravel: Понимание режима обслуживания
Здравствуйте, разработчики! В сегодняшней статье мы рассмотрим важный аспект управления приложениями, который часто упускается из виду в суете...
Установка и настройка Nginx и PHP на Ubuntu-сервере
Установка и настройка Nginx и PHP на Ubuntu-сервере
В этот раз я сделаю руководство по установке и настройке nginx и php на Ubuntu OS.
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
Как установить PHP на Mac
Как установить PHP на Mac
PHP - это популярный язык программирования, который используется для разработки веб-приложений. Если вы используете Mac и хотите разрабатывать...
234
1
181 962
12
Перейти к ответу Данный вопрос помечен как решенный

Ответы 12

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

Удачи.

Для полной реализации, вероятно, потребуются __isset (), __unset () и реализация интерфейса Interator.

Kornel 18.01.2009 22:50

@porneL: что такое интерфейс Interator?

Pim Jager 19.01.2009 01:29

Я бы отредактировал его комментарий, но вы не можете этого сделать. Я думаю, он имеет в виду Итератор

Allain Lalonde 19.01.2009 01:41

Мне очень нравится ваше решение, Аллен, но, боюсь, это означает, что нам придется переписать все наше приложение, если мы решим его использовать.

Veynom 19.01.2009 11:26

Хорошо ... тогда выберите способ, который не требует полной перезаписи.

Allain Lalonde 19.01.2009 17:18
foreach($objectA as $k => $v) $objectB->$k = $v;

Это быстрее, чем принятый ответ в версиях PHP <7 (по оценкам, на 50% быстрее). Но в PHP> = 7 принятый ответ примерно на 400% быстрее. Смотрите здесь: sandbox.onlinephpfunctions.com/code/…

yunzen 25.01.2017 14:32

Как мы можем использовать или получить здесь объединенные данные?

user6632933 25.07.2018 11:17

@ramedju В этом примере $objectB содержит объединенные данные.

Kornel 26.07.2018 00:11

Вот функция, которая сглаживает объект или массив. Используйте это, только если вы уверены, что ваши ключи уникальны. Если у вас есть ключи с таким же именем, они будут перезаписаны. Вам нужно будет поместить это в класс и заменить «Функции» на имя вашего класса. Наслаждаться...

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 ()] и преобразование их в массивы отвечает на вопрос, но я думал, что 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';

Просто чтобы указать: передача по ссылке во время вызова была помечена как устаревшая в PHP 5.3.0 и удалена в PHP 5.4.0 (что приводило к появлению фатальной ошибки). Чтобы устранить проблему: Замена foreach($objects as &$object) $this->with(&$object); на foreach($objects as &$object) $this->with($object); устраняет проблему. Источник: [php.net/manual/en/language.references.pass.php]

wes.hysell 17.12.2013 02:16

Дополнительно: if ($this->first_precedence) array_push($this->composite, &$object); else array_unshift($this->composite, &$object); следует заменить на if ($this->first_precedence) array_push($this->composite, $object); else array_unshift($this->composite, $object);.

wes.hysell 17.12.2013 02:20

поэтому, чтобы обобщить ваши комментарии, удалите амперсанд (&) из объекта $ внутри: foreach (первый комментарий) ... array_push, array_unshift (второй комментарий)

Chris 05.04.2015 03:48

@Chris Я обновил код, чтобы исправить проблемы в соответствии с комментариями выше.

Ryan Schumacher 08.04.2015 20:41

В коде "Использование" вы неправильно написали Compositor как Compositer.

Xesau 26.09.2015 19:00

Если $first_precedence - это boolean, то вы должны просто назначить его, а не назначать результат или TRUE === TRUE, который всегда снова равен: логическому.

kaiser 23.09.2016 10:30
Ответ принят как подходящий

If your objects only contain fields (no methods), this works:

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

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

Вы также можете использовать array_merge_recursive для глубокого копирования. Вас также может заинтересовать array_replace_recursive. Подробно различия объясняются здесь: brian.serveblog.net/2011/07/31/php-array_replace-vs-array_me‌ rge

Vincent Pazeller 27.11.2013 13:43

Полученный в результате объект будет экземпляром stdclass. Хотя он в некотором смысле «работает» с объектами с методами, в этом случае он фактически разрушает объект (удаляя методы).

Brilliand 05.02.2015 03:34

Это полезно для возврата нескольких наборов результатов в одной функции (и возврата только объекта с парами ключ-значение.)

Leonel Atencio 26.02.2016 23:07

Чтобы подтвердить комментарий @Brilliand, это не работает с объектами Laravel в коллекции Laravel.

brianlmerritt 22.08.2017 12:59

Использование этого для объединения объектов stdClass, возвращаемых в результате вызова json_decode (). Просто так и работает. Большое спасибо!

Jamie Carl 05.12.2017 08:06

Это не сработает, если в объекте есть целочисленный ключ. Рассмотрим следующий пример: $ arr1 = array ('a' => 9, 'b' => 'asd'); $ arr2 = array ('a' => 10, 'd' => 'qwert', 0 => 100, 1 => 200, 4 => 400); $ arr3 = array_merge ($ arr1, $ arr2); эхо (print_r ($ arr3, 1)); Фактический вывод: массив ([a] => 10 [b] => asd [d] => qwert [0] => 100 [1] => 200 [2] => 400). Требуемый вывод: массив ([a] => 10 [b] => asd [d] => qwert [0] => 100 [1] => 200 [4] => 400)

Souvik 23.01.2018 11:29

Это только у меня, или это дословная копия ответа, который уже публиковался в течение нескольких месяцев? stackoverflow.com/a/794356/151509

maryisdead 09.05.2019 18:44

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

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

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

Почему бы вам просто не сделать: $ objB = $ objA;

Scottymeuk 13.06.2013 19:15

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

//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(). Остальное - простой array_merge() предоставленного объекта с общедоступными свойствами ArrayObjects:

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

На самом деле это должен быть принятый ответ. Единственное, что было бы хорошо, если бы merge($array) действительно запрашивал и \ArrayObject.

kaiser 23.09.2016 10:34

Давайте будем простыми!

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;

Это очень умно, снимаю шляпу. Я не думаю, что мне было бы удобно, если бы методы не были определены в результирующем классе объектов.

Slytherin 09.12.2016 17:35

спасибо? .. за шляпу ... Это было просто для забавы, и я согласен с вами насчет удобства использования, в основном касательно автозаполнения в netbeans или другом редакторе

bortunac 10.12.2016 20:32

Чтобы объединить любое количество необработанных объектов

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

Этот фрагмент кода рекурсивно преобразует эти данные в один тип (массив или объект) без вложенных циклов foreach. Надеюсь, это кому-то поможет!

Когда объект находится в формате массива, вы можете использовать array_merge и при необходимости преобразовать обратно в объект.

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

Все заслуги: Джейсон Окли

Другие вопросы по теме