123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101 |
- <?php declare(strict_types=1);
- namespace PhpParser;
- class JsonDecoder
- {
- /** @var \ReflectionClass[] Node type to reflection class map */
- private $reflectionClassCache;
- public function decode(string $json) {
- $value = json_decode($json, true);
- if (json_last_error()) {
- throw new \RuntimeException('JSON decoding error: ' . json_last_error_msg());
- }
- return $this->decodeRecursive($value);
- }
- private function decodeRecursive($value) {
- if (\is_array($value)) {
- if (isset($value['nodeType'])) {
- if ($value['nodeType'] === 'Comment' || $value['nodeType'] === 'Comment_Doc') {
- return $this->decodeComment($value);
- }
- return $this->decodeNode($value);
- }
- return $this->decodeArray($value);
- }
- return $value;
- }
- private function decodeArray(array $array) : array {
- $decodedArray = [];
- foreach ($array as $key => $value) {
- $decodedArray[$key] = $this->decodeRecursive($value);
- }
- return $decodedArray;
- }
- private function decodeNode(array $value) : Node {
- $nodeType = $value['nodeType'];
- if (!\is_string($nodeType)) {
- throw new \RuntimeException('Node type must be a string');
- }
- $reflectionClass = $this->reflectionClassFromNodeType($nodeType);
- /** @var Node $node */
- $node = $reflectionClass->newInstanceWithoutConstructor();
- if (isset($value['attributes'])) {
- if (!\is_array($value['attributes'])) {
- throw new \RuntimeException('Attributes must be an array');
- }
- $node->setAttributes($this->decodeArray($value['attributes']));
- }
- foreach ($value as $name => $subNode) {
- if ($name === 'nodeType' || $name === 'attributes') {
- continue;
- }
- $node->$name = $this->decodeRecursive($subNode);
- }
- return $node;
- }
- private function decodeComment(array $value) : Comment {
- $className = $value['nodeType'] === 'Comment' ? Comment::class : Comment\Doc::class;
- if (!isset($value['text'])) {
- throw new \RuntimeException('Comment must have text');
- }
- return new $className(
- $value['text'], $value['line'] ?? -1, $value['filePos'] ?? -1, $value['tokenPos'] ?? -1
- );
- }
- private function reflectionClassFromNodeType(string $nodeType) : \ReflectionClass {
- if (!isset($this->reflectionClassCache[$nodeType])) {
- $className = $this->classNameFromNodeType($nodeType);
- $this->reflectionClassCache[$nodeType] = new \ReflectionClass($className);
- }
- return $this->reflectionClassCache[$nodeType];
- }
- private function classNameFromNodeType(string $nodeType) : string {
- $className = 'PhpParser\\Node\\' . strtr($nodeType, '_', '\\');
- if (class_exists($className)) {
- return $className;
- }
- $className .= '_';
- if (class_exists($className)) {
- return $className;
- }
- throw new \RuntimeException("Unknown node type \"$nodeType\"");
- }
- }
|