Chris@13
|
1 <?php declare(strict_types=1);
|
Chris@13
|
2
|
Chris@13
|
3 namespace PhpParser;
|
Chris@13
|
4
|
Chris@13
|
5 class JsonDecoder
|
Chris@13
|
6 {
|
Chris@13
|
7 /** @var \ReflectionClass[] Node type to reflection class map */
|
Chris@13
|
8 private $reflectionClassCache;
|
Chris@13
|
9
|
Chris@13
|
10 public function decode(string $json) {
|
Chris@13
|
11 $value = json_decode($json, true);
|
Chris@13
|
12 if (json_last_error()) {
|
Chris@13
|
13 throw new \RuntimeException('JSON decoding error: ' . json_last_error_msg());
|
Chris@13
|
14 }
|
Chris@13
|
15
|
Chris@13
|
16 return $this->decodeRecursive($value);
|
Chris@13
|
17 }
|
Chris@13
|
18
|
Chris@13
|
19 private function decodeRecursive($value) {
|
Chris@13
|
20 if (\is_array($value)) {
|
Chris@13
|
21 if (isset($value['nodeType'])) {
|
Chris@13
|
22 if ($value['nodeType'] === 'Comment' || $value['nodeType'] === 'Comment_Doc') {
|
Chris@13
|
23 return $this->decodeComment($value);
|
Chris@13
|
24 }
|
Chris@13
|
25 return $this->decodeNode($value);
|
Chris@13
|
26 }
|
Chris@13
|
27 return $this->decodeArray($value);
|
Chris@13
|
28 }
|
Chris@13
|
29 return $value;
|
Chris@13
|
30 }
|
Chris@13
|
31
|
Chris@13
|
32 private function decodeArray(array $array) : array {
|
Chris@13
|
33 $decodedArray = [];
|
Chris@13
|
34 foreach ($array as $key => $value) {
|
Chris@13
|
35 $decodedArray[$key] = $this->decodeRecursive($value);
|
Chris@13
|
36 }
|
Chris@13
|
37 return $decodedArray;
|
Chris@13
|
38 }
|
Chris@13
|
39
|
Chris@13
|
40 private function decodeNode(array $value) : Node {
|
Chris@13
|
41 $nodeType = $value['nodeType'];
|
Chris@13
|
42 if (!\is_string($nodeType)) {
|
Chris@13
|
43 throw new \RuntimeException('Node type must be a string');
|
Chris@13
|
44 }
|
Chris@13
|
45
|
Chris@13
|
46 $reflectionClass = $this->reflectionClassFromNodeType($nodeType);
|
Chris@13
|
47 /** @var Node $node */
|
Chris@13
|
48 $node = $reflectionClass->newInstanceWithoutConstructor();
|
Chris@13
|
49
|
Chris@13
|
50 if (isset($value['attributes'])) {
|
Chris@13
|
51 if (!\is_array($value['attributes'])) {
|
Chris@13
|
52 throw new \RuntimeException('Attributes must be an array');
|
Chris@13
|
53 }
|
Chris@13
|
54
|
Chris@13
|
55 $node->setAttributes($this->decodeArray($value['attributes']));
|
Chris@13
|
56 }
|
Chris@13
|
57
|
Chris@13
|
58 foreach ($value as $name => $subNode) {
|
Chris@13
|
59 if ($name === 'nodeType' || $name === 'attributes') {
|
Chris@13
|
60 continue;
|
Chris@13
|
61 }
|
Chris@13
|
62
|
Chris@13
|
63 $node->$name = $this->decodeRecursive($subNode);
|
Chris@13
|
64 }
|
Chris@13
|
65
|
Chris@13
|
66 return $node;
|
Chris@13
|
67 }
|
Chris@13
|
68
|
Chris@13
|
69 private function decodeComment(array $value) : Comment {
|
Chris@13
|
70 $className = $value['nodeType'] === 'Comment' ? Comment::class : Comment\Doc::class;
|
Chris@13
|
71 if (!isset($value['text'])) {
|
Chris@13
|
72 throw new \RuntimeException('Comment must have text');
|
Chris@13
|
73 }
|
Chris@13
|
74
|
Chris@13
|
75 return new $className(
|
Chris@13
|
76 $value['text'], $value['line'] ?? -1, $value['filePos'] ?? -1, $value['tokenPos'] ?? -1
|
Chris@13
|
77 );
|
Chris@13
|
78 }
|
Chris@13
|
79
|
Chris@13
|
80 private function reflectionClassFromNodeType(string $nodeType) : \ReflectionClass {
|
Chris@13
|
81 if (!isset($this->reflectionClassCache[$nodeType])) {
|
Chris@13
|
82 $className = $this->classNameFromNodeType($nodeType);
|
Chris@13
|
83 $this->reflectionClassCache[$nodeType] = new \ReflectionClass($className);
|
Chris@13
|
84 }
|
Chris@13
|
85 return $this->reflectionClassCache[$nodeType];
|
Chris@13
|
86 }
|
Chris@13
|
87
|
Chris@13
|
88 private function classNameFromNodeType(string $nodeType) : string {
|
Chris@13
|
89 $className = 'PhpParser\\Node\\' . strtr($nodeType, '_', '\\');
|
Chris@13
|
90 if (class_exists($className)) {
|
Chris@13
|
91 return $className;
|
Chris@13
|
92 }
|
Chris@13
|
93
|
Chris@13
|
94 $className .= '_';
|
Chris@13
|
95 if (class_exists($className)) {
|
Chris@13
|
96 return $className;
|
Chris@13
|
97 }
|
Chris@13
|
98
|
Chris@13
|
99 throw new \RuntimeException("Unknown node type \"$nodeType\"");
|
Chris@13
|
100 }
|
Chris@13
|
101 }
|