Skip to content

Commit f0dcad5

Browse files
djaf77Anton Zolotilin
authored andcommitted
Factory: fix method bodies import [Closes #61] (#62)
Co-authored-by: Anton Zolotilin <[email protected]>
1 parent 995ed5b commit f0dcad5

File tree

4 files changed

+63
-17
lines changed

4 files changed

+63
-17
lines changed

src/PhpGenerator/Factory.php

Lines changed: 48 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -192,8 +192,7 @@ private function loadMethodBodies(\ReflectionClass $from): array
192192
foreach ($nodeFinder->findInstanceOf($class, Node\Stmt\ClassMethod::class) as $method) {
193193
/** @var Node\Stmt\ClassMethod $method */
194194
if ($method->stmts) {
195-
$start = $method->stmts[0]->getAttribute('startFilePos');
196-
$body = substr($code, $start, end($method->stmts)->getAttribute('endFilePos') - $start + 1);
195+
$body = $this->extractBody($nodeFinder, $code, $method->stmts);
197196
$bodies[$method->name->toString()] = Helpers::indentPhp($body, -2);
198197
}
199198
}
@@ -208,17 +207,60 @@ private function loadFunctionBody(\ReflectionFunction $from): string
208207
}
209208

210209
[$code, $stmts] = $this->parse($from);
210+
211+
$nodeFinder = new PhpParser\NodeFinder;
211212
/** @var Node\Stmt\Function_ $function */
212-
$function = (new PhpParser\NodeFinder)->findFirst($stmts, function (Node $node) use ($from) {
213+
$function = $nodeFinder->findFirst($stmts, function (Node $node) use ($from) {
213214
return $node instanceof Node\Stmt\Function_ && $node->namespacedName->toString() === $from->name;
214215
});
215216

216-
$start = $function->stmts[0]->getAttribute('startFilePos');
217-
$body = substr($code, $start, end($function->stmts)->getAttribute('endFilePos') - $start + 1);
217+
$body = $this->extractBody($nodeFinder, $code, $function->stmts);
218218
return Helpers::indentPhp($body, -1);
219219
}
220220

221221

222+
/**
223+
* @param Node[] $statements
224+
*/
225+
private function extractBody(PhpParser\NodeFinder $nodeFinder, string $originalCode, array $statements): string
226+
{
227+
$start = $statements[0]->getAttribute('startFilePos');
228+
$body = substr($originalCode, $start, end($statements)->getAttribute('endFilePos') - $start + 1);
229+
230+
$replacements = [];
231+
// name-nodes => resolved fully-qualified name
232+
foreach ($nodeFinder->findInstanceOf($statements, Node\Name::class) as $node) {
233+
if ($node->hasAttribute('resolvedName')
234+
&& $node->getAttribute('resolvedName') instanceof Node\Name\FullyQualified
235+
) {
236+
$replacements[] = [
237+
$node->getStartFilePos(),
238+
$node->getEndFilePos(),
239+
$node->getAttribute('resolvedName')->toCodeString(),
240+
];
241+
}
242+
}
243+
244+
//sort collected resolved names by position in file
245+
usort($replacements, function ($a, $b) {
246+
return $a[0] <=> $b[0];
247+
});
248+
$correctiveOffset = -$start;
249+
//replace changes body length so we need correct offset
250+
foreach ($replacements as [$startPos, $endPos, $replacement]) {
251+
$replacingStringLength = $endPos - $startPos + 1;
252+
$body = substr_replace(
253+
$body,
254+
$replacement,
255+
$correctiveOffset + $startPos,
256+
$replacingStringLength
257+
);
258+
$correctiveOffset += strlen($replacement) - $replacingStringLength;
259+
}
260+
return $body;
261+
}
262+
263+
222264
private function parse($from): array
223265
{
224266
$file = $from->getFileName();
@@ -235,7 +277,7 @@ private function parse($from): array
235277
$stmts = $parser->parse($code);
236278

237279
$traverser = new PhpParser\NodeTraverser;
238-
$traverser->addVisitor(new PhpParser\NodeVisitor\NameResolver);
280+
$traverser->addVisitor(new PhpParser\NodeVisitor\NameResolver(null, ['replaceNodes' => false]));
239281
$stmts = $traverser->traverse($stmts);
240282

241283
return [$code, $stmts];

tests/PhpGenerator/GlobalFunction.phpt

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ require __DIR__ . '/../bootstrap.php';
1111
/** global */
1212
function func(stdClass $a, $b = null)
1313
{
14-
echo 'hello';
14+
echo sprintf('hello, %s', 'world');
1515
return 1;
1616
}
1717

@@ -28,13 +28,15 @@ function func(stdClass $a, $b = null)
2828

2929

3030
$function = GlobalFunction::withBodyFrom('func');
31-
same(
32-
'/**
31+
same(<<<'XX'
32+
/**
3333
* global
3434
*/
3535
function func(stdClass $a, $b = null)
3636
{
37-
echo \'hello\';
37+
echo \sprintf('hello, %s', 'world');
3838
return 1;
3939
}
40-
', (string) $function);
40+
41+
XX
42+
, (string) $function);

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

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

2828
public function long()
2929
{
30-
if ($member instanceof Method) {
30+
if ($member instanceof \Abc\Method) {
3131
$s = [1, 2, 3];
3232
}
3333
/*
3434
$this->methods[$member->getName()] = $member;
3535
*/
36-
throw new Nette\InvalidArgumentException('Argument must be Method|Property|Constant.');
36+
throw new \Nette\InvalidArgumentException('Argument must be Method|Property|Constant.');
3737
}
3838

3939

@@ -56,8 +56,8 @@ abstract class Class7
5656
/** multi
5757
line
5858
comment */
59-
60-
if ($member instanceof Method) {
59+
// Alias Method will not be resolved in comment
60+
if ($member instanceof \Abc\Method) {
6161
$s1 = '
6262
a
6363
b
@@ -87,6 +87,6 @@ a
8787
c
8888
<?php
8989
}
90-
throw new Nette\InvalidArgumentException();
90+
throw new \Nette\InvalidArgumentException();
9191
}
9292
}

tests/PhpGenerator/fixtures/class-body.phpf

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ declare(strict_types=1);
44

55
namespace Abc;
66

7+
use Nette;
8+
79
abstract class Class7
810
{
911
abstract function abstractFun();
@@ -51,7 +53,7 @@ abstract class Class7
5153
/** multi
5254
line
5355
comment */
54-
56+
// Alias Method will not be resolved in comment
5557
if ($member instanceof Method) {
5658
$s1 = '
5759
a

0 commit comments

Comments
 (0)