Last active
September 19, 2019 15:06
-
-
Save lt/269810c917f2dbd1e09efe56688c441a to your computer and use it in GitHub Desktop.
Decode JSON into a specific class
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
function deep_copy_and_validate_object($source, $destination) | |
{ | |
$reflectionClass = new \ReflectionClass($destination); | |
foreach ($reflectionClass->getProperties() as $reflectionProperty) { | |
$propertyName = $reflectionProperty->getName(); | |
if (property_exists($source, $propertyName)) { | |
$propertyValue = $source->$propertyName; | |
$expectedClass = false; | |
$expectedArray = false; | |
$expectedType = false; | |
$comment = $reflectionProperty->getDocComment(); | |
if (is_string($comment) && preg_match('~^(?:/\*|\s*)\*\s*@var\s+\$([^\[\]\s]+)(\[\])?\s+(\S+)~m', $comment, $match)) { | |
if ($match[1] === $propertyName) { | |
$expectedArray = !empty($match[2]); | |
if (class_exists($match[3])) { | |
$expectedClass = $match[3]; | |
} else { | |
$expectedType = $match[3]; | |
} | |
} | |
} | |
if ($expectedClass && is_object($propertyValue)) { | |
if ($expectedArray) { | |
throw new \DomainException('Expected an array but got an object: ' . $propertyName); | |
} | |
$tempDestination = new $expectedClass; | |
deep_copy_and_validate_object($propertyValue, $tempDestination); | |
$propertyValue = $tempDestination; | |
} | |
else if ($expectedArray && is_array($propertyValue)) { | |
if ($expectedClass) { | |
$tempArray = []; | |
foreach ($propertyValue as $tempSource) { | |
if (!is_object($tempSource)) { | |
throw new \DomainException('Array contains value that cannot be copied to annotated class'); | |
} | |
$tempDestination = new $expectedClass; | |
deep_copy_and_validate_object($tempSource, $tempDestination); | |
$tempArray[] = $tempDestination; | |
} | |
$propertyValue = $tempArray; | |
} | |
else { | |
foreach ($propertyValue as $arrayValue) { | |
$actualType = gettype($arrayValue); | |
if ($actualType !== $expectedType) { | |
throw new \DomainException('Expected type ' . $expectedType . ' but got type ' . $actualType . ': ' . $propertyName); | |
} | |
} | |
} | |
} | |
else if ($expectedClass || $expectedArray) { | |
$actualType = gettype($propertyValue); | |
if ($expectedClass) { | |
throw new \DomainException('Expected an object but got type ' . $actualType . ': ' . $propertyName); | |
} | |
if ($expectedArray) { | |
throw new \DomainException('Expected an array but got type ' . $actualType . ': ' . $propertyName); | |
} | |
} | |
else if ($expectedType) { | |
$actualType = gettype($propertyValue); | |
if ($actualType !== $expectedType) { | |
throw new \DomainException('Expected type ' . $expectedType . ' but got type ' . $actualType . ': ' . $propertyName); | |
} | |
} | |
$reflectionProperty->setAccessible(true); | |
$reflectionProperty->setValue($destination, $propertyValue); | |
} | |
} | |
} | |
function json_decode_class(string $json, $destination, int $depth = 512, int $options = 0) | |
{ | |
$decodedObject = json_decode($json, false, $depth, $options); | |
if (!is_object($decodedObject)) { | |
return $decodedObject; | |
} | |
if (is_string($destination) && class_exists($destination)) { | |
$destination = new $destination; | |
} | |
else if (!is_object($destination)) { | |
throw new InvalidArgumentException('$class must be the fully qualified name of an existing class, or an object'); | |
} | |
deep_copy_and_validate_object($decodedObject, $destination); | |
return $destination; | |
} | |
class C2 { | |
/** @var $qux string */ | |
private $qux; | |
} | |
class C1 { | |
/** @var $foo C2 */ | |
public $foo; | |
/** @var $bar[] C2 */ | |
public $bar; | |
} | |
$json = <<<EOJ | |
{ | |
"foo": { | |
"qux": "Hello" | |
}, | |
"bar": [ | |
{ | |
"qux": "X" | |
}, | |
{ | |
"qux": "Y" | |
}, | |
{ | |
"qux": "Z" | |
} | |
], | |
"does not exist": "does not get copied" | |
} | |
EOJ; | |
$obj = json_decode_class($json, 'C1'); | |
var_dump($obj); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment