Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 22 additions & 95 deletions src/Parse/VariableParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,125 +5,52 @@
namespace Smeghead\PhpVariableHardUsage\Parse;

use PhpParser\Node\Expr\Variable;
use PhpParser\Node\Stmt;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\Function_;
use PhpParser\NodeDumper;
use PhpParser\NodeVisitor\FindingVisitor;
use PhpParser\Node\FunctionLike;
use PhpParser\NodeFinder;
use PhpParser\Parser;
use Smeghead\PhpVariableHardUsage\Parse\Exception\ParseFailedException;

final class VariableParser
{
private Parser $parser;

private NodeFinder $nodeFinder;

public function __construct()
{
$this->parser = (new \PhpParser\ParserFactory())->createForNewestSupportedVersion();
$this->nodeFinder = new NodeFinder();
}

/**
* @param list<Stmt> $stmt
* @return list<Function_>
*/
private function getFunctions(array $stmt): array
public function parse(string $content): ParseResult
{
$functionVisitor = new FindingVisitor(function ($node) {
return $node instanceof Function_;
});
$traverser = new \PhpParser\NodeTraverser();
$traverser->addVisitor($functionVisitor);
$traverser->traverse($stmt);
$stmts = $this->parser->parse($content);
if ($stmts === null) {
throw new ParseFailedException();
}

return $functionVisitor->getFoundNodes(); // @phpstan-ignore-line
}
$functionLikes = $this->nodeFinder->findInstanceOf($stmts, FunctionLike::class);

/**
* @param Function_|ClassMethod $function
* @return list<Variable>
*/
private function getVariables(Function_|ClassMethod $function): array
{
$variableVisitor = new FindingVisitor(function ($node) {
return $node instanceof Variable;
});
$traverser = new \PhpParser\NodeTraverser();
$traverser->addVisitor($variableVisitor);
$traverser->traverse([$function]);
$functions = $this->collectParseResultPerFunctionLike($functionLikes);

return $variableVisitor->getFoundNodes(); // @phpstan-ignore-line
return new ParseResult($functions);
}

/**
* @param list<Stmt> $stmts
* @param list<FunctionLike> $functionLikes
* @return list<Func>
*/
private function parseFunctions(array $stmts): array
private function collectParseResultPerFunctionLike(array $functionLikes): array
{
$foundFunctions = $this->getFunctions($stmts);
return array_map(function (FunctionLike $function) {
$functionIdentifier = $function->name->name ?? $function->getType() . '@' . $function->getStartLine();
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

関数名がない場合に便宜的な名前をつけるようにしています。


$functions = [];
foreach ($foundFunctions as $foundFunction) {
$variables = $this->getVariables($foundFunction);
$func = new Func($foundFunction->name->name);
$func = new Func($functionIdentifier);

$variables = $this->nodeFinder->findInstanceOf($function, Variable::class);
foreach ($variables as $variable) {
$func->addVariable(new VarReference($variable->name, $variable->getLine())); // @phpstan-ignore-line
}
$functions[] = $func;
}
return $functions;
}

/**
* @param list<Stmt> $stmt
* @return list<Class_>
*/
private function getClasses(array $stmt): array
{
$classVisitor = new FindingVisitor(function ($node) {
return $node instanceof \PhpParser\Node\Stmt\Class_;
});
$traverser = new \PhpParser\NodeTraverser();
$traverser->addVisitor($classVisitor);
$traverser->traverse($stmt);

return $classVisitor->getFoundNodes(); // @phpstan-ignore-line
}

/**
* @param list<Stmt> $stmts
* @return list<Func>
*/
private function parseClasses(array $stmts): array
{
$foundClasses = $this->getClasses($stmts);

$methods = [];
foreach ($foundClasses as $foundClass) {
foreach ($foundClass->getMethods() as $method) {
$variables = $this->getVariables($method);
$func = new Func(sprintf('%s::%s', $foundClass->name, $method->name->name));
foreach ($variables as $variable) {
$func->addVariable(new VarReference($variable->name, $variable->getLine())); // @phpstan-ignore-line
}
$methods[] = $func;
}
}
return $methods;
}

public function parse(string $content): ParseResult
{
$stmts = $this->parser->parse($content);
if ($stmts === null) {
throw new ParseFailedException();
}
$dumper = new NodeDumper();
// echo $dumper->dump($stmts) . PHP_EOL;

$functions = $this->parseFunctions($stmts) + $this->parseClasses($stmts);

return new ParseResult($functions);
return $func;
}, $functionLikes);
}
}
3 changes: 2 additions & 1 deletion test/VariableParserTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ public function testParseClass(): void
$this->assertInstanceOf(ParseResult::class, $result);
$functions = $result->functions;
$this->assertCount(1, $functions);
$this->assertEquals('Clazz::bigFunction', $functions[0]->name);
// $this->assertEquals('Clazz::bigFunction', $functions[0]->name);
$this->assertEquals('bigFunction', $functions[0]->name);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

クラス名が出力されなくなったことをテストに反映しました。クラス名の出力を維持する場合、元に戻す必要があります。

$this->assertCount(4, $functions[0]->getVariables());

$vars = $functions[0]->getVariables();
Expand Down