web-dev-qa-db-ja.com

PHPでオブジェクトをキャストする方法

いくつかの属性を共有するいくつかのクラスがあり、次のようなことをしたいと思います。

$dog = (Dog) $cat;

それは可能ですか、または一般的な回避策はありますか?

スーパークラスでも、インターフェイスでも、何らかの形で関連するものでもありません。それらは2つの異なるクラスであり、phpでcatクラスの属性をdogにマッピングし、新しいオブジェクトを提供します。 –

私はもう少し原因を特定する必要があると思います。

異なる親クラスから継承するクラスがあるため、保存方法に基づいて継承ツリーを作成しました。最初から悪いかもしれませんが、問題は実際には同等であるが相互作用するクラスがたくさんあることですmysqlともう1つはxmlファイルを使用します。ので、私は持っています:

class MySql_SomeEntity extends SomeMysqlInteract{}

そして

Xml_SomeEntity extends SomeXmlInteract{}

少し深いツリーですが、問題はそれです。私はそれらを同じクラスから継承させることができないため、複数の継承は許可されず、スーパークラスの原因との現在の相互作用を分けることは大きな問題になるでしょう。

基本的に、それぞれの属性は実際的に同じです。

私はこの一致するクラスがたくさんあるので、変換(値を各属性に渡す)できる一般的なキャストまたはそのような何かをしたいと思いますが、このクラスの全員に最も簡単な方法を検索しようとしています。

57
user267599

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()を使用します。

この記事 は、「PHPの暗いコーナー」とユーザーレベルでの型キャストの実装について説明します。

30
user250120

上記の関数を使用して、類似しないクラスオブジェクトをキャストできます(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);
30
Adam Puza

本当にやりたいことは interface を実装することのように思えます。

インターフェイスはオブジェクトが処理できるメソッドを指定します。インターフェイスをサポートするオブジェクトが必要なメソッドにインターフェイスを実装するオブジェクトを渡すときは、インターフェイスの名前を引数に入力するだけです。

3
Matthew Sturges

継承を使用せずに( 著者による言及 )、開発者を前提として、あるクラスから別のクラスtransformできるソリューションを探しているようです。 2つのクラスの類似性を知っており、理解しています

オブジェクト間で変換するための既存のソリューションはありません。あなたが試すことができるのは:

3
Trav L

キャストしようとしているオブジェクトにユーザー定義クラスでもあるプロパティがあり、リフレクションを実行したくない場合は、これを使用できます。

<?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;
    }
  }
}
1
Justin

より良いアプローチ:

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";
}
1
useless

キャストする必要はありません。すべてが動的です。

クラス割引があります。
このクラスを拡張するいくつかのクラスがあります。
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は割引固有の関数です。

1
darpet

あなたは工場について考えるかもしれません

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ソリューションが唯一のものであり、クラスキャスティングに近づきます。

0
KingCrunch
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
ArthurDent

最善のアプローチは、クラスの新しいインスタンスを作成し、オブジェクトを割り当てることだけだと思います。ここに私がやることがあります:

public function ($someVO) {

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

}

これを行うと、ほとんどのIDEでコードヒントが提供され、正しいプロパティを使用していることを確認できます。

0
Elad Elrom