mirror of
https://github.com/macintoshplus/mongo-php-driver.git
synced 2026-04-25 17:38:09 +02:00
Add script to generate function map for static analysis tools (#1436)
* Add script to generate function map for static analysis tools * Refactor function map generation script * Remove workaround for unserialize methods * Update list of phony make targets * Add section about generating function maps to contribution docs * Fix code style in function map generator
This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
<?php
|
||||
|
||||
if (PHP_VERSION_ID < 80000) {
|
||||
echo 'This script requires PHP 8.0 or higher';
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$filename = __DIR__ . '/functionmap.php';
|
||||
(new FunctionMapGenerator)->createFunctionMap($filename);
|
||||
printf("Created call map in %s\n", $filename);
|
||||
|
||||
class FunctionMapGenerator
|
||||
{
|
||||
public function createFunctionMap(string $filename): void
|
||||
{
|
||||
$this->writeFunctionMap($filename, $this->getFunctionMap());
|
||||
}
|
||||
|
||||
private function getFunctionMap(): array
|
||||
{
|
||||
$classes = array_filter(get_declared_classes(), $this->filterItems(...));
|
||||
$interfaces = array_filter(get_declared_interfaces(), $this->filterItems(...));
|
||||
$functions = array_filter(get_defined_functions()['internal'], $this->filterItems(...));
|
||||
|
||||
$functionMap = [];
|
||||
|
||||
// Generate call map for functions
|
||||
foreach ($functions as $functionName) {
|
||||
$reflectionFunction = new ReflectionFunction($functionName);
|
||||
$functionMap[$reflectionFunction->getName()] = $this->getFunctionMapEntry($reflectionFunction);
|
||||
}
|
||||
|
||||
// Generate call map for classes and interfaces
|
||||
$members = array_merge($classes, $interfaces);
|
||||
sort($members);
|
||||
|
||||
$skippedMethods = ['__set_state', '__wakeup', '__serialize', '__unserialize'];
|
||||
|
||||
foreach ($members as $member) {
|
||||
$reflectionClass = new ReflectionClass($member);
|
||||
|
||||
foreach ($reflectionClass->getMethods(ReflectionMethod::IS_PUBLIC) as $method) {
|
||||
if ($method->getDeclaringClass() != $reflectionClass && $method->getName() != '__toString') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (in_array($method->getName(), $skippedMethods, true)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$methodKey = $reflectionClass->getName() . '::' . $method->getName();
|
||||
$functionMap[$methodKey] = $this->getFunctionMapEntry($method);
|
||||
}
|
||||
}
|
||||
|
||||
return $functionMap;
|
||||
}
|
||||
|
||||
private function writeFunctionMap(string $filename, array $functionMap): void
|
||||
{
|
||||
$lines = [];
|
||||
foreach ($functionMap as $methodName => $typeInfo) {
|
||||
$generatedTypeInfo = implode(
|
||||
', ',
|
||||
array_map(
|
||||
function (string|int $key, string $value): string {
|
||||
if (is_int($key)) {
|
||||
return $this->removeDoubleBackslash(var_export($value, true));
|
||||
}
|
||||
|
||||
return sprintf('%s => %s', var_export($key, true), $this->removeDoubleBackslash(var_export($value, true)));
|
||||
},
|
||||
array_keys($typeInfo),
|
||||
array_values($typeInfo)
|
||||
)
|
||||
);
|
||||
|
||||
$lines[] = sprintf(
|
||||
' %s => [%s],',
|
||||
$this->removeDoubleBackslash(var_export($methodName, true)),
|
||||
$generatedTypeInfo
|
||||
);
|
||||
}
|
||||
|
||||
$fileTemplate = <<<'PHP'
|
||||
<?php
|
||||
|
||||
$mongoDBFunctionMap = [
|
||||
%s
|
||||
];
|
||||
|
||||
PHP;
|
||||
|
||||
file_put_contents($filename, sprintf($fileTemplate, implode("\n", $lines)));
|
||||
}
|
||||
|
||||
private function filterItems(string $name): bool {
|
||||
$namespaces = ['MongoDB\BSON\\', 'MongoDB\Driver\\'];
|
||||
|
||||
$name = strtolower($name);
|
||||
|
||||
foreach ($namespaces as $namespace) {
|
||||
// Always compare lowercase names, as get_defined_functions lowercases function names by default
|
||||
if (str_starts_with($name, strtolower($namespace))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function getFunctionMapEntry(ReflectionFunctionAbstract $function): array
|
||||
{
|
||||
$returnType = match(true) {
|
||||
$function->hasReturnType() => (string) $function->getReturnType(),
|
||||
$function->hasTentativeReturnType() => (string) $function->getTentativeReturnType(),
|
||||
default => 'void',
|
||||
};
|
||||
|
||||
$functionMapEntry = [$returnType];
|
||||
|
||||
foreach ($function->getParameters() as $parameter) {
|
||||
$parameterKey = $parameter->getName();
|
||||
if ($parameter->isOptional()) {
|
||||
$parameterKey .= '=';
|
||||
}
|
||||
|
||||
$functionMapEntry[$parameterKey] = (string) $parameter->getType();
|
||||
}
|
||||
|
||||
return $functionMapEntry;
|
||||
}
|
||||
|
||||
private function removeDoubleBackslash(string $string): string
|
||||
{
|
||||
return str_replace('\\\\', '\\', $string);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user