วิธีที่ดีที่สุดในการรวมวัตถุ 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() ก่อนที่จะแปลงเป็นวัตถุอีกครั้ง แต่เราไม่สามารถพูดได้ว่าเราภูมิใจหากเป็นเช่นนั้น

person Veynom    schedule 18.01.2009    source แหล่งที่มา
comment
วัตถุมีฟิลด์ค่อนข้างมาก ดังนั้นการ foreach จึงค่อนข้างช้า - คอมพิวเตอร์ค่อนข้างเร็ว 'ค่อนข้างช้า' มักจะเร็วพอ   -  person Sean McSomething    schedule 28.04.2009


คำตอบ (12)


หากวัตถุของคุณมีเพียงฟิลด์ (ไม่มีวิธีการ) สิ่งนี้จะได้ผล:

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

สิ่งนี้ยังใช้งานได้จริงเมื่อวัตถุมีวิธีการ (ทดสอบด้วย PHP 5.3 และ 5.6)

person flochtililoch    schedule 23.12.2009
comment
คุณอาจใช้ array_merge_recursive เพื่อให้มีลักษณะการคัดลอกแบบลึก คุณอาจสนใจ array_replace_recursive มีการอธิบายความแตกต่างโดยละเอียดที่นี่: brian.serveblog.net /2011/07/31/php-array_replace-vs-array_merge - person Vincent Pazeller; 27.11.2013
comment
วัตถุที่เป็นผลลัพธ์จากสิ่งนี้จะเป็นอินสแตนซ์ของ stdclass แม้ว่ามันจะใช้งานได้กับวัตถุที่มีวิธีการ แต่มันก็ทำลายวัตถุในกรณีนั้นได้อย่างมีประสิทธิภาพ (โดยการลบวิธีการออก) - person Brilliand; 05.02.2015
comment
สิ่งนี้มีประโยชน์สำหรับการส่งคืนชุดผลลัพธ์หลายชุดในฟังก์ชันเดียว (และส่งคืนเฉพาะวัตถุที่มีคู่คีย์-ค่า) - person Leonel Atencio; 26.02.2016
comment
เพื่อยืนยันความคิดเห็นของ @Brilliand สิ่งนี้ใช้ไม่ได้กับวัตถุ Laravel ในคอลเลกชัน Laravel - person brianlmerritt; 22.08.2017
comment
การใช้สิ่งนี้เพื่อรวมวัตถุ stdClass ที่ส่งคืนจากการเรียกไปยัง json_decode() เรียบง่ายและใช้งานได้ ขอบคุณมากมาย! - person Jamie Carl; 05.12.2017
comment
สิ่งนี้จะไม่ทำงานหากมีคีย์จำนวนเต็มในวัตถุ ลองพิจารณาตัวอย่างต่อไปนี้ : $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)); เอาท์พุตตามจริง : Array ( [a] =› 10 [b] =› asd [d] =› qwert [0] =› 100 [1] =› 200 [2] =› 400 ) เอาท์พุตที่ต้องการ : Array ( [a] =› 10 [b] =› asd [d] =› qwert [0] =› 100 [1] =› 200 [4] =› 400 ) - person Souvik; 23.01.2018
comment
เป็นเพียงฉันหรือคำตอบนี้เป็นสำเนาคำตอบแบบคำต่อคำที่โพสต์ไว้หลายเดือนแล้ว? stackoverflow.com/a/794356/151509 - person maryisdead; 09.05.2019

คุณสามารถสร้างอ็อบเจ็กต์อื่นที่ส่งการเรียกไปยังเมธอดเวทย์มนตร์ไปยังอ็อบเจ็กต์ที่อยู่ด้านล่าง ต่อไปนี้คือวิธีที่คุณจะจัดการกับ __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;
    }
  }
}

ขอให้โชคดี.

person Allain Lalonde    schedule 18.01.2009
comment
การใช้งานที่สมบูรณ์อาจต้องใช้ __isset(), __unset() และใช้อินเทอร์เฟซ Interator - person Kornel; 18.01.2009
comment
@porneL: อินเทอร์เฟซ Interator คืออะไร - person Pim Jager; 19.01.2009
comment
ฉันจะแก้ไขความคิดเห็นของเขา แต่คุณไม่สามารถทำได้ ฉันคิดว่าเขาหมายถึง Iterator - person Allain Lalonde; 19.01.2009
comment
ฉันชอบวิธีแก้ปัญหาของคุณมาก Allain แต่ฉันเกรงว่านั่นหมายความว่าเราต้องเขียนแอปพลิเคชันใหม่ทั้งหมดหากเราตัดสินใจใช้ - person Veynom; 19.01.2009
comment
ตกลง... จากนั้นเลือกวิธีที่ไม่ต้องเขียนใหม่ทั้งหมด - person Allain Lalonde; 19.01.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';
person Ryan Schumacher    schedule 27.05.2009
comment
เพียงชี้ให้เห็นว่า: การอ้างอิงผ่านเวลาโทรถูกทำเครื่องหมายว่าเลิกใช้แล้วใน 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] - person wes.hysell; 17.12.2013
comment
นอกจากนี้: 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); - person wes.hysell; 17.12.2013
comment
ดังนั้นเพื่อสรุปความคิดเห็นของคุณ ให้ลบเครื่องหมาย Ampersand (&) ออกจาก $object ภายใน: foreach (ความคิดเห็นแรก)... array_push, array_unshift (ความคิดเห็นที่สอง) - person Chris; 05.04.2015
comment
@Chris ฉันอัปเดตโค้ดเพื่อแก้ไขปัญหาตามความคิดเห็นด้านบน - person Ryan Schumacher; 08.04.2015
comment
ในโค้ด 'การใช้งาน' คุณสะกด Compositor เป็น Compositer ผิด - person Xesau; 26.09.2015
comment
หาก $first_precedence เป็น boolean คุณควรกำหนดแทนการกำหนดผลลัพธ์หรือ TRUE === TRUE ซึ่งจะเท่ากับอีกครั้งเสมอ: บูลีน - person kaiser; 23.09.2016

วิธีแก้ปัญหาที่ง่ายมากเมื่อพิจารณาว่าคุณมีวัตถุ A และ B:

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

นั่นคือทั้งหมดที่ ตอนนี้คุณมี objA พร้อมค่าทั้งหมดจาก objB

person Jônatas Eridani    schedule 01.06.2010
comment
ทำไมคุณไม่เพียงแค่ทำ: $objB = $objA; - person Scottymeuk; 13.06.2013

คลาส \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 );
person Corelmax    schedule 12.08.2012
comment
นี่ควรเป็นคำตอบที่ยอมรับจริงๆ สิ่งเดียวที่ดีก็คือถ้า merge($array) ขอ \ArrayObject ด้วยเช่นกัน - person kaiser; 23.09.2016

วิธีแก้ปัญหา เพื่อรักษาทั้งวิธีการและคุณสมบัติจาก onjects ที่ผสานคือการสร้างคลาสตัวรวมที่สามารถทำได้

  • นำวัตถุจำนวนเท่าใดก็ได้บน __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;
person bortunac    schedule 08.06.2015
comment
มันฉลาดมาก นับถือเลย ฉันไม่คิดว่าฉันจะพอใจกับวิธีการที่ไม่ได้ถูกกำหนดไว้ในคลาสอ็อบเจ็กต์ผลลัพธ์ - person Slytherin; 09.12.2016
comment
ขอบคุณ ?.. สำหรับหมวก ... มันเป็นเพียงเพื่อความสนุกสนานและฉันเห็นด้วยกับคุณเกี่ยวกับความสะดวกสบายในการใช้งานส่วนใหญ่เกี่ยวกับการเติมข้อความอัตโนมัติใน netbeans หรือโปรแกรมแก้ไขอื่น ๆ - person bortunac; 10.12.2016

ฉันจะเชื่อมโยงวัตถุที่สองเข้ากับคุณสมบัติของวัตถุแรก ถ้าวัตถุที่สองเป็นผลมาจากฟังก์ชันหรือวิธีการ ให้ใช้การอ้างอิง อดีต:

//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');
person Adrian    schedule 07.12.2010

เพื่อรวมวัตถุดิบจำนวนเท่าใดก็ได้

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

นี่คือฟังก์ชันที่จะทำให้วัตถุหรืออาร์เรย์แบนลง ใช้สิ่งนี้เฉพาะในกรณีที่คุณแน่ใจว่ากุญแจของคุณไม่ซ้ำกัน หากคุณมีคีย์ที่มีชื่อเดียวกัน คีย์เหล่านั้นจะถูกเขียนทับ คุณจะต้องวางสิ่งนี้ไว้ในชั้นเรียนและแทนที่ "ฟังก์ชั่น" ด้วยชื่อชั้นเรียนของคุณ สนุก...

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;
}
person Community    schedule 15.05.2009

มาทำให้มันง่ายกันเถอะ!

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

หากนั่นไม่ตอบคำถามของคุณ มันจะช่วยตอบคำถามของคุณได้อย่างแน่นอน เครดิตสำหรับรหัสข้างต้นตกเป็นของตัวฉันเอง :)

person Rolf    schedule 02.09.2014

ข้อมูลโค้ดนี้จะแปลงข้อมูลนั้นเป็นประเภทเดียวแบบวนซ้ำ (อาร์เรย์หรือวัตถุ) โดยไม่มีลูป foreach ที่ซ้อนกัน หวังว่ามันจะช่วยใครซักคน!

เมื่อ Object อยู่ในรูปแบบอาเรย์ คุณสามารถใช้ array_merge และแปลงกลับเป็น Object ได้หากต้องการ

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

เครดิตทั้งหมดไปที่: Jason Oakley

person Drmzindec    schedule 12.10.2018

person    schedule
comment
นี่เร็วกว่าคำตอบที่ยอมรับใน PHP เวอร์ชัน ‹ 7 (เร็วกว่าประมาณ 50%) แต่ใน PHP ›= 7 คำตอบที่ยอมรับนั้นเร็วขึ้นประมาณ 400% ดูที่นี่: sandbox.onlinephpfunctions.com/code/ - person yunzen; 25.01.2017
comment
เราจะใช้หรือรับข้อมูลที่ผสานที่นี่ได้อย่างไร - person ; 25.07.2018
comment
@ramedju ในตัวอย่างนี้ $objectB เก็บข้อมูลที่ผสานแล้ว - person Kornel; 26.07.2018