2010-02-09 4 views
52

Ive некоторые классы, которые делят некоторые атрибуты, и я хотел бы сделать что-то вроде: $ dog = (Dog) $ cat;Как создавать объекты в PHP

Возможно ли это или вообще существует общая работа?

Его не суперкласс, ни интерфейс, ни связанное каким-либо образом. Они всего лишь две разные кланы, я хотел бы, чтобы php сопоставлял атрибуты от класса кошки собаке и давал мне новый объект. -

Я предполагаю, что я хочу указать немного больше причины, похожее на бессмысленную вещь.

ive кланы, которые наследуют от родительских кланов diferents, потому что ive создало дерево наследования, основанное на методе сохранения, возможно, мое плохое с самого начала, но проблема в том, что у меня есть много кланов, которые практически равны, но взаимодействуют с mysql и otherone с файлами xml. поэтому у меня есть: класс MySql_SomeEntity extends SomeMysqlInteract {} и Xml_SomeEntity расширяет SomeXmlInteract {} его немного более глубокое дерево, но проблема его в том. я не могу заставить их наследовать от одного и того же класса, потому что многоуровневое наследование не оговорено, и я не могу разделить текущее взаимодействие с суперклассами, потому что это будет большой проблемой.

В принципе, атрибуты в каждом из них практичны одинаково.

, так как у меня есть много таких махинаций, я хотел бы сделать какое-то общее литье или что-то в этом роде, которое может конвертировать (передать значения каждому атрибуту), но я пытаюсь найти самый простой способ для всех этих кладов ,

+0

@Ignacio, нет, я не согласен. @ user267599 ищет для cast from inheritenace в PHP5, который я не думаю, что он существует. '$ dog = (Dog) $ four_legged_animal' будет лучшим примером. –

+0

не совсем. Это не суперкласс, ни интерфейс, ни связанные с ним. Они всего лишь две разные кланы, я хотел бы, чтобы php сопоставлял атрибуты от класса кошки собаке и давал мне новый объект. – user267599

+0

В качестве комментария, если вы собираетесь делать что-то вроде '$ dog = (Dog) $ cat', вы также не должны: называть свой объект' Dog' (или 'Cat') в первую очередь; или перебрасывать их таким образом. –

ответ

3

без использования наследования (как mentioned by author), кажется, что вы ищете решение, которое может transform один класс другого с preassumption разработчика знает и понимает сходство 2 классов.

Нет никакого существующего решения для преобразования между объектами. То, что вы можете попробовать это:

+2

Есть ли метод при использовании наследования? – elviejo79

31

Там нет встроенного метода для типа литья, определенных пользователем объектов в PHP , Тем не менее, здесь есть несколько возможных решений:

1) Используйте функцию, подобную приведенной ниже, для десериализации объекта, измените строку так, чтобы нужные свойства были включены в новый объект после десериализации.

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) В качестве альтернативы, вы можете скопировать свойства объекта с помощью отражения/итерация вручную через них все или используя get_object_vars().

This article должен просветить вас на «темных углах PHP» и реализовать типизацию на уровне пользователя.

+0

+1 Я использовал это в прошлом, чтобы преобразовать корзину в SavedCart в таблицу сеансов пользователя MySQL. –

+9

+1 грязный взломать. ;) – cbednarski

+0

Мы используем этот метод, и это работает, это точно. Но это довольно медленно, поскольку 'serialize' и' unserialize' не являются тривиальными. Мы также подозреваем, что это причина критических segfaults и сбоев php. Также вы должны проверить, не является ли объект экземпляром желаемого класса, иначе вы тратите немало ресурсов, у нас много головных болей, так как он преувеличенно замедляет наш сайт в два раза ... Так что будьте осторожны с этим. –

1

Вам не требуется литье. Все динамично.

У меня есть класс Скидка.
У меня есть несколько классов, которые распространяется этот класс:
ProductDiscount
StoreDiscount
ShippingDiscount
...

Где-то в коде у меня есть:

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

И где-то у меня есть:

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 

Где getDiscountAmount - это специальная функция ProductDiscount, а getDiscountType - специальная функция скидок.

+0

Вы используете только функции «Скидка», вы не можете использовать функции «ProductDiscount» в своем примере. Это не литье, вы только назвали свой объект «Скидка» «ProductDiscount», который теперь по-прежнему является «скидкой». Сделайте var_dump(); – Daniel

0

Вы можете думать о заводах

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

В противном случае user250120s решение является единственным, который приближается к классу литья.

28

Вы можете использовать выше функции для литья не аналогичные объекты класса (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; 
} 

Пример:

class A 
{ 
    private $_x; 
} 

class B 
{ 
    public $_x; 
} 

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

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

Примечание: это вообще не обрабатывает наследование. Предположим, что вы хотите отбросить с одного дочернего объекта на другой дочерний объект, он не будет копировать свойства родителя. – 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); 

Возвращает:

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

На самом деле это не очень важно - и у него уже есть твердый ответ. –

2

It похоже на то, что вы действительно хотите сделать, это impl ement a interface.

В вашем интерфейсе будут указаны методы, которые может обрабатывать объект, и когда вы передаете объект, реализующий интерфейс, методу, который хочет объект, поддерживающий интерфейс, просто введите аргумент с именем интерфейса.

0

Я думаю, что лучший подход - просто создать новый экземпляр класса и назначить объект. Вот что я хотел бы сделать:

public function ($someVO) { 

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

} 

Делая это даст вам подсказку коды в большинстве Иды и помогут гарантировать, что Вы используете правильные свойства.

+0

Это волшебство? Нет имени функции, нет возвращаемого значения? А второй оператор не переопределяет первый? – robsch

+1

@robsch Второй оператор действительно перезаписывает значение переменной. Первый оператор существует только там, поэтому ваша среда IDE будет знать, в каком классе он должен включить завершение кода и намек на работу. Однако он не делает ничего, чтобы гарантировать, что $ someVO имеет правильный тип. Это даже не должно быть классом, хотя это может привести к ошибкам очень быстро, например. в третьем заявлении. Это ** не ** бросает объект вообще. –

1

лучше Подход:

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

Пожалуйста, проверьте мои изменения. Я добавил несколько строк, которые позволят нам динамически ** ** получить значения всех свойств от источника до целевого объекта, вместо того, чтобы делать это вручную. Но yehp, я предпочитаю ваш ответ, а не тот, который стоит сверху :) –

+1

@AllenLinatoc Ваше изменение было бы отклонено, потому что вы существенно изменили исходный ответ. Лучшим подходом было бы опубликовать свой собственный ответ с вашими улучшениями, не считая * бесполезным * для придумывания исходного кода. –

0

Если объект, который вы пытаетесь отлиты из или имеет свойства, которые также определенные пользователем классы, и вы не хотите, чтобы пройти через отражение, вы можете использовать это.

<?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; 
    } 
    } 
} 
Смежные вопросы