Chris@13: decodeRecursive($value); Chris@13: } Chris@13: Chris@13: private function decodeRecursive($value) { Chris@13: if (\is_array($value)) { Chris@13: if (isset($value['nodeType'])) { Chris@13: if ($value['nodeType'] === 'Comment' || $value['nodeType'] === 'Comment_Doc') { Chris@13: return $this->decodeComment($value); Chris@13: } Chris@13: return $this->decodeNode($value); Chris@13: } Chris@13: return $this->decodeArray($value); Chris@13: } Chris@13: return $value; Chris@13: } Chris@13: Chris@13: private function decodeArray(array $array) : array { Chris@13: $decodedArray = []; Chris@13: foreach ($array as $key => $value) { Chris@13: $decodedArray[$key] = $this->decodeRecursive($value); Chris@13: } Chris@13: return $decodedArray; Chris@13: } Chris@13: Chris@13: private function decodeNode(array $value) : Node { Chris@13: $nodeType = $value['nodeType']; Chris@13: if (!\is_string($nodeType)) { Chris@13: throw new \RuntimeException('Node type must be a string'); Chris@13: } Chris@13: Chris@13: $reflectionClass = $this->reflectionClassFromNodeType($nodeType); Chris@13: /** @var Node $node */ Chris@13: $node = $reflectionClass->newInstanceWithoutConstructor(); Chris@13: Chris@13: if (isset($value['attributes'])) { Chris@13: if (!\is_array($value['attributes'])) { Chris@13: throw new \RuntimeException('Attributes must be an array'); Chris@13: } Chris@13: Chris@13: $node->setAttributes($this->decodeArray($value['attributes'])); Chris@13: } Chris@13: Chris@13: foreach ($value as $name => $subNode) { Chris@13: if ($name === 'nodeType' || $name === 'attributes') { Chris@13: continue; Chris@13: } Chris@13: Chris@13: $node->$name = $this->decodeRecursive($subNode); Chris@13: } Chris@13: Chris@13: return $node; Chris@13: } Chris@13: Chris@13: private function decodeComment(array $value) : Comment { Chris@13: $className = $value['nodeType'] === 'Comment' ? Comment::class : Comment\Doc::class; Chris@13: if (!isset($value['text'])) { Chris@13: throw new \RuntimeException('Comment must have text'); Chris@13: } Chris@13: Chris@13: return new $className( Chris@13: $value['text'], $value['line'] ?? -1, $value['filePos'] ?? -1, $value['tokenPos'] ?? -1 Chris@13: ); Chris@13: } Chris@13: Chris@13: private function reflectionClassFromNodeType(string $nodeType) : \ReflectionClass { Chris@13: if (!isset($this->reflectionClassCache[$nodeType])) { Chris@13: $className = $this->classNameFromNodeType($nodeType); Chris@13: $this->reflectionClassCache[$nodeType] = new \ReflectionClass($className); Chris@13: } Chris@13: return $this->reflectionClassCache[$nodeType]; Chris@13: } Chris@13: Chris@13: private function classNameFromNodeType(string $nodeType) : string { Chris@13: $className = 'PhpParser\\Node\\' . strtr($nodeType, '_', '\\'); Chris@13: if (class_exists($className)) { Chris@13: return $className; Chris@13: } Chris@13: Chris@13: $className .= '_'; Chris@13: if (class_exists($className)) { Chris@13: return $className; Chris@13: } Chris@13: Chris@13: throw new \RuntimeException("Unknown node type \"$nodeType\""); Chris@13: } Chris@13: }