Skip to content

Commit

Permalink
Extractor: extracts native PHP values
Browse files Browse the repository at this point in the history
  • Loading branch information
dg committed Oct 17, 2023
1 parent a0f1aef commit d915b7f
Show file tree
Hide file tree
Showing 2 changed files with 113 additions and 10 deletions.
51 changes: 41 additions & 10 deletions src/PhpGenerator/Extractor.php
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand All @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -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);
}
}

Expand All @@ -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);
Expand All @@ -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));
}
}


Expand Down
72 changes: 72 additions & 0 deletions tests/PhpGenerator/Extractor.extractAll.vars.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<?php

declare(strict_types=1);

use Nette\PhpGenerator\Attribute;
use Nette\PhpGenerator\Extractor;
use Nette\PhpGenerator\Literal;
use Tester\Assert;
require __DIR__ . '/../bootstrap.php';


$file = (new Extractor(<<<'XX'
<?php
#[Attr(1, foo: 2, bar: new Attr(3))]
class Class1
{
const Foo = [1];
public $null = null;
public $scalar = [true, false, 1, 1.0, 'hello'];
public $const = [PHP_VERSION, self::Foo];
public $array = [1, 2, ['x' => [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(),
);

0 comments on commit d915b7f

Please sign in to comment.