İki PHP nesnesini birleştirmek için en iyi yöntem nedir?


222

İki PHP5 nesnemiz var ve birinin içeriğini ikinciye birleştirmek istiyoruz. Aralarında alt sınıf kavramı yoktur, bu nedenle aşağıdaki konuda açıklanan çözümler uygulanamaz.

Bir PHP nesnesini farklı bir nesne türüne nasıl kopyalarsınız

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

Uyarılar:

  • Bunlar nesneler, sınıflar değil.
  • Nesneler oldukça fazla alan içeriyor, bu nedenle bir foreach oldukça yavaş olacaktır.
  • Şimdiye kadar A ve B nesnelerini dizilere dönüştürmeyi ve sonra bir nesneye yeniden dönüştürmeden önce array_merge () kullanarak birleştirmeyi düşünüyoruz, ancak bundan gurur duyduğumuzu söyleyemeyiz.

30
"Nesneler çok fazla alan içeriyor, bu yüzden bir öngörü oldukça yavaş olurdu." - Bilgisayarlar oldukça hızlı, 'oldukça yavaş' genellikle yeterince hızlı.
Sean McSomething

Yanıtlar:


435

Nesneleriniz yalnızca alan içeriyorsa (yöntem yok), bu işe yarar:

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

Bu aslında nesnelerin yöntemleri olduğunda da işe yarar. (PHP 5.3 ve 5.6 ile test edilmiştir)


1
Derin bir kopya davranışına sahip olmak için array_merge_recursive öğesini de kullanabilirsiniz. Ayrıca ilginizi çekebilir dizi_replace_recursive. Farklılıklar burada ayrıntılı olarak açıklanmıştır: brian.serveblog.net/2011/07/31/php-array_replace-vs-array_merge
Vincent Pazeller

12
Bundan kaynaklanan nesne bir örneği olacaktır stdclass. Yöntemleri olan nesneler üzerinde bir anlamda "çalışmasına" rağmen, bu durumda nesneyi etkili bir şekilde mahveder (yöntemleri kaldırarak).
Brilliand

Bu, tek bir işlevde birden çok
sonuç kümesi

1
Nesnede bir tamsayı anahtarı varsa bu çalışmaz. Aşağıdaki örneği düşünün: $ arr1 = dizi ('a' => 9, 'b' => 'asd'); $ arr2 = dizi ('a' => 10, 'd' => 'qwert', 0 => 100, 1 => 200, 4 => 400); $ arr3 = dizi_merge ($ arr1, $ arr2); echo (print_r ($ arr3; 1)); Gerçek Çıkış: Dizi ([a] => 10 [b] => asd [d] => qwert [0] => 100 [1] => 200 [2] => 400) İstenen Çıkış: Dizi ([a] => 10 [b] => asd [d] => qwert [0] => 100 [1] => 200 [4] => 400)
Souvik

2
Sadece ben miyim yoksa bu cevap aylardır gönderilmiş olan bir cevabın kelimesi kelimesine bir kopyası mı? stackoverflow.com/a/794356/151509
maryisdead 09

28

Temel nesnelere sihirli yöntemlere çağrı gönderen başka bir nesne oluşturabilirsiniz. İşte __getbununla başa çıkacaksınız , ancak tam olarak çalışmasını sağlamak için ilgili tüm sihirli yöntemleri geçersiz kılmanız gerekir. Sözdizimi hatalarını muhtemelen kafamın üstünden girmiş olduğum için bulacaksınız.

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

İyi şanslar.


Tam uygulamanın muhtemelen __isset (), __unset () ve Interator arabirimini uygulaması gerekir.
Kornel

@porneL: Interator Arabirimi nedir?
Pim Jager

2
Yorumunu düzenlerdim, ama bunu yapamazsın. Sanırım İteratör demek
Allain Lalonde

Çözümünüzü çok seviyorum Allain, ama korkarım ki kullanmaya karar verirsek tüm başvurumuzu yeniden yazmamız gerekiyor.
Veynom

3
Tamam ... sonra tam bir yeniden yazma gerektirmeyen yolu seçin.
Allain Lalonde

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

6
Bu, PHP <7 sürümlerinde kabul edilen yanıttan daha hızlıdır (yaklaşık% 50 daha hızlıdır). Ancak PHP> = 7'de kabul edilen cevap% 400 daha hızlıdır. Buraya bakın: sandbox.onlinephpfunctions.com/code/…
yunzen

Birleştirilen verileri burada nasıl kullanabilir veya alabiliriz?

1
@ramedju Bu örnekte $objectBbirleştirilmiş veriler bulunmaktadır.
Kornel

10

Genel nesneleri [stdClass ()] kullanarak ve diziler olarak döküm soruyu cevaplıyor, ama Besteci büyük bir cevap olduğunu düşündüm. Yine de bazı özellik geliştirmelerini kullanabileceğini ve başka biri için yararlı olabileceğini hissettim.

Özellikleri:

  • Referans veya klon belirtme
  • Öncelik almak için ilk veya son girişi belirtin
  • Array_merge ile sözdizimi benzerliği ile birden fazla (ikiden fazla) nesne birleştirme
  • Yöntem bağlama: $ obj-> f1 () -> f2 () -> f3 () ...
  • Dinamik kompozitler: $ obj-> merge (...) / * burada çalışın * / $ obj-> merge (...)

Kod:

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

Kullanımı:

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

Misal:

$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
Sadece belirtmek gerekirse: Çağrı zamanı referans by-pass PHP 5.3.0'da kullanımdan kaldırıldı olarak işaretlendi ve PHP 5.4.0'da kaldırıldı (yükseltilmiş bir Önemli Hata ile sonuçlandı). Sorunu düzeltmek için: Değiştirme foreach($objects as &$object) $this->with(&$object);ile foreach($objects as &$object) $this->with($object);düzeldikçe sorunu. Kaynak: [ php.net/manual/en/language.references.pass.php]
wes.hysell

2
Ayrıca: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

1
yorumlarınızı özetlemek için Ampersand (&) öğesini $ object içindeki kaldır: foreach (first comment) ... array_push, array_unshift (ikinci yorum)
Chris

1
@Chris Yukarıdaki yorumlara göre sorunları düzeltmek için kodu güncelledim.
Ryan Schumacher

'Kullanım' kodunuzda Compositor'ı Compositer olarak yanlış
yazdınız

7

A ve B nesneleriniz olduğunu düşünen çok basit bir çözüm:

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

Bu kadar. Artık objB'den gelen tüm değerlere sahip objA var.


Neden sadece yapmıyorsunuz: $ objB = $ objA;
Scottymeuk

2

\ArrayObjectSınıf imkanına sahiptir alışverişi orijinal kesmek için mevcut bir dizi referans . Bunu yapmak için iki kullanışlı yöntemle gelir: exchangeArray()ve getArrayCopy(). Geri kalanı s genel özellikleri array_merge()ile sağlanan nesnenin basit basit ArrayObject:

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

Kullanımı bu kadar kolaydır:

 $base = new MergeBase();

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

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

 $base->merge( $toMerge );

Bu aslında kabul edilen cevap olmalı . Güzel bir şey merge($array), aslında bir \ArrayObjectde talep ediyorum olurdu .
kaiser

2

bir çözüm Korumak için, birleştirilmiş nesnelerden hem yöntemler hem de özellikler, aşağıdakileri yapabilen bir birleştirici sınıfı oluşturmaktır

  • __construct üzerinde istediğiniz sayıda nesne alın
  • __call kullanarak herhangi bir yönteme erişme
  • __get kullanarak herhangi bir mülke erişim

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

basit kullanım

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;

Bu çok zekice, bunun için şapkalar. Sonuçta elde edilen nesneleri sınıfta tanımlanmayan yöntemler ile rahat olacağını sanmıyorum.
Slytherin

teşekkürler? .. şapka için ... Sadece eğlence içindi ve çoğunlukla netbeans veya diğer editörlerde otomatik tamamlama ile ilgili kullanım konusunda size katılıyorum
bortunac

1

İkinci nesneyi ilk nesnenin bir özelliğine bağlamakla giderdim. İkinci nesne bir işlevin veya yöntemin sonucuysa, başvurular kullanın. Ör:

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

İstediğiniz sayıda ham nesneyi birleştirmek için

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

0

İşte bir nesneyi veya diziyi düzleştirecek bir işlev. Bunu yalnızca anahtarlarınızın benzersiz olduğundan eminseniz kullanın. Aynı ada sahip anahtarlarınız varsa bunların üzerine yazılır. Bunu bir sınıfa yerleştirmeniz ve "İşlevler" i sınıfınızın adıyla değiştirmeniz gerekir. Zevk almak...

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

Basit tutalım!

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

Bu, sorunuza cevap vermezse, cevaba kesinlikle yardımcı olacaktır. Yukarıdaki kod için kredi kendime gidiyor :)


0

Bu kod snippet'i, iç içe geçmiş foreach döngüleri olmadan bu verileri özyinelemeli olarak tek bir türe (dizi veya nesneye) dönüştürür. Umarım birine yardım eder!

Bir Nesne dizi biçiminde olduğunda dizi_merge kullanabilir ve gerekirse Nesneye geri dönüştürebilirsiniz.

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

Usul yolu

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üm kredi gidiyor: Jason Oakley

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.