2010-02-09 12 views
52

Ive algunas clases que comparten algunos atributos, y me gustaría hacer algo como: $ dog = (Dog) $ cat;Cómo enviar objetos en PHP

¿Es posible o hay algún trabajo genérico?

No es una superclase, o una interfaz o relacionada de ninguna manera. Son solo 2 clases diferentes. Me gustaría que php asigne los atributos de una clase de gato a un perro y me proporcione el nuevo objeto. -

Supongo que tengo que especificar un poco más de causa porque parece una cosa sin sentido.

ive clases que heredan de diferentes clases de padres porque he hecho un árbol de herencia basado en el método de ahorro, tal vez sea malo desde el principio, pero el problema es que tengo muchas clases que son prácticamente iguales pero interactúa con mysql y el otro con archivos xml. así que tengo: clase MySql_SomeEntity extiende SomeMysqlInteract {} y Xml_SomeEntity extiende SomeXmlInteract {} es un árbol un poco más profundo, pero el problema es eso. No puedo hacer que hereden de la misma clase porque no se permite la herencia múltiple, y no puedo separar la interacción actual con las superclases porque sería un gran problema.

Básicamente los atributos en cada uno son prácticos de la misma manera.

ya que tengo muchas de estas clases de mecanizado me gustaría hacer algo de fundición genérica o algo similar que pueda convertir (pasar los valores a cada atributo) y estoy tratando de buscar la forma más simple para cada una de estas clases .

+0

@Ignacio, no, estoy en desacuerdo. @ user267599 está buscando yeso de inheritenace en PHP5, que no creo que exista. '$ dog = (Perro) $ four_legged_animal' sería un mejor ejemplo. –

+0

no del todo. No es una superclase, o una interfaz o relacionada de ninguna manera. Son solo 2 clases diferentes. Me gustaría que php asigne los atributos de una clase de gato a un perro y me proporcione el nuevo objeto. – user267599

+0

En una nota al pie como comentario, si vas a hacer algo como '$ dog = (Dog) $ cat' No deberías tampoco: llamar a tu objeto' Dog' (o 'Cat') en primer lugar; o lanzándolos como tal. –

Respuesta

3

Sin utilizar la herencia (como mentioned by author), parece que usted está buscando una solución que puede transform una clase a otra con preassumption del desarrollador sabe y entender la similitud de 2 clases.

No existe una solución para transformar objetos. Lo que puede probar son:

+2

¿Hay algún método al usar herencia? – elviejo79

31

No hay ningún método integrado para la conversión de tipos de objetos definidos por el usuario en PHP . Dicho esto, aquí hay varias soluciones posibles:

1) Utilice una función como la de abajo para deserializar el objeto, modifique la cadena para que las propiedades que necesita estén incluidas en el nuevo objeto una vez que se haya deserializado.

function cast($obj, $to_class) { 
    if(class_exists($to_class)) { 
    $obj_in = serialize($obj); 
    $obj_out = 'O:' . strlen($to_class) . ':"' . $to_class . '":' . substr($obj_in, $obj_in[2] + 7); 
    return unserialize($obj_out); 
    } 
    else 
    return false; 
} 

2) Como alternativa, puede copiar las propiedades del objeto utilizando la reflexión/iteración de forma manual a través de ellos todos o usando get_object_vars().

This article debería informarte sobre las "esquinas oscuras de PHP" e implementar el encasillado en el nivel de usuario.

+0

+1 He utilizado esto en el pasado para convertir el carrito a SavedCart en una tabla de sesión de usuario de MySQL. –

+9

+1 sucio truco. ;) – cbednarski

+0

Usamos este método y funciona, eso es seguro. Pero es bastante lento ya que 'serialize' y' unserialize' no son triviales. También sospechamos que esto es causa de segfeults crípticos y fallas de php. También debe verificar si el objeto ya no es una instancia de la clase deseada, de lo contrario, desperdiciará muchos recursos, nos dio muchos dolores de cabeza ya que ralentizó nuestro sitio dos veces ... Así que tenga cuidado con esto. –

1

No es necesario realizar un casting. Todo es dinámico

Tengo una clase de descuento.
tengo varias clases que se extiende esta clase:
ProductDiscount
StoreDiscount
ShippingDiscount
...

En alguna parte del código que tengo:

$pd = new ProductDiscount(); 
$pd->setDiscount(5, ProductDiscount::PRODUCT_DISCOUNT_PERCENT); 
$pd->setProductId(1); 

$this->discounts[] = $pd; 

..... 

$sd = new StoreDiscount(); 
$sd->setDiscount(5, StoreDiscount::STORE_DISCOUNT_PERCENT); 
$sd->setStoreId(1); 

$this->discounts[] = $sd; 

Y en algún lugar tengo:

foreach ($this->discounts as $discount){ 

    if ($discount->getDiscountType()==Discount::DISCOUNT_TYPE_PRODUCT){ 

     $productDiscount = $discount; // you do not need casting. 
     $amount = $productDiscount->getDiscountAmount($this->getItemTotalPrice()); 
     ... 
    } 

}// foreach 

Donde getDiscountAmount es la función específica ProductDiscount, y getDiscountType es la función específica de descuento.

+0

Solo utiliza las funciones de "Descuento", no puede usar las características de "CuentaProducto" en su muestra. Este no es un elenco, usted solo nombró su objeto "Descuento" "ProductoDiscount" que ahora todavía es un "Descuento". Haz un var_dump(); – Daniel

0

usted puede pensar acerca de las fábricas

class XyFactory { 
    public function createXyObject ($other) { 
     $new = new XyObject($other->someValue); 
     // Do other things, that let $new look like $other (except the used class) 
     return $new; 
    } 
} 

solución de lo contrario user250120s es el único, que se acerca a la fundición clase.

28

Puede utilizar la función anterior para la fundición de objetos de clases que no sean similares (PHP> = 5,3)

/** 
* Class casting 
* 
* @param string|object $destination 
* @param object $sourceObject 
* @return object 
*/ 
function cast($destination, $sourceObject) 
{ 
    if (is_string($destination)) { 
     $destination = new $destination(); 
    } 
    $sourceReflection = new ReflectionObject($sourceObject); 
    $destinationReflection = new ReflectionObject($destination); 
    $sourceProperties = $sourceReflection->getProperties(); 
    foreach ($sourceProperties as $sourceProperty) { 
     $sourceProperty->setAccessible(true); 
     $name = $sourceProperty->getName(); 
     $value = $sourceProperty->getValue($sourceObject); 
     if ($destinationReflection->hasProperty($name)) { 
      $propDest = $destinationReflection->getProperty($name); 
      $propDest->setAccessible(true); 
      $propDest->setValue($destination,$value); 
     } else { 
      $destination->$name = $value; 
     } 
    } 
    return $destination; 
} 

ejemplo:

class A 
{ 
    private $_x; 
} 

class B 
{ 
    public $_x; 
} 

$a = new A(); 
$b = new B(); 

$x = cast('A',$b); 
$x = cast('B',$a); 
+0

Nota: Esto no maneja la herencia en absoluto. Supongamos que desea lanzar desde un objeto secundario a otro objeto secundario, no copiará sobre las propiedades de los padres. – martixy

0
class It { 
    public $a = ''; 

    public function __construct($a) { 
     $this->a = $a; 
    } 
    public function printIt() { 
     ; 
    } 
} 

//contains static function to 'convert' instance of parent It to sub-class instance of Thing 

class Thing extends it { 
    public $b = ''; 

    public function __construct($a, $b) { 
     $this->a = $a; 
     $this->b = $b; 
    } 
    public function printThing() { 
     echo $this->a . $this->b; 
    } 
     //static function housed by target class since trying to create an instance of Thing 
    static function thingFromIt(It $it, $b) { 
     return new Thing($it->a, $b); 
    } 
} 


//create an instance of It 
$it = new It('1'); 

//create an instance of Thing 
$thing = Thing::thingFromIt($it, '2'); 


echo 'Class for $it: ' . get_class($it); 
echo 'Class for $thing: ' . get_class($thing); 

Devuelve:

Class for $it: It 
Class for $thing: Thing 
+0

Esto realmente no da en el clavo, y ya tiene una respuesta sólida. –

2

Se suena como lo que realmente quieres hacer es impl ement a interface.

Su interfaz especificará los métodos que puede manejar el objeto y cuando pasa un objeto que implementa la interfaz a un método que quiere un objeto que admita la interfaz, simplemente escriba el argumento con el nombre de la interfaz.

0

Creo que el mejor enfoque es simplemente crear una nueva instancia de una clase y luego asignar el objeto. Esto es lo que haría:

public function ($someVO) { 

    $someCastVO = new SomeVO(); 
    $someCastVO = $someVO; 
    $someCastVO->SomePropertyInVO = "123"; 

} 

Hacer esto le dará sugerencias de código en la mayoría de entornos de desarrollo y ayudar a asegurarse de que está utilizando las propiedades correctas.

+0

¿Es eso magia? Sin nombre de función, sin valor de retorno? Y la segunda declaración no anula la primera? – robsch

+1

@robsch La segunda declaración sobrescribe el valor de la variable. La primera declaración solo está allí, por lo que su IDE sabrá qué clase es el objeto para permitir que la finalización del código y la sugerencia funcionen. Sin embargo, no hace nada para garantizar que $ someVO sea del tipo correcto. Ni siquiera tiene que ser una clase, aunque eso causará errores muy rápidamente, p. en la tercera declaración. Esto ** no ** arroja el objeto en absoluto. –

1

una mejor aproach:

class Animal 
{ 
    private $_name = null; 

    public function __construct($name = null) 
    { 
     $this->_name = $name; 
    } 

    /** 
    * casts object 
    * @param Animal $to 
    * @return Animal 
    */ 
    public function cast($to) 
    { 
     if ($to instanceof Animal) { 
      $to->_name = $this->_name; 
     } else { 
      throw(new Exception('cant cast ' . get_class($this) . ' to ' . get_class($to))); 
     return $to; 
    } 

    public function getName() 
    { 
     return $this->_name; 
    } 
} 

class Cat extends Animal 
{ 
    private $_preferedKindOfFish = null; 

    public function __construct($name = null, $preferedKindOfFish = null) 
    { 
     parent::__construct($name); 
     $this->_preferedKindOfFish = $preferedKindOfFish; 
    } 

    /** 
    * casts object 
    * @param Animal $to 
    * @return Animal 
    */ 
    public function cast($to) 
    { 
     parent::cast($to); 
     if ($to instanceof Cat) { 
      $to->_preferedKindOfFish = $this->_preferedKindOfFish; 
     } 
     return $to; 
    } 

    public function getPreferedKindOfFish() 
    { 
     return $this->_preferedKindOfFish; 
    } 
} 

class Dog extends Animal 
{ 
    private $_preferedKindOfCat = null; 

    public function __construct($name = null, $preferedKindOfCat = null) 
    { 
     parent::__construct($name); 
     $this->_preferedKindOfCat = $preferedKindOfCat; 
    } 

    /** 
    * casts object 
    * @param Animal $to 
    * @return Animal 
    */ 
    public function cast($to) 
    { 
     parent::cast($to); 
     if ($to instanceof Dog) { 
      $to->_preferedKindOfCat = $this->_preferedKindOfCat; 
     } 
     return $to; 
    } 

    public function getPreferedKindOfCat() 
    { 
     return $this->_preferedKindOfCat; 
    } 
} 

$dogs = array(
    new Dog('snoopy', 'vegetarian'), 
    new Dog('coyote', 'any'), 
); 

foreach ($dogs as $dog) { 
    $cat = $dog->cast(new Cat()); 
    echo get_class($cat) . ' - ' . $cat->getName() . "\n"; 
} 
+0

Por favor revisa mi edición. Agregué varias líneas que nos permitirán obtener ** dinámicamente ** los valores de todas las propiedades del objeto de origen al de destino, _ en lugar de hacerlo manualmente_. Pero yehp, prefiero tu respuesta, en lugar de la que está arriba :) –

+1

@AllenLinatoc Tu edición habría sido rechazada porque estabas cambiando sustancialmente la respuesta original. Un mejor enfoque habría sido publicar su propia respuesta con sus mejoras al tiempo que daba crédito a * inútil * para encontrar el código original. –

0

Si el objeto que está tratando de lanzar desde o hacia tiene propiedades que son también las clases definidas por el usuario, y no quiere pasar por la reflexión, puede utilizar esta.

<?php 
declare(strict_types=1); 
namespace Your\Namespace\Here 
{ 
    use Zend\Logger; // or your logging mechanism of choice 
    final class OopFunctions 
    { 
    /** 
    * @param object $from 
    * @param object $to 
    * @param Logger $logger 
    * 
    * @return object 
    */ 
    static function Cast($from, $to, $logger) 
    { 
     $logger->debug($from); 
     $fromSerialized = serialize($from); 
     $fromName = get_class($from); 
     $toName = get_class($to); 
     $toSerialized = str_replace($fromName, $toName, $fromSerialized); 
     $toSerialized = preg_replace("/O:\d*:\"([^\"]*)/", "O:" . strlen($toName) . ":\"$1", $toSerialized); 
     $toSerialized = preg_replace_callback(
     "/s:\d*:\"[^\"]*\"/", 
     function ($matches) 
     { 
      $arr = explode(":", $matches[0]); 
      $arr[1] = mb_strlen($arr[2]) - 2; 
      return implode(":", $arr); 
     }, 
     $toSerialized 
    ); 
     $to = unserialize($toSerialized); 
     $logger->debug($to); 
     return $to; 
    } 
    } 
} 
Cuestiones relacionadas