Skip to content

Commit b94591c

Browse files
committed
Add script to generate function map for static analysis tools
1 parent c4d1951 commit b94591c

File tree

1 file changed

+95
-0
lines changed

1 file changed

+95
-0
lines changed

scripts/create-functionmap.php

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
<?php
2+
3+
$filter = function (string $name): bool {
4+
$namespaces = ['MongoDB\BSON\\', 'MongoDB\Driver\\'];
5+
6+
foreach ($namespaces as $namespace) {
7+
if (str_starts_with($name, $namespace) || str_starts_with($name, strtolower($namespace))) {
8+
return true;
9+
}
10+
}
11+
12+
return false;
13+
};
14+
15+
$getFunctionCallMapEntry = function(ReflectionFunctionAbstract $function): array {
16+
$returnType = match(true) {
17+
$function->hasReturnType() => (string) $function->getReturnType(),
18+
$function->hasTentativeReturnType() => (string) $function->getTentativeReturnType(),
19+
default => 'void',
20+
};
21+
$callmapEntry = [$returnType];
22+
23+
foreach ($function->getParameters() as $parameter) {
24+
$parameterKey = $parameter->getName();
25+
if ($parameter->isOptional()) {
26+
$parameterKey .= '=';
27+
}
28+
29+
$parameterType = (string) $parameter->getType();
30+
if ($function->getName() === 'unserialize' && $parameter->getName() === 'serialized') {
31+
$parameterType = 'string';
32+
}
33+
34+
$callmapEntry[$parameterKey] = $parameterType;
35+
}
36+
37+
return $callmapEntry;
38+
};
39+
40+
$classes = array_filter(get_declared_classes(), $filter);
41+
$interfaces = array_filter(get_declared_interfaces(), $filter);
42+
$functions = array_filter(get_defined_functions()['internal'], $filter);
43+
44+
$callmap = [];
45+
46+
// Generate call map for functions
47+
foreach ($functions as $functionName) {
48+
$reflectionFunction = new ReflectionFunction($functionName);
49+
$callmap[$reflectionFunction->getName()] = $getFunctionCallMapEntry($reflectionFunction);
50+
}
51+
52+
// Generate call map for classes and interfaces
53+
$members = array_merge($classes, $interfaces);
54+
sort($members);
55+
56+
$skippedMethods = ['__set_state', '__wakeup', '__serialize', '__unserialize'];
57+
58+
foreach ($members as $member) {
59+
$reflectionClass = new ReflectionClass($member);
60+
61+
foreach ($reflectionClass->getMethods(ReflectionMethod::IS_PUBLIC) as $method) {
62+
if ($method->getDeclaringClass() != $reflectionClass && $method->getName() != '__toString') {
63+
continue;
64+
}
65+
66+
if (in_array($method->getName(), $skippedMethods, true)) {
67+
continue;
68+
}
69+
70+
$methodKey = $reflectionClass->getName() . '::' . $method->getName();
71+
$callmap[$methodKey] = $getFunctionCallMapEntry($method);
72+
}
73+
}
74+
75+
$map = var_export($callmap, true);
76+
77+
// Format output
78+
79+
$replacements = [
80+
'#array \(#' => '[', // Short array syntax (opening)
81+
'#\)#' => ']', // Short array syntax (closing)
82+
'#0 => #' => '', // Numeric index for return type
83+
'#\\\\{2}#' => '\\', // Double backslash
84+
'# => $\s+#m' => ' => ', // Newlines at start of array
85+
'#\s+\[$\s+#m' => ' [', // Newlines at start of array
86+
'#,$\s+#m' => ', ', // Newline after array elements
87+
'#, \],\s+#' => "],\n", // Newline after array elements
88+
'#^\s*\'#m' => ' \'', // Fix indentation
89+
];
90+
91+
echo preg_replace(
92+
array_keys($replacements),
93+
array_values($replacements),
94+
$map,
95+
);

0 commit comments

Comments
 (0)