Skip to content

Commit 087dde2

Browse files
authored
Merge pull request #2 from smeghead/feature/add-namespace
add namespace to scope output.
2 parents 87bcee4 + 5b13a01 commit 087dde2

13 files changed

+101
-16
lines changed

CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# CHANGELOG
2+
3+
## v0.0.1 (2025-03-18)
4+
5+
### Features
6+
7+
* Add support for anonymous function. by @hirokinoue
8+
* Add namespace to scope output.
9+
10+
### Bug fix
11+
12+
* Refactor how to find nodes. by @hirokinoue
13+

src/Analyze/AnalysisResult.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,11 @@ public function format(): string
3030
'maxVariableHardUsage' => $this->maxVariableHardUsage,
3131
'avarageVariableHardUsage' => $this->avarageVariableHardUsage,
3232
];
33-
$output['scopes'] = array_map(fn(Scope $scope) => ['name' => $scope->getName(), 'variableHardUsage' => $scope->getVariableHardUsage()], $this->scopes);
33+
$output['scopes'] = array_map(fn(Scope $scope) => [
34+
'namespace' => $scope->getNamespace(),
35+
'name' => $scope->getName(),
36+
'variableHardUsage' => $scope->getVariableHardUsage()
37+
], $this->scopes);
3438
return json_encode($output, JSON_PRETTY_PRINT) . PHP_EOL;
3539
}
3640
}

src/Analyze/FunctionScope.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,16 @@ final class FunctionScope implements Scope
1212
/**
1313
* @param list<AnalyzedVariable> $analyzedVariables
1414
*/
15-
public function __construct(private string $name, array $analyzedVariables)
15+
public function __construct(private ?string $namespace, private string $name, array $analyzedVariables)
1616
{
1717
$this->analyzedVariables = $analyzedVariables;
1818
}
1919

20+
public function getNamespace(): ?string
21+
{
22+
return $this->namespace;
23+
}
24+
2025
public function getName(): string
2126
{
2227
return $this->name;

src/Analyze/Scope.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
interface Scope
88
{
9+
public function getNamespace(): ?string;
910
public function getName(): string;
1011

1112
/**

src/Analyze/VariableAnalyzer.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ private function analyzeFunction(Func $function): Scope
4040
$analyzedVars[] = new AnalyzedVariable($variableName, $variableHardUsage);
4141
}
4242

43-
return new FunctionScope($function->name, $analyzedVars);
43+
return new FunctionScope($function->namespace, $function->name, $analyzedVars);
4444
}
4545

4646
/**

src/Parse/Func.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@ final class Func
99
/** @var list<VarReference> */
1010
private array $variables;
1111

12-
public function __construct(public readonly string $name)
12+
public function __construct(
13+
public readonly ?string $namespace,
14+
public readonly string $name
15+
)
1316
{
1417
$this->variables = [];
1518
}

src/Parse/VariableParser.php

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use PhpParser\Node\FunctionLike;
99
use PhpParser\NodeFinder;
1010
use PhpParser\NodeTraverser;
11+
use PhpParser\NodeVisitor\NameResolver;
1112
use PhpParser\Parser;
1213
use Smeghead\PhpVariableHardUsage\Parse\Exception\ParseFailedException;
1314
use Smeghead\PhpVariableHardUsage\Parse\Visitor\FunctionLikeFindingVisitor;
@@ -24,12 +25,25 @@ public function __construct()
2425
$this->nodeFinder = new NodeFinder();
2526
}
2627

28+
/**
29+
* @param array<\PhpParser\Node\Stmt> $stmts
30+
* @return array<\PhpParser\Node\Stmt>
31+
*/
32+
private function resolveNames(array $stmts): array
33+
{
34+
$nameResolver = new NameResolver();
35+
$nodeTraverser = new NodeTraverser();
36+
$nodeTraverser->addVisitor($nameResolver);
37+
return $nodeTraverser->traverse($stmts);
38+
}
39+
2740
public function parse(string $content): ParseResult
2841
{
2942
$stmts = $this->parser->parse($content);
3043
if ($stmts === null) {
3144
throw new ParseFailedException();
3245
}
46+
$stmts = $this->resolveNames($stmts);
3347

3448
$traverser = new NodeTraverser();
3549
$visitor = new FunctionLikeFindingVisitor(fn($node) => $node instanceof FunctionLike);
@@ -49,15 +63,15 @@ public function parse(string $content): ParseResult
4963
private function collectParseResultPerFunctionLike(array $functionLikes): array
5064
{
5165
return array_map(function (FunctionLike $function) {
66+
$namespace = $function->getAttribute('namespace'); // Get the namespace name set in FunctionLikeFindingVisitor
5267
$className = $function->getAttribute('className'); // Get the class name set in FunctionLikeFindingVisitor
5368
$functionName = $function->name->name ?? $function->getType() . '@' . $function->getStartLine();
5469
$functionIdentifier = sprintf(
5570
'%s%s',
5671
isset($className) ? $className . '::' : '',
5772
$functionName
5873
);
59-
60-
$func = new Func($functionIdentifier);
74+
$func = new Func($namespace, $functionIdentifier);
6175

6276
$variables = $this->nodeFinder->findInstanceOf($function, Variable::class);
6377
foreach ($variables as $variable) {

src/Parse/Visitor/FunctionLikeFindingVisitor.php

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,25 +5,34 @@
55
namespace Smeghead\PhpVariableHardUsage\Parse\Visitor;
66

77
use PhpParser\Node;
8+
use PhpParser\Node\FunctionLike;
89
use PhpParser\NodeVisitor\FindingVisitor;
910
use PhpParser\Node\Stmt\Class_;
1011
use PhpParser\Node\Stmt\ClassMethod;
12+
use PhpParser\Node\Stmt\Namespace_;
1113

1214
/**
1315
* FindingVisitor that searches for elements of FunctionLike
1416
*
1517
* For ClassMethod, set the class name to ClassName as Attribute.
1618
*/
1719
class FunctionLikeFindingVisitor extends FindingVisitor {
18-
protected array $foundNodes = [];
1920
private ?string $currentClass = null;
21+
private ?string $currentNamespace = null;
2022

2123
public function enterNode(Node $node) {
2224
if ($node instanceof Class_) {
2325
// Record class name
2426
$this->currentClass = $node->name ? $node->name->name : null;
2527
}
28+
if ($node instanceof Namespace_) {
29+
// Record class name
30+
$this->currentNamespace = $node->name ? $node->name->name : null;
31+
}
2632

33+
if ($node instanceof FunctionLike) {
34+
$node->setAttribute('namespace', $this->currentNamespace);
35+
}
2736
if ($node instanceof ClassMethod) {
2837
$node->setAttribute('className', $this->currentClass);
2938
}

test/VariableAnalizerTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ class VariableAnalizerTest extends TestCase
1111
{
1212
public function testAnalyzeFunctionSimple(): void
1313
{
14-
$func = new Func('testFunction');
14+
$func = new Func(null, 'testFunction');
1515
$func->addVariable(new VarReference('a', 1));
1616
$func->addVariable(new VarReference('a', 2));
1717
$func->addVariable(new VarReference('a', 3));
@@ -27,7 +27,7 @@ public function testAnalyzeFunctionSimple(): void
2727

2828
public function testAnalyzeFunctionLong(): void
2929
{
30-
$func = new Func('testFunction');
30+
$func = new Func(null, 'testFunction');
3131
$func->addVariable(new VarReference('a', 1));
3232
$func->addVariable(new VarReference('a', 2));
3333
$func->addVariable(new VarReference('a', 100));

test/VariableParserTest.php

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ public function testParseFunction(): void
1919
$this->assertInstanceOf(ParseResult::class, $result);
2020
$functions = $result->functions;
2121
$this->assertCount(1, $functions);
22-
$this->assertEquals('smallFunction', $functions[0]->name);
22+
$this->assertSame('smallFunction', $functions[0]->name);
23+
$this->assertSame(null, $functions[0]->namespace);
2324
$this->assertCount(2, $functions[0]->getVariables());
2425

2526
$vars = $functions[0]->getVariables();
@@ -38,18 +39,19 @@ public function testParseClass(): void
3839
$this->assertInstanceOf(ParseResult::class, $result);
3940
$functions = $result->functions;
4041
$this->assertCount(1, $functions);
41-
$this->assertEquals('Clazz::bigFunction', $functions[0]->name);
42+
$this->assertSame('Smeghead\PhpVariableHardUsage\Fixtures', $functions[0]->namespace);
43+
$this->assertSame('Clazz::bigFunction', $functions[0]->name);
4244
$this->assertCount(4, $functions[0]->getVariables());
4345

4446
$vars = $functions[0]->getVariables();
4547
$this->assertSame('num', $vars[0]->name);
46-
$this->assertSame(9, $vars[0]->lineNumber, 'first $num (9)');
48+
$this->assertSame(11, $vars[0]->lineNumber, 'first $num (11)');
4749
$this->assertSame('num', $vars[1]->name);
48-
$this->assertSame(11, $vars[1]->lineNumber, 'second $num (11)');
50+
$this->assertSame(13, $vars[1]->lineNumber, 'second $num (13)');
4951
$this->assertSame('num', $vars[2]->name);
50-
$this->assertSame(12, $vars[2]->lineNumber, 'second $num (12)');
52+
$this->assertSame(14, $vars[2]->lineNumber, 'second $num (14)');
5153
$this->assertSame('num', $vars[3]->name);
52-
$this->assertSame(15, $vars[3]->lineNumber, 'second $num (15)');
54+
$this->assertSame(17, $vars[3]->lineNumber, 'second $num (17)');
5355
}
5456

5557
public function testParseAnonymousFunction(): void
@@ -61,7 +63,7 @@ public function testParseAnonymousFunction(): void
6163
$this->assertInstanceOf(ParseResult::class, $result);
6264
$functions = $result->functions;
6365
$this->assertCount(1, $functions);
64-
$this->assertEquals('Expr_Closure@4', $functions[0]->name);
66+
$this->assertSame('Expr_Closure@4', $functions[0]->name);
6567
$this->assertCount(4, $functions[0]->getVariables());
6668

6769
$vars = $functions[0]->getVariables();
@@ -74,4 +76,23 @@ public function testParseAnonymousFunction(): void
7476
$this->assertSame('b', $vars[3]->name);
7577
$this->assertSame(5, $vars[3]->lineNumber, 'second $b (5)');
7678
}
79+
80+
public function testParseNamespace(): void
81+
{
82+
$parser = new VariableParser();
83+
$content = file_get_contents($this->fixtureDir . '/namespace.php');
84+
$result = $parser->parse($content);
85+
86+
$this->assertInstanceOf(ParseResult::class, $result);
87+
$functions = $result->functions;
88+
$this->assertCount(2, $functions);
89+
$this->assertSame('SomeNamespace', $functions[0]->namespace);
90+
$this->assertSame('someFunction', $functions[0]->name);
91+
$this->assertCount(0, $functions[0]->getVariables());
92+
93+
$this->assertSame('OtherNamespace', $functions[1]->namespace);
94+
$this->assertSame('otherFunction', $functions[1]->name);
95+
$this->assertCount(0, $functions[1]->getVariables());
96+
}
97+
7798
}

test/fixtures/Clazz.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
declare(strict_types=1);
44

5+
namespace Smeghead\PhpVariableHardUsage\Fixtures;
6+
57
class Clazz
68
{
79
public function bigFunction(): void

test/fixtures/brace_namespace.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?php
2+
3+
namespace AnotherNamespace {
4+
function anotherFunction(): void {}
5+
}

test/fixtures/namespace.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?php
2+
namespace SomeNamespace;
3+
4+
function someFunction(): void {}
5+
6+
namespace OtherNamespace;
7+
8+
function otherFunction(): void {}

0 commit comments

Comments
 (0)