サードパーティのストレージシステムを使用しており、何らかの理由で何を入力してもstdClassオブジェクトのみを返します。したがって、stdClassオブジェクトを特定の型の完全なオブジェクトにキャスト/変換する方法があるかどうか知りたいです。
たとえば、次の行に沿ったもの:
//$stdClass is an stdClass instance
$converted = (BusinessClass) $stdClass;
StdClassを配列にキャストしてBusinessClassコンストラクターに渡すだけですが、気付いていない初期クラスを復元する方法があるかもしれません。
注:「ストレージシステムを変更する」タイプの回答は、興味の対象ではないので興味がありません。言語能力に関する学術的な質問と考えてください。
乾杯
キャストの可能性については、 タイプジャグリングのマニュアル を参照してください。
許可されるキャストは次のとおりです。
マッパーを書く が必要で、stdClassから別の具象クラスにキャストします。難しいことではありません。
または、ハックのような気分であれば、次のコードを適応させることができます。
function arrayToObject(array $array, $className) {
return unserialize(sprintf(
'O:%d:"%s"%s',
strlen($className),
$className,
strstr(serialize($array), ':')
));
}
配列を特定のクラスのオブジェクトに擬似キャストします。これは、最初に配列をシリアル化し、次に特定のクラスを表すようにシリアル化されたデータを変更することで機能します。結果は、このクラスのインスタンスにシリアライズされません。しかし、私が言ったように、それはハックですので、副作用を期待してください。
オブジェクト間では、コードは次のようになります
function objectToObject($instance, $className) {
return unserialize(sprintf(
'O:%d:"%s"%s',
strlen($className),
$className,
strstr(strstr(serialize($instance), '"'), ':')
));
}
上記の関数を使用して、類似しないクラスオブジェクトをキャストできます(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);
stdClass
の既存のプロパティをすべて、指定したクラス名の新しいオブジェクトに移動するには:
/**
* recast stdClass object to an object with type
*
* @param string $className
* @param stdClass $object
* @throws InvalidArgumentException
* @return mixed new, typed object
*/
function recast($className, stdClass &$object)
{
if (!class_exists($className))
throw new InvalidArgumentException(sprintf('Inexistant class %s.', $className));
$new = new $className();
foreach($object as $property => &$value)
{
$new->$property = &$value;
unset($object->$property);
}
unset($value);
$object = (unset) $object;
return $new;
}
使用法:
$array = array('h','n');
$obj=new stdClass;
$obj->action='auth';
$obj->params= &$array;
$obj->authKey=md5('i');
class RestQuery{
public $action;
public $params=array();
public $authKey='';
}
$restQuery = recast('RestQuery', $obj);
var_dump($restQuery, $obj);
出力:
object(RestQuery)#2 (3) {
["action"]=>
string(4) "auth"
["params"]=>
&array(2) {
[0]=>
string(1) "h"
[1]=>
string(1) "n"
}
["authKey"]=>
string(32) "865c0c0b4ab0e063e5caa3387c1a8741"
}
NULL
new
演算子が必要なパラメーターが不明であるため、これは制限されています。あなたの場合はおそらくフィッティングです。
よく似た問題があります。簡素化された反射ソリューションは私にとってはうまくいきました:
public static function cast($destination, \stdClass $source)
{
$sourceReflection = new \ReflectionObject($source);
$sourceProperties = $sourceReflection->getProperties();
foreach ($sourceProperties as $sourceProperty) {
$name = $sourceProperty->getName();
$destination->{$name} = $source->$name;
}
return $destination;
}
誰かがこれが役に立つことを願っています
// new instance of stdClass Object
$item = (object) array(
'id' => 1,
'value' => 'test object',
);
// cast the stdClass Object to another type by passing
// the value through constructor
$casted = new ModelFoo($item);
// OR..
// cast the stdObject using the method
$casted = new ModelFoo;
$casted->cast($item);
class Castable
{
public function __construct($object = null)
{
$this->cast($object);
}
public function cast($object)
{
if (is_array($object) || is_object($object)) {
foreach ($object as $key => $value) {
$this->$key = $value;
}
}
}
}
class ModelFoo extends Castable
{
public $id;
public $value;
}
ディープキャストの関数を変更(再帰を使用)
/**
* Translates type
* @param $destination Object destination
* @param stdClass $source Source
*/
private static function Cast(&$destination, stdClass $source)
{
$sourceReflection = new \ReflectionObject($source);
$sourceProperties = $sourceReflection->getProperties();
foreach ($sourceProperties as $sourceProperty) {
$name = $sourceProperty->getName();
if (gettype($destination->{$name}) == "object") {
self::Cast($destination->{$name}, $source->$name);
} else {
$destination->{$name} = $source->$name;
}
}
}
ところで:主にデシリアライゼーションがオブジェクトのタイプを壊し、DateTimeオブジェクトを含むstdclassに変わるため、シリアライズする場合、変換は非常に重要です。
@Jadrovskiの例を更新し、オブジェクトと配列を使用できるようになりました。
例
$stdobj=new StdClass();
$stdobj->field=20;
$obj=new SomeClass();
fixCast($obj,$stdobj);
サンプル配列
$stdobjArr=array(new StdClass(),new StdClass());
$obj=array();
$obj[0]=new SomeClass(); // at least the first object should indicates the right class.
fixCast($obj,$stdobj);
コード:(再帰的)。ただし、配列で再帰的かどうかはわかりません。追加のis_arrayが欠落している可能性があります
public static function fixCast(&$destination,$source)
{
if (is_array($source)) {
$getClass=get_class($destination[0]);
$array=array();
foreach($source as $sourceItem) {
$obj = new $getClass();
fixCast($obj,$sourceItem);
$array[]=$obj;
}
$destination=$array;
} else {
$sourceReflection = new \ReflectionObject($source);
$sourceProperties = $sourceReflection->getProperties();
foreach ($sourceProperties as $sourceProperty) {
$name = $sourceProperty->getName();
if (is_object(@$destination->{$name})) {
fixCast($destination->{$name}, $source->$name);
} else {
$destination->{$name} = $source->$name;
}
}
}
}
businessClassに新しいメソッドを追加することを検討してください。
public static function fromStdClass(\stdClass $in): BusinessClass
{
$out = new self();
$reflection_object = new \ReflectionObject($in);
$reflection_properties = $reflection_object->getProperties();
foreach ($reflection_properties as $reflection_property)
{
$name = $reflection_property->getName();
if (property_exists('BusinessClass', $name))
{
$out->{$name} = $in->$name;
}
}
return $out;
}
次に、$ stdClassから新しいBusinessClassを作成できます。
$converted = BusinessClass::fromStdClass($stdClass);
さらに別のアプローチ。
最近のPHP 7バージョン。
$theStdClass = (object) [
'a' => 'Alpha',
'b' => 'Bravo',
'c' => 'Charlie',
'd' => 'Delta',
];
$foo = new class($theStdClass) {
public function __construct($data) {
if (!is_array($data)) {
$data = (array) $data;
}
foreach ($data as $prop => $value) {
$this->{$prop} = $value;
}
}
public function Word4Letter($letter) {
return $this->{$letter};
}
};
print $foo->Word4Letter('a') . PHP_EOL; // Alpha
print $foo->Word4Letter('b') . PHP_EOL; // Bravo
print $foo->Word4Letter('c') . PHP_EOL; // Charlie
print $foo->Word4Letter('d') . PHP_EOL; // Delta
print $foo->Word4Letter('e') . PHP_EOL; // PHP Notice: Undefined property
この例では、$ fooは、コンストラクターの唯一のパラメーターとして1つの配列またはstdClassを受け取る匿名クラスとして初期化されています。
最終的に、渡されたオブジェクトに含まれる各アイテムをループ処理し、オブジェクトのプロパティに動的に割り当てます。
このapprochイベントをより汎用的にするには、stdClassをキャストできるようにする任意のクラスに実装するインターフェースまたはTraitを作成します。