From 997bbffb6528aa2b4d5ae5ed60b880918570dd7f Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Mon, 10 Aug 2026 21:44:13 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E6=9E=84=EF=BC=8C=E6=8B=86=E5=88=86?= =?UTF-8?q?=E5=86=85=E7=BD=AE=E7=B1=BB=E3=80=81=E5=87=BD=E6=95=B0=E3=80=81?= =?UTF-8?q?=E5=B1=9E=E6=80=A7=E8=A1=A8=EF=BC=8C=E8=BF=99=E4=BA=9B=E6=98=AF?= =?UTF-8?q?=20MINIT=20=E9=98=B6=E6=AE=B5=E5=B0=B1=E7=A1=AE=E5=AE=9A?= =?UTF-8?q?=E7=9A=84=EF=BC=8C=E6=97=A0=E9=9C=80=20thread=5Flocal=EF=BC=8C?= =?UTF-8?q?=E5=8F=AF=E9=A2=84=E5=8A=A0=E8=BD=BD=EF=BC=8Crequest=20shutdown?= =?UTF-8?q?=20=E9=98=B6=E6=AE=B5=E6=97=A0=E9=9C=80=E9=87=8A=E6=94=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/CompilerBase.php | 112 +++++++++++++++++++++++++++++++++++++++---- src/Translator.php | 112 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 214 insertions(+), 10 deletions(-) diff --git a/src/CompilerBase.php b/src/CompilerBase.php index 2c0d138c..380ae538 100644 --- a/src/CompilerBase.php +++ b/src/CompilerBase.php @@ -239,6 +239,9 @@ class CompilerBase implements PropertyAccessContext public const string CLASS_MAP = 'class_map'; public const string FUNC_MAP = 'func_map'; public const string PROP_MAP = 'property_map'; + public const string INTERNAL_CLASS_MAP = 'internal_class_map'; + public const string INTERNAL_FUNC_MAP = 'internal_func_map'; + public const string INTERNAL_PROP_MAP = 'internal_property_map'; public const string NAMESPACE_SEPARATOR = '__'; public const string PREFIX = 'php_'; @@ -270,9 +273,16 @@ class CompilerBase implements PropertyAccessContext protected int $classIndex = 0; /** + * 用户定义(请求生命周期)类名 → ID,运行期为 THREAD_LOCAL 缓存,RSHUTDOWN 清理 * @var array */ protected array $classMap = []; + /** + * 内置/编译产物(进程生命周期)类名 → ID,运行期为全局缓存,MINIT 填充,永不清理 + * @var array + */ + protected array $internalClassMap = []; + protected int $internalClassIndex = 0; /** * @var array */ @@ -280,11 +290,28 @@ class CompilerBase implements PropertyAccessContext protected int $funcIndex = 0; /** + * 用户定义(请求生命周期)函数/方法 → ID,运行期为 THREAD_LOCAL 缓存,RSHUTDOWN 清理 + * key 为函数名或 `Class::method` * @var array */ protected array $funcMap = []; + /** + * 内置/编译产物(进程生命周期)函数/方法 → ID,运行期为全局缓存,MINIT 填充,永不清理 + * key 为函数名或 `Class::method` + * @var array + */ + protected array $internalFuncMap = []; + protected int $internalFuncIndex = 0; protected int $propIndex = 0; + /** + * 用户定义类的属性 offset 缓存,key 为 `Class::prop`,RSHUTDOWN 清理 + */ protected array $propMap = []; + /** + * 内置/编译产物类的属性 offset 缓存,key 为 `Class::prop`,MINIT 填充,永不清理 + */ + protected array $internalPropMap = []; + protected int $internalPropIndex = 0; protected const array PHP_RUNTIME_TYPE_MAP = [ 'integer' => Type::INT, 'double' => Type::FLOAT, @@ -884,6 +911,7 @@ class CompilerBase implements PropertyAccessContext abort($expr); break; } + return ''; } public function stop(string $string): never @@ -1194,10 +1222,47 @@ class CompilerBase implements PropertyAccessContext return implode(self::NAMESPACE_SEPARATOR, $names); } + /** + * 判断类的符号指针是否进程级稳定(MINIT 注册,跨请求缓存安全)。 + * 编译产物(本单元编译的类/接口)与 PHP 内置类/接口均满足条件。 + */ + protected function isProcessStableClass(string $className): bool + { + if ($this->hasClass($className) || $this->hasInterface($className)) { + return true; + } + $ref = Reflection::getClass(ltrim($className, '\\')); + return $ref !== null && $ref->isInternal(); + } + + /** + * 判断函数/方法符号指针是否进程级稳定。 + * `Class::method` 形式的 key 以其所属类的稳定性为准。 + */ + protected function isProcessStableFunction(string $funcName): bool + { + if (str_contains($funcName, '::')) { + [$class] = explode('::', $funcName, 2); + return $this->isProcessStableClass($class); + } + if ($this->hasFunction($funcName)) { + return true; + } + $ref = Reflection::getFunction(ltrim($funcName, '\\')); + return $ref !== null && $ref->isInternal(); + } + protected function getClassId(string $className): int { if (isset($this->classMap[$className])) { - $id = $this->classMap[$className]; + return $this->classMap[$className]; + } + if (isset($this->internalClassMap[$className])) { + return $this->internalClassMap[$className]; + } + if ($this->isProcessStableClass($className)) { + $id = $this->internalClassIndex++; + $this->internalClassMap[$className] = $id; } else { $id = $this->classIndex++; $this->classMap[$className] = $id; @@ -1208,7 +1273,14 @@ class CompilerBase implements PropertyAccessContext protected function getFuncId(string $funcName): int { if (isset($this->funcMap[$funcName])) { - $id = $this->funcMap[$funcName]; + return $this->funcMap[$funcName]; + } + if (isset($this->internalFuncMap[$funcName])) { + return $this->internalFuncMap[$funcName]; + } + if ($this->isProcessStableFunction($funcName)) { + $id = $this->internalFuncIndex++; + $this->internalFuncMap[$funcName] = $id; } else { $id = $this->funcIndex++; $this->funcMap[$funcName] = $id; @@ -1223,7 +1295,14 @@ class CompilerBase implements PropertyAccessContext { $key = $className . '::' . $propName; if (isset($this->propMap[$key])) { - $id = $this->propMap[$key]; + return $this->propMap[$key]; + } + if (isset($this->internalPropMap[$key])) { + return $this->internalPropMap[$key]; + } + if ($this->isProcessStableClass($className)) { + $id = $this->internalPropIndex++; + $this->internalPropMap[$key] = $id; } else { $id = $this->propIndex++; $this->propMap[$key] = $id; @@ -1234,7 +1313,8 @@ class CompilerBase implements PropertyAccessContext protected function getClassEntryPtr(string $className): string { $id = $this->getClassId($className); - return 'php_get_class(' . $id . ', ' . $this->getLiteralString($className) . ')'; + $helper = isset($this->internalClassMap[$className]) ? 'php_get_internal_class' : 'php_get_class'; + return $helper . '(' . $id . ', ' . $this->getLiteralString($className) . ')'; } protected function getCeWrapper(string $className): string @@ -1243,28 +1323,32 @@ class CompilerBase implements PropertyAccessContext return $this->context->ceWrappers[$className]; } $object = $this->addTmpVar(Type::OBJECT); - $this->context->beforeStmtLines[] = 'Z_PTR_P(' . $object . '.ptr()) = ' . $this->getClassEntryPtr($className) . ';'; $this->context->ceWrappers[$className] = $object; return $object; } protected function getFuncPtr(string $funcName): string { - return 'php_get_func(' . $this->getFuncId($funcName) . ', ' . $this->getLiteralString($funcName) . ')'; + $id = $this->getFuncId($funcName); + $helper = isset($this->internalFuncMap[$funcName]) ? 'php_get_internal_func' : 'php_get_func'; + return $helper . '(' . $id . ', ' . $this->getLiteralString($funcName) . ')'; } protected function getMethodPtr(string $class, string $method): string { $funcId = $this->getFuncId($class . '::' . $method); $classId = $this->getClassId($class); - return 'php_get_method(' . $funcId . ', ' . $this->getLiteralString($method) . ', ' . $classId . ', ' . $this->getLiteralString($class) . ')'; + // 方法的稳定性与所属类一致,因此 class_id 必定落在同一张表中 + $helper = isset($this->internalFuncMap[$class . '::' . $method]) ? 'php_get_internal_method' : 'php_get_method'; + return $helper . '(' . $funcId . ', ' . $this->getLiteralString($method) . ', ' . $classId . ', ' . $this->getLiteralString($class) . ')'; } protected function getPropertyOffset(string $class, string $prop): string { - $funcId = $this->getPropertyId($class, $prop); + $propId = $this->getPropertyId($class, $prop); $classId = $this->getClassId($class); - return 'php_get_prop(' . $funcId . ', ' . $this->getLiteralString($prop) . ', ' . $classId . ', ' . $this->getLiteralString($class) . ')'; + $helper = isset($this->internalPropMap[$class . '::' . $prop]) ? 'php_get_internal_prop' : 'php_get_prop'; + return $helper . '(' . $propId . ', ' . $this->getLiteralString($prop) . ', ' . $classId . ', ' . $this->getLiteralString($class) . ')'; } protected function writeLog($msg): void @@ -1319,6 +1403,7 @@ class CompilerBase implements PropertyAccessContext abort($expr); break; } + return ''; } /** @@ -4313,6 +4398,15 @@ class CompilerBase implements PropertyAccessContext $code .= $this->getIndent() . 'int _cnt_flag = 0;' . PHP_EOL; } $code .= $this->genLocalVarDecl($this->context->localVars); + // Native static calls pass a lightweight Object containing the called + // class entry. A wrapper can be shared by all calls to the same class, + // but its initialization must dominate every control-flow branch that + // may use it. Emitting it at the first call site is unsafe when that + // site belongs to an if/switch/loop branch that is not executed. + foreach ($this->context->ceWrappers as $className => $object) { + $code .= $this->getIndent() . 'Z_PTR_P(' . $object . '.ptr()) = ' + . $this->getClassEntryPtr($className) . ';' . PHP_EOL; + } foreach ($this->context->globalVars as $name => $type) { // $GLOBALS is handled via php_globals_array() at each read site if ($name === 'GLOBALS') { diff --git a/src/Translator.php b/src/Translator.php index bb0e5090..43a2684c 100644 --- a/src/Translator.php +++ b/src/Translator.php @@ -744,9 +744,13 @@ class Translator extends Preprocessor // 确保数组大小至少为 1,避免 C/C++ 编译错误 $classCount = max(1, count($this->classMap)); $lines[] = 'extern THREAD_LOCAL zend_class_entry *' . self::PREFIX . self::CLASS_MAP . '[' . $classCount . '];' . PHP_EOL; + $internalClassCount = max(1, count($this->internalClassMap)); + $lines[] = 'extern zend_class_entry *' . self::PREFIX . self::INTERNAL_CLASS_MAP . '[' . $internalClassCount . '];' . PHP_EOL; $funcCount = max(1, count($this->funcMap)); $lines[] = 'extern THREAD_LOCAL zend_function *' . self::PREFIX . self::FUNC_MAP . '[' . $funcCount . '];' . PHP_EOL; + $internalFuncCount = max(1, count($this->internalFuncMap)); + $lines[] = 'extern zend_function *' . self::PREFIX . self::INTERNAL_FUNC_MAP . '[' . $internalFuncCount . '];' . PHP_EOL; $pythonModuleDeclarations = $this->genPythonModuleDataDeclarations(); if ($pythonModuleDeclarations !== '') { @@ -755,6 +759,8 @@ class Translator extends Preprocessor $propCount = max(1, count($this->propMap)); $lines[] = 'extern THREAD_LOCAL uint32_t ' . self::PREFIX . self::PROP_MAP . '[' . $propCount . '];' . PHP_EOL; + $internalPropCount = max(1, count($this->internalPropMap)); + $lines[] = 'extern uint32_t ' . self::PREFIX . self::INTERNAL_PROP_MAP . '[' . $internalPropCount . '];' . PHP_EOL; foreach ($this->getClassLikesWithConstants() as $classDef) { foreach ($classDef->constants as $constant) { @@ -812,14 +818,18 @@ class Translator extends Preprocessor $code .= "// class entry \n"; // 确保数组大小至少为 1,避免 C/C++ 编译错误 $code .= 'THREAD_LOCAL zend_class_entry *' . self::PREFIX . self::CLASS_MAP . '[' . max(1, count($this->classMap)) . '];' . PHP_EOL; + // 内置/编译产物类,进程级缓存,MINIT 填充,无需 THREAD_LOCAL,RSHUTDOWN 不清理 + $code .= 'zend_class_entry *' . self::PREFIX . self::INTERNAL_CLASS_MAP . '[' . max(1, count($this->internalClassMap)) . '];' . PHP_EOL; $code .= "// func \n"; $code .= 'THREAD_LOCAL zend_function *' . self::PREFIX . self::FUNC_MAP . '[' . max(1, count($this->funcMap)) . '];' . PHP_EOL; + $code .= 'zend_function *' . self::PREFIX . self::INTERNAL_FUNC_MAP . '[' . max(1, count($this->internalFuncMap)) . '];' . PHP_EOL; $code .= $this->genPythonModuleStorage(); $code .= "// property \n"; $code .= 'THREAD_LOCAL uint32_t ' . self::PREFIX . self::PROP_MAP . '[' . max(1, count($this->propMap)) . '];' . PHP_EOL; + $code .= 'uint32_t ' . self::PREFIX . self::INTERNAL_PROP_MAP . '[' . max(1, count($this->internalPropMap)) . '];' . PHP_EOL; $code .= "// functions \n"; @@ -852,9 +862,61 @@ uint32_t php_get_prop(int prop_id, const php::Str &prop_name, int class_id, cons } return php_property_map[prop_id] - 1024; } + +zend_class_entry *php_get_internal_class(int class_id, const php::Str &class_name) { + if (UNEXPECTED(php_internal_class_map[class_id] == nullptr)) { + php_internal_class_map[class_id] = php::getClassEntrySafe(class_name); + } + return php_internal_class_map[class_id]; +} + +zend_function *php_get_internal_func(int func_id, const php::Str &func_name) { + if (UNEXPECTED(php_internal_func_map[func_id] == nullptr)) { + php_internal_func_map[func_id] = php::getFunction(func_name); + } + return php_internal_func_map[func_id]; +} + +zend_function *php_get_internal_method(int func_id, const php::Str &method_name, int class_id, const php::Str &class_name) { + if (UNEXPECTED(php_internal_func_map[func_id] == nullptr)) { + auto ce = php_get_internal_class(class_id, class_name); + php_internal_func_map[func_id] = php::getMethod(ce, method_name); + } + return php_internal_func_map[func_id]; +} + +uint32_t php_get_internal_prop(int prop_id, const php::Str &prop_name, int class_id, const php::Str &class_name) { + if (UNEXPECTED(php_internal_property_map[prop_id] == 0)) { + php_internal_property_map[prop_id] = php::getPropertyOffset(class_name, prop_name) + 1024; + } + return php_internal_property_map[prop_id] - 1024; +} CODE; $code .= "\n\n"; + // MINIT 阶段填充内部符号缓存使用的容错查找函数(查找失败返回空,留给运行期惰性重试) + if ($this->internalFuncMap !== []) { + $code .= <<<'CODE' +static zend_function *php_find_internal_function(const php::Str &name) { + auto lc_name = zend_string_tolower(name.str()); + auto fn = (zend_function *) zend_hash_find_ptr(CG(function_table), lc_name); + zend_string_release(lc_name); + return fn; +} + +static zend_function *php_find_internal_method(zend_class_entry *ce, const php::Str &name) { + if (UNEXPECTED(ce == nullptr)) { + return nullptr; + } + auto lc_name = zend_string_tolower(name.str()); + auto fn = (zend_function *) zend_hash_find_ptr(&ce->function_table, lc_name); + zend_string_release(lc_name); + return fn; +} +CODE; + $code .= "\n\n"; + } + $code .= $this->genPythonModuleGetter(); $code .= "// literal strings \n"; @@ -936,6 +998,7 @@ CODE; foreach ($this->registerSymbols as $registerSymbolFn) { $code .= $registerSymbolFn . '(module_number);' . PHP_EOL; } + $code .= $this->genInternalSymbolCacheInit(); $code .= '} zend_end_try();' . PHP_EOL; $code .= 'return SUCCESS;' . PHP_EOL; $code .= '}' . PHP_EOL . PHP_EOL; @@ -1073,7 +1136,8 @@ CODE; } } - // 扩展模式,需要在 RSHUTDOWN 阶段中清理函数、类、属性表 + // 扩展模式,需要在 RSHUTDOWN 阶段中清理用户定义的函数、类、属性缓存; + // 内置/编译产物符号在 internal_*_map 中,进程级存活,无需清理 if ($this->isBuildModeExt()) { $code .= 'std::memset(' . self::PREFIX . self::FUNC_MAP . ', 0, sizeof(' . self::PREFIX . self::FUNC_MAP . '));' . PHP_EOL; $code .= 'std::memset(' . self::PREFIX . self::CLASS_MAP . ', 0, sizeof(' . self::PREFIX . self::CLASS_MAP . '));' . PHP_EOL; @@ -1147,6 +1211,52 @@ CODE; return $file; } + /** + * 生成 MINIT 阶段填充内部符号缓存(进程级类/函数/属性)的代码。 + * 类与函数使用容错查找:跨扩展符号可能因模块初始化顺序尚未注册, + * 查找失败时留空,由运行期 helper 惰性重试(此时抛错语义与拆分前一致)。 + * 属性缓存的所属类必为本模块编译产物(已在上方注册完毕),直接调用 helper 填充。 + */ + protected function genInternalSymbolCacheInit(): string + { + if ($this->internalClassMap === [] && $this->internalFuncMap === [] && $this->internalPropMap === []) { + return ''; + } + $code = '// internal symbol caches' . PHP_EOL; + foreach ($this->internalClassMap as $name => $id) { + $code .= self::PREFIX . self::INTERNAL_CLASS_MAP . '[' . $id . '] = php::getClassEntry(' + . $this->genSymbolNameExpr($name) . ');' . PHP_EOL; + } + foreach ($this->internalFuncMap as $key => $id) { + if (str_contains($key, '::')) { + [$class, $method] = explode('::', $key, 2); + $code .= self::PREFIX . self::INTERNAL_FUNC_MAP . '[' . $id . '] = php_find_internal_method(php::getClassEntry(' + . $this->genSymbolNameExpr($class) . '), ' . $this->genSymbolNameExpr($method) . ');' . PHP_EOL; + } else { + $code .= self::PREFIX . self::INTERNAL_FUNC_MAP . '[' . $id . '] = php_find_internal_function(' + . $this->genSymbolNameExpr($key) . ');' . PHP_EOL; + } + } + foreach ($this->internalPropMap as $key => $id) { + [$class, $prop] = explode('::', $key, 2); + $code .= '(void) php_get_internal_prop(' . $id . ', ' . $this->genSymbolNameExpr($prop) + . ', 0, ' . $this->genSymbolNameExpr($class) . ');' . PHP_EOL; + } + return $code; + } + + /** + * 生成符号名称的 C++ 表达式。优先复用字面量字符串表中的已有条目; + * 表条目在 MINIT 代码生成前已输出,缺失时(不应发生)退化为内联字符串。 + */ + private function genSymbolNameExpr(string $name): string + { + if (!$this->noLiteralStrings && isset($this->literalStrings[$name])) { + return self::LITERAL_STRINGS . '[' . $this->literalStrings[$name] . ']'; + } + return Type::STR . '{ZEND_STRL(' . $this->genCharPtr($name, true) . '), true}'; + } + public function getModuleName(): string { return Constants::EXTENSION_PREFIX . $this->targetName;