As Gordon says is not possible. But if you are looking for a way to obtain a string that can be decoded as an instance of a give class you can use serialize and unserialize instead.
Not automatically. But you can do it the old fashioned route.
$data = json_decode($json, true);
$class = new Whatever();
foreach ($data as $key => $value) $class->{$key} = $value;
Or alternatively, you could make that more automatic:
class Whatever {
public function set($data) {
foreach ($data AS $key => $value) $this->{$key} = $value;
}
}
$class = new Whatever();
$class->set($data);
Edit: getting a little fancier:
class JSONObject {
public function __construct($json = false) {
if ($json) $this->set(json_decode($json, true));
}
public function set($data) {
foreach ($data AS $key => $value) {
if (is_array($value)) {
$sub = new JSONObject;
$sub->set($value);
$value = $sub;
}
$this->{$key} = $value;
}
}
}
// These next steps aren't necessary. I'm just prepping test data.
$data = array(
"this" => "that",
"what" => "who",
"how" => "dy",
"multi" => array(
"more" => "stuff"
)
);
$jsonString = json_encode($data);
// Here's the sweetness.
$class = new JSONObject($jsonString);
print_r($class);
JSON is a simple protocol to transfer data between various programming languages (and it's also a subset of JavaScript) which supports just certain types: numbers, strings, arrays/lists, objects/dicts. Objects are just key=value maps and Arrays are ordered lists.
So there is no way to express custom objects in a generic way. The solution is defining a structure where your program(s) will know that it's a custom object.
You can do it - it's a kludge but totally possible. We had to do when we started storing things in couchbase.
$stdobj = json_decode($json_encoded_myClassInstance); //JSON to stdClass
$temp = serialize($stdobj); //stdClass to serialized
// Now we reach in and change the class of the serialized object
$temp = preg_replace('@^O:8:"stdClass":@','O:7:"MyClass":',$temp);
// Unserialize and walk away like nothing happend
$myClassInstance = unserialize($temp); // Presto a php Class
In our benchmarks this was way faster than trying to iterate through all the class variables.
Caveat: Won't work for nested objects other than stdClass
Edit: keep in mind the data source, it's strongly recommended that you don't do this withe untrusted data from users without a very carful analysis of the risks.
I once created an abstract base class for this purpose. Let's call it JsonConvertible. It should serialize and deserialize the public members. This is possible using Reflection and late static binding.
abstract class JsonConvertible {
static function fromJson($json) {
$result = new static();
$objJson = json_decode($json);
$class = new \ReflectionClass($result);
$publicProps = $class->getProperties(\ReflectionProperty::IS_PUBLIC);
foreach ($publicProps as $prop) {
$propName = $prop->name;
if (isset($objJson->$propName) {
$prop->setValue($result, $objJson->$propName);
}
else {
$prop->setValue($result, null);
}
}
return $result;
}
function toJson() {
return json_encode($this);
}
}
class MyClass extends JsonConvertible {
public $name;
public $whatever;
}
$mine = MyClass::fromJson('{"name": "My Name", "whatever": "Whatever"}');
echo $mine->toJson();
Just from memory, so probably not flawless. You will also have to exclude static properties and may give derived classes the chance to make some properties ignored when serialized to/from json. I hope you get the idea, nonetheless.
<?php
class CatalogProduct
{
public $product_id;
public $sku;
public $name;
public $set;
public $type;
public $category_ids;
public $website_ids;
function __construct(array $data)
{
foreach($data as $key => $val)
{
if(property_exists(__CLASS__,$key))
{
$this->$key = $val;
}
}
}
}
You can make a wrapper for your object and make the wrapper look like it is the object itself. And it will work with multilevel objects.
<?php
class Obj
{
public $slave;
public function __get($key) {
return property_exists ( $this->slave , $key ) ? $this->slave->{$key} : null;
}
public function __construct(stdClass $slave)
{
$this->slave = $slave;
}
}
$std = json_decode('{"s3":{"s2":{"s1":777}}}');
$o = new Obj($std);
echo $o->s3->s2->s1; // you will have 777
This worked perfectly for my use case. However Yevgeniy Afanasyev's response seems equally promising to me. It could be possible to have your class have an extra "constructor", like so:
public static function withJson(string $json) {
$instance = new static();
// Do your thing
return $instance;
}
EDIT: I have been using karriereat/json-decoder for some time now, and I have had absolutely no trouble with it. It is lightweight and very easily extensible. Here's an example of a binding I wrote to deserialize JSON into a Carbon/CarbonImmutable object.
use App\Model\Person;
$person = new Person();
$person->setName('foo');
$person->setAge(99);
$person->setSportsperson(false);
$jsonContent = $serializer->serialize($person, 'json');
// $jsonContent contains {"name":"foo","age":99,"sportsperson":false,"createdAt":null}
echo $jsonContent; // or return it in a Response
Deserializing from JSON to Object: (this example uses XML just to demonstrate the flexibility of formats)
class Bar{public $bar = " Boss";}
class Bas
{
public $ber ;
public $bas=" Boven";
public function __construct()
{$this->ber = new Bar;}
}
class Baz
{
public $bes ;
public $baz=" Baaz";
public function __construct()
{$this->bes = new Bas;}
}
$Bazjson = '{"bes":{"ber":{"bar":"Baas"}}}';
$Bazobj = new Baz;
loadJSON($Bazobj, $Bazjson);
var_dump($Bazobj);