Skip to content

Instantly share code, notes, and snippets.

@cviebrock
Last active September 13, 2024 00:21
Show Gist options
  • Save cviebrock/f9240bd86f8a747cec8c3af15962b546 to your computer and use it in GitHub Desktop.
Save cviebrock/f9240bd86f8a747cec8c3af15962b546 to your computer and use it in GitHub Desktop.
PHP function that uses AST to try and find all instances of strings that are class names (so you can convert them to `MyClass::class`)
<?php declare(strict_types=1);
/**
* PHP script that uses composer files to find all classes in a project,
* then uses AST to try and find all instances of string expressions in the project,
* then filters that list of string expressions to those that match class names.
*
* The idea is to look for instances of:
*
* someFunction('MyClass');
*
* so that you can convert them to:
*
* someFunction(MyClass::name);
*
* PROBABLY WILL NOT FIND ALL INSTANCES -- USE AT OWN RISK!
*/
require __DIR__ . '/vendor/autoload.php';
$classmap = include(__DIR__ . '/vendor/composer/autoload_classmap.php');
$projectClasses = array_filter(
$classmap,
fn ($class) => (
!str_starts_with($class, __DIR__.'/vendor/')
)
);
$matches = [];
foreach ($projectClasses as $className => $filePath) {
$strings = [];
$ast = ast\parse_file($filePath, $version=110);
traverseAst($ast, $strings);
$matches[$filePath] = array_filter(
$strings,
function ($entry) use ($projectClasses) {
return array_key_exists($entry['str'], $projectClasses);
}
);
}
$c = 0;
foreach ($matches as $filePath => $fileMatches) {
foreach ($fileMatches as $match) {
printf(
"%s:%d ... %s ...\n",
$filePath, $match['line'], $match['str']
);
$c++;
}
}
echo "Found {$c} strings.\n";
function traverseAst(ast\Node $node, array &$strings, $kind=null): void
{
foreach ($node->children as $child) {
if ($child instanceof ast\Node) {
traverseAst($child, $strings, $kind);
} else {
switch ($node->kind) {
case \ast\AST_ARG_LIST:
case \ast\AST_ECHO:
case \ast\AST_BINARY_OP:
$strings[] = [
'line' => $node->lineno,
'str' => (string) $child,
];
break;
default:
// nothing
break;
}
// printf(
// "% 4d: [%d] %s '%s'\n",
// $node->lineno,
// $node->kind,
// gettype($child),
// $child,
// );
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment