Skip to content

Commit aa72cef

Browse files
committed
Refactor function map generation script
1 parent b94591c commit aa72cef

File tree

3 files changed

+161
-95
lines changed

3 files changed

+161
-95
lines changed

Makefile.frag

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,3 +66,25 @@ libmongoc-version-current:
6666

6767
libmongocrypt-version-current:
6868
cd src/libmongocrypt/ && python etc/calc_release_version.py > ../LIBMONGOCRYPT_VERSION_CURRENT
69+
70+
generate-function-map: all
71+
@if test ! -z "$(PHP_EXECUTABLE)" && test -x "$(PHP_EXECUTABLE)"; then \
72+
INI_FILE=`$(PHP_EXECUTABLE) -d 'display_errors=stderr' -r 'echo php_ini_loaded_file();' 2> /dev/null`; \
73+
if test "$$INI_FILE"; then \
74+
$(EGREP) -h -v $(PHP_DEPRECATED_DIRECTIVES_REGEX) "$$INI_FILE" > $(top_builddir)/tmp-php.ini; \
75+
else \
76+
echo > $(top_builddir)/tmp-php.ini; \
77+
fi; \
78+
INI_SCANNED_PATH=`$(PHP_EXECUTABLE) -d 'display_errors=stderr' -r '$$a = explode(",\n", trim(php_ini_scanned_files())); echo $$a[0];' 2> /dev/null`; \
79+
if test "$$INI_SCANNED_PATH"; then \
80+
INI_SCANNED_PATH=`$(top_srcdir)/build/shtool path -d $$INI_SCANNED_PATH`; \
81+
$(EGREP) -h -v $(PHP_DEPRECATED_DIRECTIVES_REGEX) "$$INI_SCANNED_PATH"/*.ini >> $(top_builddir)/tmp-php.ini; \
82+
fi; \
83+
CC="$(CC)" \
84+
$(PHP_EXECUTABLE) -n -c $(top_builddir)/tmp-php.ini -n -c $(top_builddir)/tmp-php.ini -d extension_dir=$(top_builddir)/modules/ $(PHP_TEST_SHARED_EXTENSIONS) $(top_srcdir)/scripts/generate-functionmap.php; \
85+
RESULT_EXIT_CODE=$$?; \
86+
rm $(top_builddir)/tmp-php.ini; \
87+
exit $$RESULT_EXIT_CODE; \
88+
else \
89+
echo "ERROR: Cannot generate function maps without CLI sapi."; \
90+
fi

scripts/create-functionmap.php

Lines changed: 0 additions & 95 deletions
This file was deleted.

scripts/generate-functionmap.php

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
<?php
2+
3+
if (PHP_VERSION_ID < 80000) {
4+
echo 'This script requires PHP 8.0 or higher';
5+
exit(1);
6+
}
7+
8+
$filename = __DIR__ . '/functionmap.php';
9+
(new FunctionMapGenerator)->createFunctionMap($filename);
10+
printf("Created call map in %s\n", $filename);
11+
12+
class FunctionMapGenerator
13+
{
14+
public function createFunctionMap(string $filename): void {
15+
$this->writeFunctionMap($filename, $this->getFunctionMap());
16+
}
17+
18+
public function getFunctionMap(): array {
19+
$classes = array_filter(get_declared_classes(), $this->filterItems(...));
20+
$interfaces = array_filter(get_declared_interfaces(), $this->filterItems(...));
21+
$functions = array_filter(get_defined_functions()['internal'], $this->filterItems(...));
22+
23+
$functionMap = [];
24+
25+
// Generate call map for functions
26+
foreach ($functions as $functionName) {
27+
$reflectionFunction = new ReflectionFunction($functionName);
28+
$functionMap[$reflectionFunction->getName()] = $this->getFunctionMapEntry($reflectionFunction);
29+
}
30+
31+
// Generate call map for classes and interfaces
32+
$members = array_merge($classes, $interfaces);
33+
sort($members);
34+
35+
$skippedMethods = ['__set_state', '__wakeup', '__serialize', '__unserialize'];
36+
37+
foreach ($members as $member) {
38+
$reflectionClass = new ReflectionClass($member);
39+
40+
foreach ($reflectionClass->getMethods(ReflectionMethod::IS_PUBLIC) as $method) {
41+
if ($method->getDeclaringClass() != $reflectionClass && $method->getName() != '__toString') {
42+
continue;
43+
}
44+
45+
if (in_array($method->getName(), $skippedMethods, true)) {
46+
continue;
47+
}
48+
49+
$methodKey = $reflectionClass->getName() . '::' . $method->getName();
50+
$functionMap[$methodKey] = $this->getFunctionMapEntry($method);
51+
}
52+
}
53+
54+
return $functionMap;
55+
}
56+
57+
private function writeFunctionMap(string $filename, array $functionMap): void
58+
{
59+
$lines = [];
60+
foreach ($functionMap as $methodName => $typeInfo) {
61+
$generatedTypeInfo = implode(
62+
', ',
63+
array_map(
64+
function (string|int $key, string $value): string {
65+
if (is_int($key)) {
66+
return $this->removeDoubleBackslash(var_export($value, true));
67+
}
68+
69+
return sprintf('%s => %s', var_export($key, true), $this->removeDoubleBackslash(var_export($value, true)));
70+
},
71+
array_keys($typeInfo),
72+
array_values($typeInfo)
73+
)
74+
);
75+
76+
$lines[] = sprintf(
77+
' %s => [%s],',
78+
$this->removeDoubleBackslash(var_export($methodName, true)),
79+
$generatedTypeInfo
80+
);
81+
}
82+
83+
$fileTemplate = <<<'PHP'
84+
<?php
85+
86+
$mongoDBFunctionMap = [
87+
%s
88+
];
89+
90+
PHP;
91+
92+
file_put_contents($filename, sprintf($fileTemplate, implode("\n", $lines)));
93+
}
94+
95+
private function filterItems(string $name): bool {
96+
$namespaces = ['MongoDB\BSON\\', 'MongoDB\Driver\\'];
97+
98+
$name = strtolower($name);
99+
100+
foreach ($namespaces as $namespace) {
101+
// Always compare lowercase names, as get_defined_functions lowercases function names by default
102+
if (str_starts_with($name, strtolower($namespace))) {
103+
return true;
104+
}
105+
}
106+
107+
return false;
108+
}
109+
110+
private function getFunctionMapEntry(ReflectionFunctionAbstract $function): array {
111+
$returnType = match(true) {
112+
$function->hasReturnType() => (string) $function->getReturnType(),
113+
$function->hasTentativeReturnType() => (string) $function->getTentativeReturnType(),
114+
default => 'void',
115+
};
116+
117+
$functionMapEntry = [$returnType];
118+
119+
foreach ($function->getParameters() as $parameter) {
120+
$parameterKey = $parameter->getName();
121+
if ($parameter->isOptional()) {
122+
$parameterKey .= '=';
123+
}
124+
125+
$parameterType = (string) $parameter->getType();
126+
if ($function->getName() === 'unserialize' && $parameter->getName() === 'serialized') {
127+
$parameterType = 'string';
128+
}
129+
130+
$functionMapEntry[$parameterKey] = $parameterType;
131+
}
132+
133+
return $functionMapEntry;
134+
}
135+
136+
private function removeDoubleBackslash(string $string): string {
137+
return str_replace('\\\\', '\\', $string);
138+
}
139+
}

0 commit comments

Comments
 (0)