Skip to content

Commit 1c15611

Browse files
committed
Resolving of names in body & literals [Closes #85]
1 parent fbd1e66 commit 1c15611

11 files changed

+179
-34
lines changed

src/PhpGenerator/Extractor.php

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ private function parseCode(string $code): void
5050
$stmts = $parser->parse($this->code);
5151

5252
$traverser = new PhpParser\NodeTraverser;
53+
$traverser->addVisitor(new PhpParser\NodeVisitor\ParentConnectingVisitor);
5354
$traverser->addVisitor(new PhpParser\NodeVisitor\NameResolver(null, ['preserveOriginalNames' => true]));
5455
$this->statements = $traverser->traverse($stmts);
5556
}
@@ -67,7 +68,7 @@ public function extractMethodBodies(string $className): array
6768
foreach ($nodeFinder->findInstanceOf($classNode, Node\Stmt\ClassMethod::class) as $methodNode) {
6869
/** @var Node\Stmt\ClassMethod $methodNode */
6970
if ($methodNode->stmts) {
70-
$res[$methodNode->name->toString()] = $this->getReformattedBody($methodNode->stmts, 2);
71+
$res[$methodNode->name->toString()] = $this->getReformattedContents($methodNode->stmts, 2);
7172
}
7273
}
7374
return $res;
@@ -81,12 +82,12 @@ public function extractFunctionBody(string $name): ?string
8182
return $node instanceof Node\Stmt\Function_ && $node->namespacedName->toString() === $name;
8283
});
8384

84-
return $this->getReformattedBody($functionNode->stmts, 1);
85+
return $this->getReformattedContents($functionNode->stmts, 1);
8586
}
8687

8788

8889
/** @param Node[] $statements */
89-
private function getReformattedBody(array $statements, int $level): string
90+
private function getReformattedContents(array $statements, int $level): string
9091
{
9192
$body = $this->getNodeContents(...$statements);
9293
$body = $this->performReplacements($body, $this->prepareReplacements($statements));
@@ -101,10 +102,13 @@ private function prepareReplacements(array $statements): array
101102
(new NodeFinder)->find($statements, function (Node $node) use (&$replacements, $start) {
102103
if ($node instanceof Node\Name\FullyQualified) {
103104
if ($node->getAttribute('originalName') instanceof Node\Name) {
105+
$type = $node->getAttribute('parent') instanceof Node\Expr\ConstFetch
106+
? PhpNamespace::NAME_CONSTANT
107+
: ($node->getAttribute('parent') instanceof Node\Expr\FuncCall ? PhpNamespace::NAME_FUNCTION : PhpNamespace::NAME_NORMAL);
104108
$replacements[] = [
105109
$node->getStartFilePos() - $start,
106110
$node->getEndFilePos() - $start,
107-
$node->toCodeString(),
111+
Helpers::tagName($node->toCodeString(), $type),
108112
];
109113
}
110114

@@ -302,7 +306,7 @@ private function addPropertyToClass(ClassType $class, Node\Stmt\Property $node):
302306
}
303307
$prop->setType($node->type ? $this->toPhp($node->type) : null);
304308
if ($item->default) {
305-
$prop->setValue(new Literal($this->toPhp($item->default)));
309+
$prop->setValue(new Literal($this->getReformattedContents([$item->default], 1)));
306310
}
307311
$prop->setReadOnly(method_exists($node, 'isReadonly') && $node->isReadonly());
308312
$this->addCommentAndAttributes($prop, $node);
@@ -328,7 +332,8 @@ private function addMethodToClass(ClassType $class, Node\Stmt\ClassMethod $node)
328332
private function addConstantToClass(ClassType $class, Node\Stmt\ClassConst $node): void
329333
{
330334
foreach ($node->consts as $item) {
331-
$const = $class->addConstant($item->name->toString(), new Literal($this->toPhp($item->value)));
335+
$value = $this->getReformattedContents([$item->value], 1);
336+
$const = $class->addConstant($item->name->toString(), new Literal($value));
332337
if ($node->isPrivate()) {
333338
$const->setPrivate();
334339
} elseif ($node->isProtected()) {
@@ -359,7 +364,7 @@ private function addCommentAndAttributes($element, Node $node): void
359364
foreach ($group->attrs as $attribute) {
360365
$args = [];
361366
foreach ($attribute->args as $arg) {
362-
$value = new Literal($this->toPhp($arg));
367+
$value = new Literal($this->getReformattedContents([$arg], 0));
363368
if ($arg->name) {
364369
$args[$arg->name->toString()] = $value;
365370
} else {
@@ -385,13 +390,13 @@ private function setupFunction($function, Node\FunctionLike $node): void
385390
$param->setReference($item->byRef);
386391
$function->setVariadic($item->variadic);
387392
if ($item->default) {
388-
$param->setDefaultValue(new Literal($this->toPhp($item->default)));
393+
$param->setDefaultValue(new Literal($this->getReformattedContents([$item->default], 2)));
389394
}
390395
$this->addCommentAndAttributes($param, $item);
391396
}
392397
$this->addCommentAndAttributes($function, $node);
393398
if ($node->stmts) {
394-
$function->setBody($this->getReformattedBody($node->stmts, 2));
399+
$function->setBody($this->getReformattedContents($node->stmts, 2));
395400
}
396401
}
397402

src/PhpGenerator/Helpers.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,27 @@ public static function formatDocComment(string $content): string
8383
}
8484

8585

86+
public static function tagName(string $name, string $type = PhpNamespace::NAME_NORMAL): string
87+
{
88+
return "/*($type*/$name";
89+
}
90+
91+
92+
public static function simplifyTaggedNames(string $code, ?PhpNamespace $namespace): string
93+
{
94+
return preg_replace_callback('~/\*\(([ncf])\*/([\w\x7f-\xff\\\\]++)~', function ($m) use ($namespace) {
95+
[, $type, $name] = $m;
96+
if (!$namespace) {
97+
return $name;
98+
} elseif ($type === PhpNamespace::NAME_NORMAL) {
99+
return $namespace->simplifyType($name);
100+
} else {
101+
return $namespace->simplifyType(self::extractNamespace($name) . '\\') . self::extractShortName($name);
102+
}
103+
}, $code);
104+
}
105+
106+
86107
public static function unformatDocComment(string $comment): string
87108
{
88109
return preg_replace('#^\s*\* ?#m', '', trim(trim(trim($comment), '/*')));

src/PhpGenerator/PhpNamespace.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ final class PhpNamespace
2626
{
2727
use Nette\SmartObject;
2828

29+
public const
30+
NAME_NORMAL = 'n',
31+
NAME_FUNCTION = 'f',
32+
NAME_CONSTANT = 'c';
33+
2934
/** @var string */
3035
private $name;
3136

src/PhpGenerator/Printer.php

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -55,13 +55,14 @@ public function printFunction(GlobalFunction $function, PhpNamespace $namespace
5555
. ($function->getReturnReference() ? '&' : '')
5656
. $function->getName();
5757
$returnType = $this->printReturnType($function);
58+
$body = Helpers::simplifyTaggedNames($function->getBody(), $this->namespace);
5859

5960
return Helpers::formatDocComment($function->getComment() . "\n")
6061
. self::printAttributes($function->getAttributes())
6162
. $line
6263
. $this->printParameters($function, strlen($line) + strlen($returnType) + 2) // 2 = parentheses
6364
. $returnType
64-
. "\n{\n" . $this->indent(ltrim(rtrim($function->getBody()) . "\n")) . "}\n";
65+
. "\n{\n" . $this->indent(ltrim(rtrim($body) . "\n")) . "}\n";
6566
}
6667

6768

@@ -75,14 +76,15 @@ public function printClosure(Closure $closure, PhpNamespace $namespace = null):
7576
$useStr = strlen($tmp = implode(', ', $uses)) > $this->dumper->wrapLength && count($uses) > 1
7677
? "\n" . $this->indentation . implode(",\n" . $this->indentation, $uses) . "\n"
7778
: $tmp;
79+
$body = Helpers::simplifyTaggedNames($closure->getBody(), $this->namespace);
7880

7981
return self::printAttributes($closure->getAttributes(), true)
8082
. 'function '
8183
. ($closure->getReturnReference() ? '&' : '')
8284
. $this->printParameters($closure)
8385
. ($uses ? " use ($useStr)" : '')
8486
. $this->printReturnType($closure)
85-
. " {\n" . $this->indent(ltrim(rtrim($closure->getBody()) . "\n")) . '}';
87+
. " {\n" . $this->indent(ltrim(rtrim($body) . "\n")) . '}';
8688
}
8789

8890

@@ -94,13 +96,14 @@ public function printArrowFunction(Closure $closure, PhpNamespace $namespace = n
9496
throw new Nette\InvalidArgumentException('Arrow function cannot bind variables by-reference.');
9597
}
9698
}
99+
$body = Helpers::simplifyTaggedNames($closure->getBody(), $this->namespace);
97100

98101
return self::printAttributes($closure->getAttributes())
99102
. 'fn'
100103
. ($closure->getReturnReference() ? '&' : '')
101104
. $this->printParameters($closure)
102105
. $this->printReturnType($closure)
103-
. ' => ' . trim($closure->getBody()) . ';';
106+
. ' => ' . trim($body) . ';';
104107
}
105108

106109

@@ -117,6 +120,7 @@ public function printMethod(Method $method, PhpNamespace $namespace = null): str
117120
. $method->getName();
118121
$returnType = $this->printReturnType($method);
119122
$params = $this->printParameters($method, strlen($line) + strlen($returnType) + strlen($this->indentation) + 2);
123+
$body = Helpers::simplifyTaggedNames((string) $method->getBody(), $this->namespace);
120124

121125
return Helpers::formatDocComment($method->getComment() . "\n")
122126
. self::printAttributes($method->getAttributes())
@@ -127,7 +131,7 @@ public function printMethod(Method $method, PhpNamespace $namespace = null): str
127131
? ";\n"
128132
: (strpos($params, "\n") === false ? "\n" : ' ')
129133
. "{\n"
130-
. $this->indent(ltrim(rtrim($method->getBody()) . "\n"))
134+
. $this->indent(ltrim(rtrim($body) . "\n"))
131135
. "}\n");
132136
}
133137

@@ -359,6 +363,7 @@ private function printAttributes(array $attrs, bool $inline = false): string
359363
$items = [];
360364
foreach ($attrs as $attr) {
361365
$args = $this->dumper->format('...?:', $attr->getArguments());
366+
$args = Helpers::simplifyTaggedNames($args, $this->namespace);
362367
$items[] = $this->printType($attr->getName(), false) . ($args ? "($args)" : '');
363368
}
364369
return $inline
@@ -385,7 +390,9 @@ protected function indent(string $s): string
385390
protected function dump($var, int $column = 0): string
386391
{
387392
$this->dumper->indentation = $this->indentation;
388-
return $this->dumper->dump($var, $column);
393+
$s = $this->dumper->dump($var, $column);
394+
$s = Helpers::simplifyTaggedNames($s, $this->namespace);
395+
return $s;
389396
}
390397

391398

tests/PhpGenerator/Extractor.extractAll.resolving.phpt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,6 @@ $namespace->add(reset($classes));
1919

2020
$printer = new Printer;
2121
sameFile(__DIR__ . '/expected/Factory.fromCode.bodies.resolving.expect', $printer->printNamespace($namespace));
22+
23+
$printer->setTypeResolving(false);
24+
sameFile(__DIR__ . '/expected/Factory.fromCode.bodies.unresolving.expect', $printer->printNamespace($namespace));

tests/PhpGenerator/PhpNamespace.phpt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@ $namespace = new PhpNamespace('Foo');
3434
Assert::same('Foo', $namespace->getName());
3535
Assert::same('\A', $namespace->simplifyName('\A'));
3636
Assert::same('\A', $namespace->simplifyName('A'));
37+
Assert::same('\A\\', $namespace->simplifyName('A\\'));
3738
Assert::same('A', $namespace->simplifyName('foo\A'));
39+
Assert::same('A\\', $namespace->simplifyName('foo\A\\'));
3840

3941
Assert::same('A', $namespace->simplifyType('foo\A'));
4042
Assert::same('null|A', $namespace->simplifyType('null|foo\A'));

tests/PhpGenerator/expected/ClassType.from.bodies.expect

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ abstract class Class7
2727

2828
public function long()
2929
{
30-
if ($member instanceof \Abc\Method) {
30+
if ($member instanceof Method) {
3131
$s = [1, 2, 3];
3232
}
3333
/*
@@ -41,17 +41,17 @@ abstract class Class7
4141
{
4242
echo FOO;
4343
echo \FOO;
44-
echo \Abc\a\FOO;
44+
echo a\FOO;
4545
echo \Nette\FOO;
4646

4747
// functions
4848
func();
4949
\func();
50-
\Abc\a\func();
50+
a\func();
5151
\Nette\func();
5252

5353
// classes
54-
$x = new \Abc\MyClass;
54+
$x = new MyClass;
5555
$y = new \stdClass;
5656
$z = \Nette\Utils\ArrayHash::class;
5757
}
@@ -77,7 +77,7 @@ abstract class Class7
7777
line
7878
comment */
7979
// Alias Method will not be resolved in comment
80-
if ($member instanceof \Abc\Method) {
80+
if ($member instanceof Method) {
8181
$s1 = "\na\n\tb\n\t\tc\n";
8282
$s2 = "\na\n\t{$b}\n\t\t$c\n";
8383

tests/PhpGenerator/expected/Factory.fromCode.bodies.expect

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -35,33 +35,33 @@ abstract class Class7
3535

3636
public function long()
3737
{
38-
if ($member instanceof \Abc\Method) {
38+
if ($member instanceof Method) {
3939
$s = [1, 2, 3];
4040
}
4141
/*
4242
$this->methods[$member->getName()] = $member;
4343
*/
44-
throw new \Nette\InvalidArgumentException('Argument must be Method|Property|Constant.');
44+
throw new Nette\InvalidArgumentException('Argument must be Method|Property|Constant.');
4545
}
4646

4747

48-
public function resolving($a = \Abc\a\FOO, self $b = null, $c = self::FOO)
48+
public function resolving($a = a\FOO, self $b = null, $c = self::FOO)
4949
{
5050
echo FOO;
5151
echo \FOO;
52-
echo \Abc\a\FOO;
53-
echo \Nette\FOO;
52+
echo a\FOO;
53+
echo Nette\FOO;
5454

5555
// functions
5656
func();
5757
\func();
58-
\Abc\a\func();
59-
\Nette\func();
58+
a\func();
59+
Nette\func();
6060

6161
// classes
62-
$x = new \Abc\MyClass;
62+
$x = new MyClass;
6363
$y = new \stdClass;
64-
$z = \Nette\Utils\ArrayHash::class;
64+
$z = Nette\Utils\ArrayHash::class;
6565
}
6666

6767

@@ -85,7 +85,7 @@ abstract class Class7
8585
line
8686
comment */
8787
// Alias Method will not be resolved in comment
88-
if ($member instanceof \Abc\Method) {
88+
if ($member instanceof Method) {
8989
$s1 = "\na\n\tb\n\t\tc\n";
9090
$s2 = "\na\n\t{$b}\n\t\t$c\n";
9191

@@ -100,6 +100,6 @@ abstract class Class7
100100
c
101101
<?php
102102
}
103-
throw new \Nette\InvalidArgumentException();
103+
throw new Nette\InvalidArgumentException();
104104
}
105105
}

tests/PhpGenerator/expected/Factory.fromCode.bodies.resolving.expect

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ abstract class Class7
3838
/*
3939
$this->methods[$member->getName()] = $member;
4040
*/
41-
throw new \Nette\InvalidArgumentException('Argument must be Method|Property|Constant.');
41+
throw new InvalidArgumentException('Argument must be Method|Property|Constant.');
4242
}
4343

4444

@@ -58,7 +58,7 @@ abstract class Class7
5858
// classes
5959
$x = new \Abc\MyClass;
6060
$y = new \stdClass;
61-
$z = \Nette\Utils\ArrayHash::class;
61+
$z = Utils\ArrayHash::class;
6262
}
6363

6464

@@ -97,6 +97,6 @@ abstract class Class7
9797
c
9898
<?php
9999
}
100-
throw new \Nette\InvalidArgumentException();
100+
throw new InvalidArgumentException();
101101
}
102102
}

0 commit comments

Comments
 (0)