Phương pháp tốt nhất để hợp nhất hai đối tượng PHP là gì?


222

Chúng tôi có hai đối tượng PHP5 và muốn hợp nhất nội dung của một thành thứ hai. Không có khái niệm về các lớp con giữa chúng để các giải pháp được mô tả trong chủ đề sau không thể áp dụng.

Làm thế nào để bạn sao chép một đối tượng PHP vào một loại đối tượng khác

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

Nhận xét:

  • Đây là những đối tượng, không phải lớp học.
  • Các đối tượng chứa khá nhiều lĩnh vực nên một foreach sẽ khá chậm.
  • Cho đến nay, chúng tôi xem xét chuyển đổi các đối tượng A và B thành các mảng sau đó hợp nhất chúng bằng mảng_merge () trước khi chuyển đổi lại thành một đối tượng nhưng chúng tôi không thể nói rằng chúng tôi tự hào nếu điều này.

30
"Các đối tượng chứa khá nhiều lĩnh vực nên một cuộc thảo luận sẽ khá chậm." - Máy tính khá nhanh, 'khá chậm' thường đủ nhanh.
Sean McS Something

Câu trả lời:


435

Nếu các đối tượng của bạn chỉ chứa các trường (không có phương thức), thì điều này hoạt động:

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

Điều này thực sự cũng hoạt động khi các đối tượng có phương thức. (đã thử nghiệm với PHP 5.3 và 5.6)


1
Bạn cũng có thể sử dụng Array_merge_recursive để có hành vi sao chép sâu. Bạn cũng có thể quan tâm đến mảng numplace_recursive. Sự khác biệt được giải thích chi tiết tại đây: brian.serveblog.net/2011/07/31/php-array_Vplace-vs-array_merge
Vincent Pazeller

12
Đối tượng kết quả từ đây sẽ là một ví dụ của stdclass. Mặc dù nó "hoạt động" theo nghĩa trên các đối tượng với các phương thức, nhưng nó thực sự phá hỏng đối tượng trong trường hợp đó (bằng cách loại bỏ các phương thức).
Brilliand

Điều này hữu ích để trả về nhiều kết quả trong một hàm (Và chỉ trả về một đối tượng có cặp giá trị khóa.)
Leonel Atencio

1
Điều này sẽ không hoạt động nếu có một khóa số nguyên trong đối tượng. Hãy xem xét ví dụ sau: $ Array1 = mảng ('a' => 9, 'b' => 'asd'); $ Array2 = mảng ('a' => 10, 'd' => 'qwert', 0 => 100, 1 => 200, 4 => 400); $ Array3 = Array_merge ($ Array1, $ Array2); echo (print_r ($ Array3, 1)); Đầu ra thực tế: Mảng ([a] => 10 [b] => asd [d] => qwert [0] => 100 [1] => 200 [2] => 400) Đầu ra mong muốn: Mảng ([a] => 10 [b] => asd [d] => qwert [0] => 100 [1] => 200 [4] => 400)
Souvik

2
Có phải chỉ tôi hay câu trả lời này là bản sao nguyên văn của câu trả lời đã được đăng trong nhiều tháng? stackoverflow.com/a/794356/151509
maryonomead

28

Bạn có thể tạo một đối tượng khác gửi các cuộc gọi đến các phương thức ma thuật đến các đối tượng cơ bản. Đây là cách bạn xử lý __get, nhưng để nó hoạt động hoàn toàn, bạn phải ghi đè tất cả các phương thức ma thuật có liên quan. Có lẽ bạn sẽ tìm thấy lỗi cú pháp vì tôi vừa nhập nó ra khỏi đỉnh đầu.

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

Chúc may mắn.


Hoàn thành thực hiện có thể sẽ cần __isset (), __unset () và thực hiện giao diện Interator.
Kornel

@yheL: Giao diện Interator là gì?
Pim Jager

2
Tôi sẽ chỉnh sửa nhận xét của anh ấy, nhưng bạn không thể làm điều đó. Tôi nghĩ anh ấy có nghĩa là Iterator
Allain Lalonde

Tôi rất thích giải pháp của bạn, Allain, nhưng tôi sợ điều đó có nghĩa là chúng tôi phải viết lại toàn bộ ứng dụng của mình nếu chúng tôi quyết định sử dụng nó.
Veynom

3
Ok ... sau đó chọn cách không yêu cầu viết lại hoàn chỉnh.
Allain Lalonde

25
foreach($objectA as $k => $v) $objectB->$k = $v;

6
Câu này nhanh hơn câu trả lời được chấp nhận trong các phiên bản PHP <7 (ước tính nhanh hơn 50%). Nhưng trong PHP> = 7, câu trả lời được chấp nhận là một cái gì đó nhanh hơn 400%. Xem tại đây: sandbox.onlinephpfifts.com/code/ Lần
yunzen

Làm thế nào chúng ta có thể sử dụng hoặc có được dữ liệu hợp nhất ở đây?

1
@azedju Trong ví dụ này $objectBchứa dữ liệu hợp nhất.
Kornel

10

Tôi hiểu rằng sử dụng các đối tượng chung [stdClass ()] và chuyển chúng thành mảng trả lời câu hỏi, nhưng tôi nghĩ Compositor là một câu trả lời tuyệt vời. Tuy nhiên, tôi cảm thấy nó có thể sử dụng một số cải tiến tính năng và có thể hữu ích cho người khác.

Đặc trưng:

  • Chỉ định tham chiếu hoặc bản sao
  • Chỉ định mục nhập đầu tiên hoặc cuối cùng để được ưu tiên
  • Nhiều (nhiều hơn hai) đối tượng hợp nhất với sự tương tự cú pháp với mảng_merge
  • Phương thức liên kết: $ obj-> f1 () -> f2 () -> f3 () ...
  • Các vật liệu tổng hợp động : $ obj-> merge (...) / * hoạt động ở đây * / $ obj-> merge (...)

Mã số:

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

Sử dụng:

$obj = new Compositor(use_reference, first_precedence);
$obj->merge([object $object [, object $object [, object $...]]]);
$obj->with([object $object]);

Thí dụ:

$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';

2
Chỉ cần chỉ ra: Tham chiếu qua thời gian cuộc gọi được đánh dấu là không dùng nữa trong PHP 5.3.0 và bị xóa trong PHP 5.4.0 (dẫn đến Lỗi nghiêm trọng tăng). Để khắc phục sự cố: Thay thế foreach($objects as &$object) $this->with(&$object);bằng foreach($objects as &$object) $this->with($object);sửa lỗi. Nguồn: [
php.net/manual/en/lingu.references.pass.php

2
Ngoài ra: if($this->first_precedence) array_push($this->composite, &$object); else array_unshift($this->composite, &$object);nên được thay thế bằngif($this->first_precedence) array_push($this->composite, $object); else array_unshift($this->composite, $object);
wes.hysell 16/12/13

1
vì vậy, để tóm tắt nhận xét của bạn, hãy xóa Ampersand (&) khỏi $ object bên trong: foreach (bình luận đầu tiên) ... Array_push, Array_unshift (bình luận thứ hai)
Chris

1
@Chris Tôi đã cập nhật mã để khắc phục các sự cố theo các nhận xét ở trên.
Ryan Schumacher

Trong mã 'Cách sử dụng' của bạn, bạn đã viết sai chính tả cho Nhà soạn nhạc
Xesau

7

Một giải pháp rất đơn giản xem xét bạn có đối tượng A và B:

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

Đó là tất cả. Bây giờ bạn có objA với tất cả các giá trị từ objB.


Tại sao bạn không chỉ làm: $ objB = $ objA;
Scottymeuk

2

Các \ArrayObjectlớp học có khả năng trao đổi các mảng hiện tại để ngắt kết nối với bản gốc tài liệu tham khảo . Để làm như vậy, nó đi kèm với hai phương pháp tiện dụng: exchangeArray()getArrayCopy(). Phần còn lại là đơn giản array_merge()của đối tượng được cung cấp với các ArrayObjectthuộc tính công khai:

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

Việc sử dụng dễ dàng như thế này:

 $base = new MergeBase();

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

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

 $base->merge( $toMerge );

Đây thực sự nên là câu trả lời được chấp nhận . Điều duy nhất tốt đẹp là nếu merge($array)thực sự sẽ yêu cầu \ArrayObjectnhư vậy.
kaiser

2

một giải pháp Để bảo toàn, cả hai phương thức và thuộc tính từ các từ khóa được hợp nhất là tạo ra một lớp tổ hợp có thể

  • lấy bất kỳ số lượng đối tượng nào trên __construct
  • truy cập bất kỳ phương thức nào bằng cách sử dụng __call
  • tích lũy bất kỳ tài sản nào bằng cách sử dụng __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';
    } 
}

sử dụng đơn giản

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;

Đó là rất thông minh, mũ ra đó. Tôi không nghĩ rằng tôi sẽ thoải mái với các phương thức không được định nghĩa trên lớp đối tượng kết quả.
Slytherin

? nhờ .. cho chiếc mũ ... Đó là chỉ để cho vui và tôi đồng ý với bạn về confort được sử dụng chủ yếu là liên quan đến autocomplete trong NetBeans hoặc biên tập viên khác
bortunac

1

Tôi sẽ đi với việc liên kết đối tượng thứ hai thành một thuộc tính của đối tượng đầu tiên. Nếu đối tượng thứ hai là kết quả của hàm hoặc phương thức, hãy sử dụng các tham chiếu. Ví dụ:

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

Để hợp nhất bất kỳ số lượng đối tượng thô

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

0

Đây là một hàm sẽ làm phẳng một đối tượng hoặc mảng. Chỉ sử dụng điều này nếu bạn chắc chắn các phím của bạn là duy nhất. Nếu bạn có khóa có cùng tên, chúng sẽ bị ghi đè. Bạn sẽ cần đặt cái này trong một lớp và thay thế "Hàm" bằng tên của lớp bạn. Thưởng thức...

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

Hãy giữ nó đơn giản!

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

Nếu điều đó không trả lời câu hỏi của bạn, nó chắc chắn sẽ giúp hướng tới câu trả lời. Tín dụng cho mã ở trên đi vào bản thân mình :)


0

Đoạn mã này sẽ chuyển đổi đệ quy dữ liệu đó thành một loại (mảng hoặc đối tượng) mà không có các vòng lặp foreach lồng nhau. Hy vọng nó sẽ giúp được ai đó!

Khi một đối tượng ở định dạng mảng, bạn có thể sử dụng mảng_merge và chuyển đổi trở lại đối tượng nếu bạn cần.

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

Cách thủ tục

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

Tất cả tín dụng được chuyển đến: Jason Oakley

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.