From d915b7fa643cd0cf6ffe306c25ac5c3db24914a4 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Sun, 15 Oct 2023 02:03:34 +0200 Subject: [PATCH] Extractor: extracts native PHP values --- src/PhpGenerator/Extractor.php | 51 ++++++++++--- .../Extractor.extractAll.vars.phpt | 72 +++++++++++++++++++ 2 files changed, 113 insertions(+), 10 deletions(-) create mode 100644 tests/PhpGenerator/Extractor.extractAll.vars.phpt diff --git a/src/PhpGenerator/Extractor.php b/src/PhpGenerator/Extractor.php index a2cf82f3..fd25c1d1 100644 --- a/src/PhpGenerator/Extractor.php +++ b/src/PhpGenerator/Extractor.php @@ -292,7 +292,7 @@ private function addPropertyToClass(ClassLike $class, Node\Stmt\Property $node): $prop->setVisibility($this->toVisibility($node->flags)); $prop->setType($node->type ? $this->toPhp($node->type) : null); if ($item->default) { - $prop->setValue($this->formatValue($item->default, 1)); + $prop->setValue($this->toValue($item->default)); } $prop->setReadOnly(method_exists($node, 'isReadonly') && $node->isReadonly()); @@ -315,7 +315,7 @@ private function addMethodToClass(ClassLike $class, Node\Stmt\ClassMethod $node) private function addConstantToClass(ClassLike $class, Node\Stmt\ClassConst $node): void { foreach ($node->consts as $item) { - $const = $class->addConstant($item->name->toString(), $this->formatValue($item->value, 1)); + $const = $class->addConstant($item->name->toString(), $this->toValue($item->value)); $const->setVisibility($this->toVisibility($node->flags)); $const->setFinal(method_exists($node, 'isFinal') && $node->isFinal()); $this->addCommentAndAttributes($const, $node); @@ -328,7 +328,7 @@ private function addEnumCaseToClass(EnumType $class, Node\Stmt\EnumCase $node): $value = match (true) { $node->expr === null => null, $node->expr instanceof Node\Scalar\LNumber, $node->expr instanceof Node\Scalar\String_ => $node->expr->value, - default => $this->formatValue($node->expr, 1), + default => $this->toValue($node->expr), }; $case = $class->addCase($node->name->toString(), $value); $this->addCommentAndAttributes($case, $node); @@ -358,11 +358,10 @@ private function addCommentAndAttributes( foreach ($group->attrs as $attribute) { $args = []; foreach ($attribute->args as $arg) { - $value = $this->formatValue($arg->value, 0); if ($arg->name) { - $args[$arg->name->toString()] = $value; + $args[$arg->name->toString()] = $this->toValue($arg->value); } else { - $args[] = $value; + $args[] = $this->toValue($arg->value); } } @@ -386,7 +385,7 @@ private function setupFunction(GlobalFunction|Method $function, Node\FunctionLik $param->setReference($item->byRef); $function->setVariadic($item->variadic); if ($item->default) { - $param->setDefaultValue($this->formatValue($item->default, 2)); + $param->setDefaultValue($this->toValue($item->default)); } $this->addCommentAndAttributes($param, $item); @@ -400,10 +399,42 @@ private function setupFunction(GlobalFunction|Method $function, Node\FunctionLik } - private function formatValue(Node\Expr $value, int $level): Literal + private function toValue(Node\Expr $node): mixed { - $value = $this->getReformattedContents([$value], $level); - return new Literal($value); + if ($node instanceof Node\Expr\ConstFetch) { + return match ($node->name->toLowerString()) { + 'null' => null, + 'true' => true, + 'false' => false, + default => new Literal($this->getReformattedContents([$node], 0)), + }; + } elseif ($node instanceof Node\Scalar\LNumber + || $node instanceof Node\Scalar\DNumber + || $node instanceof Node\Scalar\String_ + ) { + return $node->value; + + } elseif ($node instanceof Node\Expr\Array_) { + $res = []; + foreach ($node->items as $item) { + if ($item->unpack) { + $res[] = new Literal($this->getReformattedContents([$item], 0)); + + } elseif ($item->key) { + $key = $item->key instanceof Node\Identifier + ? $item->key->name + : $this->toValue($item->key); + $res[$key] = $this->toValue($item->value); + + } else { + $res[] = $this->toValue($item->value); + } + } + return $res; + + } else { + return new Literal($this->getReformattedContents([$node], 0)); + } } diff --git a/tests/PhpGenerator/Extractor.extractAll.vars.phpt b/tests/PhpGenerator/Extractor.extractAll.vars.phpt new file mode 100644 index 00000000..464c4a38 --- /dev/null +++ b/tests/PhpGenerator/Extractor.extractAll.vars.phpt @@ -0,0 +1,72 @@ + [3]], ...self::Foo]; + public $concat = 'x' . 'y'; + public $math = 10 * 3; + + public function foo($a = [1, 2, 3], $b = new stdClass(1, 2)) + { + } + } + XX))->extractAll(); + + +$class = $file->getClasses()['Class1']; +Assert::equal( + new Attribute('Attr', [1, 'foo' => 2, 'bar' => new Literal('new /*(n*/\Attr(3)')]), + $class->getAttributes()[0], +); + +Assert::same([1], $class->getConstant('Foo')->getValue()); + +Assert::same(null, $class->getProperty('null')->getValue()); +Assert::same( + [true, false, 1, 1.0, 'hello'], + $class->getProperty('scalar')->getValue(), +); +Assert::equal( + [new Literal('/*(c*/\PHP_VERSION'), new Literal('self::Foo')], + $class->getProperty('const')->getValue(), +); +Assert::equal( + [1, 2, ['x' => [3]], new Literal('...self::Foo')], + $class->getProperty('array')->getValue(), +); +Assert::equal( + new Literal("'x' . 'y'"), + $class->getProperty('concat')->getValue(), +); +Assert::equal( + new Literal('10 * 3'), + $class->getProperty('math')->getValue(), +); + +$method = $class->getMethod('foo'); +Assert::same( + [1, 2, 3], + $method->getParameter('a')->getDefaultValue(), +); +Assert::equal( + new Literal('new /*(n*/\stdClass(1, 2)'), + $method->getParameter('b')->getDefaultValue(), +);