orientado - php poo pdf
¿Cuál es el mejor método para fusionar dos objetos PHP? (13)
Si sus objetos solo contienen campos (sin métodos), esto funciona:
$obj_merged = (object) array_merge((array) $obj1, (array) $obj2);
Esto en realidad también funciona cuando los objetos tienen métodos. (probado con PHP 5.3 y 5.6)
Tenemos dos objetos PHP5 y nos gustaría combinar el contenido de uno en el segundo. No existe una noción de subclases entre ellas, por lo que las soluciones descritas en el siguiente tema no pueden aplicarse.
¿Cómo se copia un objeto PHP en un tipo de objeto diferente
//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;
Observaciones:
- Estos son objetos, no clases.
- Los objetos contienen bastantes campos, por lo que un foreach sería bastante lento.
- Hasta ahora consideramos la transformación de los objetos A y B en matrices y luego fusionándolos con array_merge () antes de transformarnos en un objeto, pero no podemos decir que estamos orgullosos de ello.
¡Seamos sencillos!
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);
}
Si eso no responde a tu pregunta, seguramente ayudará a la respuesta. El crédito por el código anterior va para mí :)
Aquí hay una función que aplanará un objeto o una matriz. Use esto solo si está seguro de que sus claves son únicas. Si tiene claves con el mismo nombre, se sobrescribirán. Deberá colocar esto en una clase y reemplazar "Funciones" con el nombre de su clase. Disfrutar...
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;
}
Entiendo que usar los objetos genéricos [stdClass ()] y lanzarlos como arreglos responde a la pregunta, pero pensé que el Compositor fue una gran respuesta. Sin embargo, sentí que podría usar algunas mejoras de características y podría ser útil para otra persona.
caracteristicas:
- Especificar referencia o clon
- Especifique la primera o la última entrada para tener prioridad.
- Objeto múltiple (más de dos) que se fusiona con una sintaxis similar a array_merge
- Método de enlace: $ obj-> f1 () -> f2 () -> f3 () ...
- Compuestos dinámicos : $ obj-> fusionar (...) / * trabajar aquí * / $ obj-> fusionar (...)
Código:
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;
}
}
Uso:
$obj = new Compositor(use_reference, first_precedence);
$obj->merge([object $object [, object $object [, object $...]]]);
$obj->with([object $object]);
Ejemplo:
$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'';
Este fragmento de código convertirá recursivamente esos datos a un solo tipo (matriz u objeto) sin los bucles foreach anidados. Espero que ayude a alguien!
Una vez que un Objeto está en formato de matriz, puede usar array_merge y volver a convertir a Objeto si lo necesita.
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;
}
}
Vía procesal
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;
}
Todo el crédito es para: Jason Oakley
Iría con vincular el segundo objeto en una propiedad del primer objeto. Si el segundo objeto es el resultado de una función o método, use referencias. Ex:
//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'');
La /ArrayObject
tiene la posibilidad de intercambiar la matriz actual para desconectar la referencia original. Para hacerlo, viene con dos métodos prácticos: exchangeArray()
y getArrayCopy()
. El resto es simple array_merge()
del objeto proporcionado con las propiedades públicas de ArrayObject
:
class MergeBase extends ArrayObject
{
public final function merge( Array $toMerge )
{
$this->exchangeArray( array_merge( $this->getArrayCopy(), $toMerge ) );
}
}
El uso es tan fácil como esto:
$base = new MergeBase();
$base[] = 1;
$base[] = 2;
$toMerge = [ 3,4,5, ];
$base->merge( $toMerge );
Para fusionar cualquier cantidad de objetos en bruto
function merge_obj(){
foreach(func_get_args() as $a){
$objects[]=(array)$a;
}
return (object)call_user_func_array(''array_merge'', $objects);
}
Podría crear otro objeto que envíe llamadas a métodos mágicos a los objetos subyacentes. Así es como manejaría __get
, pero para que funcione __get
, tendría que anular todos los métodos mágicos relevantes. Es probable que encuentre errores de sintaxis ya que acabo de ingresarlo por la parte superior de mi cabeza.
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;
}
}
}
Buena suerte.
Si sus objetos solo contienen campos (sin métodos), esto funciona:
$obj_merged = (object) array_merge((array) $obj1, (array) $obj2);
Una solución Para preservar, tanto los métodos como las propiedades de los objetos combinados es crear una clase combinadora que pueda
- tomar cualquier número de objetos en __construir
- acceder a cualquier método usando __call
- acceder a cualquier propiedad usando __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'';
}
}
uso simple
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;
Una solución muy simple considerando que tienes el objeto A y B:
foreach($objB AS $var=>$value){
$objA->$var = $value;
}
Eso es todo. Ahora tienes objA con todos los valores de objB.
foreach($objectA as $k => $v) $objectB->$k = $v;