Last active
March 1, 2024 13:12
-
-
Save kocsismate/c0d10c820606dfe297f09374aa634df5 to your computer and use it in GitHub Desktop.
Analyze implicit nullable parameter types
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
error_reporting(E_ALL); | |
ini_set("memory_limit", "6G"); | |
use PhpParser\Lexer\Emulative; | |
use PhpParser\Node; | |
use PhpParser\Node\Expr\ConstFetch; | |
use PhpParser\Node\NullableType; | |
use PhpParser\Node\Param; | |
use PhpParser\Node\UnionType; | |
use PhpParser\NodeVisitorAbstract; | |
require __DIR__ . '/vendor/autoload.php'; | |
$lexer = new Emulative([ | |
'usedAttributes' => [ | |
'comments', 'startLine', 'endLine', | |
'startFilePos', 'endFilePos', | |
] | |
]); | |
$parser = new PhpParser\Parser\Php7($lexer); | |
$visitor = new class (__DIR__ . '/sources/') extends NodeVisitorAbstract { | |
public $path; | |
public $code; | |
public int $totalImplicitNullableParamTypes = 0; | |
public array $projectsUsingImplicitNullableParamTypes = []; | |
private string $sourceDir; | |
public function __construct(string $sourceDir) | |
{ | |
$this->sourceDir = $sourceDir; | |
} | |
public function enterNode(Node $node) { | |
if (!$node instanceof Param) { | |
return; | |
} | |
if (!$node->type || !$node->default) { | |
return; | |
} | |
if ($node->default instanceof ConstFetch === false || $node->default->name->toLowerString() !== "null") { | |
return; | |
} | |
if ($node->type instanceof NullableType) { | |
return; | |
} | |
if ($node->type instanceof UnionType) { | |
foreach ($node->type->types as $type) { | |
if ($type instanceof NullableType) { | |
return; | |
} | |
} | |
} | |
if ($node->type instanceof Node\Identifier && $node->type->toLowerString() === "mixed") { | |
return; | |
} | |
$this->totalImplicitNullableParamTypes++; | |
$projectDirs = explode("/", str_replace($this->sourceDir, "", $this->path), 3); | |
$projectDir = $projectDirs[0] . "/" . $projectDirs[1]; | |
if (!isset($this->projectsUsingImplicitNullableParamTypes[$projectDir])) { | |
$this->projectsUsingImplicitNullableParamTypes[$projectDir] = true; | |
} | |
//echo $this->path . ":" . $node->getStartLine() . "\n"; | |
} | |
}; | |
$traverser = new PhpParser\NodeTraverser; | |
$traverser->addVisitor($visitor); | |
$it = new RecursiveIteratorIterator( | |
new RecursiveDirectoryIterator(__DIR__ . '/sources'), | |
RecursiveIteratorIterator::LEAVES_ONLY | |
); | |
$i = 0; | |
foreach ($it as $f) { | |
$path = $f->getPathName(); | |
if (!preg_match('/\.php$/', $path)) { | |
continue; | |
} | |
if (++$i % 1000 == 0) { | |
echo $i . "\n"; | |
} | |
$code = file_get_contents($path); | |
try { | |
$stmts = $parser->parse($code); | |
} catch (PhpParser\Error $e) { | |
echo $path . "\n"; | |
echo "Parse error: " . $e->getMessage() . "\n"; | |
continue; | |
} | |
$visitor->path = $path; | |
$visitor->code = $code; | |
$traverser->traverse($stmts); | |
} | |
echo "Total implicit nullable parameter types: ", $visitor->totalImplicitNullableParamTypes, "\n"; | |
echo "Total projects using implicit nullable parameter types: ", count($visitor->projectsUsingImplicitNullableParamTypes), "\n"; | |
foreach ($visitor->projectsUsingImplicitNullableParamTypes as $project => $_) { | |
echo "$project\n"; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment