Używam systemu pamięci masowej innej firmy, który zwraca mi tylko obiekty stdClass bez względu na to, co wprowadzam z jakiegoś niejasnego powodu. Jestem więc ciekawy, czy istnieje sposób na rzutowanie / konwertowanie obiektu stdClass na pełnoprawny obiekt danego typu.
Na przykład coś w rodzaju:
//$stdClass is an stdClass instance
$converted = (BusinessClass) $stdClass;
Po prostu rzutuję stdClass na tablicę i przesyłam ją do konstruktora BusinessClass, ale może jest sposób na przywrócenie początkowej klasy, której nie jestem świadomy.
Uwaga: nie interesują mnie odpowiedzi typu „Zmień system przechowywania”, ponieważ nie jest to punkt zainteresowania. Proszę potraktować to bardziej jako akademickie pytanie dotyczące umiejętności językowych.
Twoje zdrowie
PDOStatement::fetchObject
zrealizować to zadanie?Odpowiedzi:
Zobacz podręcznik Żonglowania typami na temat możliwych rzutów.
Dozwolone rzuty to:
Musiałbyś napisać Mappera, który wykonuje rzutowanie ze stdClass na inną konkretną klasę. Nie powinno być to zbyt trudne.
Lub, jeśli jesteś w nastroju hackerskim, możesz dostosować następujący kod:
function arrayToObject(array $array, $className) { return unserialize(sprintf( 'O:%d:"%s"%s', strlen($className), $className, strstr(serialize($array), ':') )); }
który pseudokastruje tablicę do obiektu określonej klasy. Działa to najpierw poprzez serializację tablicy, a następnie zmianę zserializowanych danych tak, aby reprezentowały określoną klasę. Wynik jest następnie odserializowany do instancji tej klasy. Ale jak powiedziałem, jest hackerski, więc spodziewaj się efektów ubocznych.
W przypadku obiektu do obiektu kod byłby
function objectToObject($instance, $className) { return unserialize(sprintf( 'O:%d:"%s"%s', strlen($className), $className, strstr(strstr(serialize($instance), '"'), ':') )); }
źródło
__PHP_Incomplete_Class
obiekt używając tej metody (przynajmniej od PHP 5.6).fooBar
s (prywatny odexample\Foo
i publiczny od stdClass). Zamiast tego zastępuje wartość.Możesz użyć powyższej funkcji do rzutowania nie podobnych obiektów klas (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; }
PRZYKŁAD:
class A { private $_x; } class B { public $_x; } $a = new A(); $b = new B(); $x = cast('A',$b); $x = cast('B',$a);
źródło
@Given the user :username has the id :id
do mojej funkcji i obsłużyć ją z odbiciami w klasie kontekstu$destination = new $destination();
można je zamienić,$destination = ( new ReflectionClass( $destination ) )->newInstanceWithoutConstructor();
jeśli chcesz uniknąć wywoływania konstruktora.Aby przenieść wszystkie istniejące właściwości a
stdClass
do nowego obiektu o określonej nazwie klasy:/** * 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; }
Stosowanie:
$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);
Wynik:
object(RestQuery)#2 (3) { ["action"]=> string(4) "auth" ["params"]=> &array(2) { [0]=> string(1) "h" [1]=> string(1) "n" } ["authKey"]=> string(32) "865c0c0b4ab0e063e5caa3387c1a8741" } NULL
Jest to ograniczone ze względu na
new
operatora, ponieważ nie wiadomo, jakich parametrów będzie potrzebować. Prawdopodobnie pasuje do twojego przypadku.źródło
Mam bardzo podobny problem. Uproszczone rozwiązanie odbicia działało dobrze dla mnie:
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; }
źródło
Mam nadzieję, że ktoś uzna to za przydatne
// 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; }
źródło
Zmieniono funkcję do głębokiego rzutowania (przy użyciu rekursji)
/** * 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; } } }
źródło
I jeszcze jedno podejście wykorzystujące wzorzec dekoratora i magiczne metody pobierające i ustawiające PHP:
// A simple StdClass object $stdclass = new StdClass(); $stdclass->foo = 'bar'; // Decorator base class to inherit from class Decorator { protected $object = NULL; public function __construct($object) { $this->object = $object; } public function __get($property_name) { return $this->object->$property_name; } public function __set($property_name, $value) { $this->object->$property_name = $value; } } class MyClass extends Decorator {} $myclass = new MyClass($stdclass) // Use the decorated object in any type-hinted function/method function test(MyClass $object) { echo $object->foo . '<br>'; $object->foo = 'baz'; echo $object->foo; } test($myclass);
źródło
rozważ dodanie nowej metody do 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; }
następnie możesz utworzyć nową BusinessClass z $ stdClass:
źródło
Jeszcze inne podejście.
Dzięki najnowszej wersji PHP 7 możliwe jest teraz to.
$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
W tym przykładzie $ foo jest inicjowane jako klasa anonimowa, która przyjmuje jedną tablicę lub stdClass jako jedyny parametr dla konstruktora.
Ostatecznie przechodzimy przez każdy element zawarty w przekazanym obiekcie i dynamicznie przypisujemy go do właściwości obiektu.
Aby uczynić to wydarzenie bardziej ogólnym, możesz napisać interfejs lub cechę, którą zaimplementujesz w dowolnej klasie, w której chcesz mieć możliwość rzutowania stdClass.
źródło
BTW: Konwersja jest bardzo ważna, jeśli jesteś serializowany, głównie dlatego, że deserializacja przerywa typ obiektów i zamienia się w klasę standardową, w tym obiekty DateTime.
Zaktualizowałem przykład @Jadrovski, teraz zezwala na obiekty i tablice.
przykład
$stdobj=new StdClass(); $stdobj->field=20; $obj=new SomeClass(); fixCast($obj,$stdobj);
przykładowa tablica
$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);
kod: (rekurencyjny). Jednak nie wiem, czy jest to cykliczne z tablicami. Może brakować dodatkowej tablicy 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; } } } }
źródło
Przekonwertuj go na tablicę, zwróć pierwszy element tej tablicy i ustaw parametr powrotu na tę klasę. Teraz powinieneś otrzymać autouzupełnianie dla tej klasy, ponieważ zarejestruje ją jako tę klasę zamiast standardowej.
/** * @return Order */ public function test(){ $db = new Database(); $order = array(); $result = $db->getConnection()->query("select * from `order` where productId in (select id from product where name = 'RTX 2070')"); $data = $result->fetch_object("Order"); //returns stdClass array_push($order, $data); $db->close(); return $order[0]; }
źródło