Translate all Chinese notes into English

master
韩天峰 1 week ago
parent 89f3cb485b
commit 9e0984acee
  1. 8
      bin/dump-ast.php
  2. 2
      bin/extractor.php
  3. 6
      src/Backend/Clang.php
  4. 82
      src/Backend/CompilerBackend.php
  5. 26
      src/Backend/CompilerFactory.php
  6. 2
      src/Backend/Gcc.php
  7. 22
      src/Backend/GccLikeBackend.php
  8. 52
      src/Backend/Msvc.php
  9. 57
      src/Build/NativeBuildConfigurationTrait.php
  10. 33
      src/Build/SourcePipelineTrait.php
  11. 224
      src/CompilerBase.php
  12. 8
      src/Context/CompilationStateTrait.php
  13. 2
      src/Entity/FunctionDef.php
  14. 78
      src/Extractor.php
  15. 34
      src/Generator/CallArgumentGenerator.php
  16. 3
      src/Generator/ClosureGenerator.php
  17. 77
      src/Generator/ResourceFileGenerator.php
  18. 14
      src/Installer/LibPhpInstaller.php
  19. 8
      src/Metadata/Constants.php
  20. 7
      src/Optimizer/FuncCallOptimizer.php
  21. 9
      src/Parser/ArrayExpressionTrait.php
  22. 12
      src/Parser/AssignOpTrait.php
  23. 4
      src/Parser/BinaryOpTrait.php
  24. 6
      src/Parser/ForeachTrait.php
  25. 4
      src/Parser/FunctionCallTrait.php
  26. 22
      src/Parser/MethodCallTrait.php
  27. 6
      src/Parser/PropertyAccessTrait.php
  28. 2
      src/Parser/SwitchTrait.php
  29. 60
      src/Platform/PlatformBase.php
  30. 29
      src/Platform/UnixPlatform.php
  31. 32
      src/Platform/Windows.php
  32. 40
      src/Preprocessor.php
  33. 4
      src/PythonTools/Converter/PythonAstLoader.php
  34. 26
      src/PythonTools/Converter/PythonToTypePhpConverter.php
  35. 2
      src/Resolver/MagicMethodDetector.php
  36. 26
      src/Resolver/NameResolutionTrait.php
  37. 16
      src/Resolver/PropertyAccessResolver.php
  38. 4
      src/Resolver/Reflection.php
  39. 5
      src/Symbol/SymbolRepository.php
  40. 211
      src/Translator.php
  41. 53
      src/TypeSystem/NativeTypeCompatibilityTrait.php
  42. 16
      src/compiler.php
  43. 18
      src/gen_stub.php

@ -2,11 +2,11 @@
<?php <?php
/** /**
* 导出 PHP 文件的 AST 语法树结构 * Dump the AST syntax tree structure of a PHP file.
* *
* 用法: php bin/dump-ast.php <file.php> * Usage: php bin/dump-ast.php <file.php>
* *
* 示例: * Examples:
* php bin/dump-ast.php examples/hello.php * php bin/dump-ast.php examples/hello.php
* php bin/dump-ast.php src/compiler.php * php bin/dump-ast.php src/compiler.php
*/ */
@ -231,7 +231,7 @@ function dumpNode(NodeAbstract $node, int $depth = 0): void
$end = $node->getEndLine(); $end = $node->getEndLine();
$subInfo = ''; $subInfo = '';
// 为常见节点类型提取关键信息 // Extract key information for common node types.
switch (true) { switch (true) {
case $node instanceof Node\Expr\Variable: case $node instanceof Node\Expr\Variable:
$subInfo = ' $' . ($node->name === null ? '(unset)' : (is_string($node->name) ? $node->name : '...')); $subInfo = ' $' . ($node->name === null ? '(unset)' : (is_string($node->name) ? $node->name : '...'));

@ -151,7 +151,7 @@ function extractorMain(array $argv): void
} }
} }
// 运行主函数 // Run the main function.
if (php_sapi_name() === 'cli') { if (php_sapi_name() === 'cli') {
extractorMain($argv); extractorMain($argv);
} }

@ -7,7 +7,7 @@ use TypePhp\Platform\Windows;
use TypePhp\Platform\Macos; use TypePhp\Platform\Macos;
/** /**
* Clang 编译器后端实现 * Clang compiler backend implementation.
*/ */
class Clang extends GccLikeBackend class Clang extends GccLikeBackend
{ {
@ -34,7 +34,7 @@ class Clang extends GccLikeBackend
} }
/** /**
* Windows 下优先使用 lld-link,找不到时回退到 link.exe * On Windows, prefer lld-link; fall back to link.exe if it is not available.
*/ */
public static function detectWindowsLinker(): string public static function detectWindowsLinker(): string
{ {
@ -62,7 +62,7 @@ class Clang extends GccLikeBackend
return 'link'; return 'link';
} }
// ──── 钩子方法覆盖 ──── // ──── Hook method overrides ────
protected function getCompilerPrefixFlags(): string protected function getCompilerPrefixFlags(): string
{ {

@ -5,18 +5,18 @@ namespace TypePhp\Backend;
use TypePhp\Platform\PlatformBase; use TypePhp\Platform\PlatformBase;
/** /**
* 编译器后端抽象基类 * Abstract base class for compiler backends.
* 定义所有编译器必须实现的接口 * Defines the interface that all compilers must implement.
*/ */
abstract class CompilerBackend abstract class CompilerBackend
{ {
/** /**
* 平台实例 * The platform instance.
*/ */
protected PlatformBase $platform; protected PlatformBase $platform;
/** /**
* 最近创建的 Response File 路径,用于构建完成后清理 * Path of the most recently created Response File, used for cleanup after the build completes.
*/ */
protected string $lastResponseFile = ''; protected string $lastResponseFile = '';
@ -26,17 +26,17 @@ abstract class CompilerBackend
} }
/** /**
* 获取编译器名称 * Get the compiler name.
*/ */
abstract public function getName(): string; abstract public function getName(): string;
/** /**
* 获取编译器命令 * Get the compiler command.
*/ */
abstract public function getCompilerCommand(): string; abstract public function getCompilerCommand(): string;
/** /**
* 获取链接器命令 * Get the linker command.
*/ */
abstract public function getLinkerCommand(): string; abstract public function getLinkerCommand(): string;
@ -51,7 +51,7 @@ abstract class CompilerBackend
} }
/** /**
* 构建完整的编译命令 * Build the complete compile command.
*/ */
abstract public function buildCompileCommand( abstract public function buildCompileCommand(
string $sourceFile, string $sourceFile,
@ -60,7 +60,7 @@ abstract class CompilerBackend
): string; ): string;
/** /**
* 构建 C 文件的编译命令(不包含 C++ 特定选项) * Build the compile command for C files (excludes C++-specific options).
*/ */
abstract public function buildCCompileCommand( abstract public function buildCCompileCommand(
string $sourceFile, string $sourceFile,
@ -69,9 +69,9 @@ abstract class CompilerBackend
): string; ): string;
/** /**
* 构建原生源文件的编译命令(汇编/Objective-C 等,使用 -x 指定语言) * Build the compile command for native source files (assembly/Objective-C, etc., using -x to specify the language).
* *
* @param string $language GCC/Clang 语言标识(assembler, objective-c, objective-c++ 等) * @param string $language GCC/Clang language identifier (assembler, objective-c, objective-c++, etc.)
*/ */
abstract public function buildNativeCompileCommand( abstract public function buildNativeCompileCommand(
string $sourceFile, string $sourceFile,
@ -81,7 +81,7 @@ abstract class CompilerBackend
): string; ): string;
/** /**
* 构建完整的链接命令 * Build the complete link command.
*/ */
abstract public function buildLinkCommand( abstract public function buildLinkCommand(
array $objectFiles, array $objectFiles,
@ -90,33 +90,35 @@ abstract class CompilerBackend
): string; ): string;
/** /**
* 构建编译选项(不含文件路径) * Build compile options (excludes file paths).
* @param array $config 编译配置 *
* - optimize: 优化级别 (0-3) * @param array $config Compile configuration
* - debug_info: 是否生成调试信息 * - optimize: optimization level (0-3)
* - sanitize: sanitizer 类型 (address, undefined, etc.) * - debug_info: whether to generate debug information
* - cpp_std: C++ 标准版本 * - sanitize: sanitizer type (address, undefined, etc.)
* - is_zts: 是否为 ZTS 模式 * - cpp_std: C++ standard version
* - build_mode: 构建模式 ('bin' or 'ext') * - is_zts: whether ZTS mode is enabled
* - enable_profiler: 是否启用性能分析 * - build_mode: build mode ('bin' or 'ext')
* - suppressed_warnings: 需要屏蔽的警告代码数组 * - enable_profiler: whether to enable profiling
* - cxxflags: 用户自定义编译标志 * - suppressed_warnings: array of warning codes to suppress
* - compiler_pdb: MSVC 编译器 PDB 输出路径 * - cxxflags: user-defined compile flags
* - compiler_pdb: MSVC compiler PDB output path
*/ */
abstract public function buildCompileOptions(array $config = []): string; abstract public function buildCompileOptions(array $config = []): string;
/** /**
* 构建链接选项(不含文件路径) * Build link options (excludes file paths).
* @param array $config 链接配置 *
* - debug_info: 是否生成调试信息 * @param array $config Link configuration
* - no_console: 是否隐藏控制台窗口 * - debug_info: whether to generate debug information
* - build_mode: 构建模式 ('bin' or 'ext') * - no_console: whether to hide the console window
* - sanitize: sanitizer 类型 * - build_mode: build mode ('bin' or 'ext')
* - sanitize: sanitizer type
*/ */
abstract public function buildLinkOptions(array $config = []): string; abstract public function buildLinkOptions(array $config = []): string;
/** /**
* 获取平台实例 * Get the platform instance.
*/ */
public function getPlatform(): PlatformBase public function getPlatform(): PlatformBase
{ {
@ -124,7 +126,7 @@ abstract class CompilerBackend
} }
/** /**
* 格式化包含路径 * Format include paths.
*/ */
protected function formatIncludePaths(array $includePaths): string protected function formatIncludePaths(array $includePaths): string
{ {
@ -132,7 +134,7 @@ abstract class CompilerBackend
} }
/** /**
* 格式化库路径 * Format library paths.
*/ */
protected function formatLibraryPaths(array $libraryPaths): string protected function formatLibraryPaths(array $libraryPaths): string
{ {
@ -140,7 +142,7 @@ abstract class CompilerBackend
} }
/** /**
* 格式化库文件 * Format library files.
*/ */
protected function formatLibraries(array $libraries): string protected function formatLibraries(array $libraries): string
{ {
@ -157,11 +159,11 @@ abstract class CompilerBackend
} }
/** /**
* 将目标文件列表写入 Response File,避免命令行参数过长超出 OS 限制(Windows 8191 字符) * Write the object file list to a Response File to avoid exceeding the OS command-line length limit (8191 characters on Windows).
* *
* @param array $objectFiles 目标文件路径列表 * @param array $objectFiles List of object file paths.
* @param string $targetFile 最终输出文件路径(Response File 写入同目录) * @param string $targetFile Final output file path (the Response File is written to the same directory).
* @return string 链接器参数,如 @build/project.rsp * @return string Linker argument, e.g. @build/project.rsp
*/ */
protected function createResponseFile(array $objectFiles, string $targetFile): string protected function createResponseFile(array $objectFiles, string $targetFile): string
{ {
@ -169,7 +171,7 @@ abstract class CompilerBackend
$this->lastResponseFile = $rspFile; $this->lastResponseFile = $rspFile;
$lines = []; $lines = [];
foreach ($objectFiles as $file) { foreach ($objectFiles as $file) {
// 路径含空格时用双引号包裹,MSVC link.exe 和 GCC/Clang 均支持 // Wrap paths containing spaces in double quotes; supported by both MSVC link.exe and GCC/Clang.
if (str_contains($file, ' ')) { if (str_contains($file, ' ')) {
$file = '"' . $file . '"'; $file = '"' . $file . '"';
} }
@ -180,7 +182,7 @@ abstract class CompilerBackend
} }
/** /**
* 删除最近创建的 Response File 临时文件 * Delete the most recently created Response File temporary file.
*/ */
public function cleanupResponseFile(): void public function cleanupResponseFile(): void
{ {

@ -8,24 +8,24 @@ use TypePhp\Platform\Linux;
use TypePhp\Platform\Macos; use TypePhp\Platform\Macos;
/** /**
* 编译器工厂类 * Compiler factory.
* 根据平台自动创建合适的编译器后端 * Automatically creates the appropriate compiler backend based on the platform.
*/ */
class CompilerFactory class CompilerFactory
{ {
/** /**
* 创建默认编译器后端 * Create the default compiler backend.
*/ */
public static function create(PlatformBase $platform): CompilerBackend public static function create(PlatformBase $platform): CompilerBackend
{ {
if ($platform instanceof Windows) { if ($platform instanceof Windows) {
// Windows 默认使用 MSVC // Windows uses MSVC by default.
return new Msvc($platform, $platform->getDefaultCompiler()); return new Msvc($platform, $platform->getDefaultCompiler());
} elseif ($platform instanceof Linux) { } elseif ($platform instanceof Linux) {
// Linux 默认使用 GCC // Linux uses GCC by default.
return new Gcc($platform, $platform->getDefaultCompiler()); return new Gcc($platform, $platform->getDefaultCompiler());
} elseif ($platform instanceof Macos) { } elseif ($platform instanceof Macos) {
// macOS 默认使用 Clang // macOS uses Clang by default.
return new Clang($platform, $platform->getDefaultCompiler()); return new Clang($platform, $platform->getDefaultCompiler());
} else { } else {
throw new \RuntimeException("Unsupported platform: " . $platform->getName()); throw new \RuntimeException("Unsupported platform: " . $platform->getName());
@ -33,7 +33,7 @@ class CompilerFactory
} }
/** /**
* 根据配置、环境变量和平台默认值解析编译器命令 * Resolve the compiler command based on configuration, environment variables, and platform defaults.
*/ */
public static function detectCompilerName(PlatformBase $platform, string $configuredCompiler = ''): string public static function detectCompilerName(PlatformBase $platform, string $configuredCompiler = ''): string
{ {
@ -57,7 +57,7 @@ class CompilerFactory
} }
/** /**
* 创建指定类型的编译器后端 * Create a compiler backend of the specified type.
*/ */
public static function createByName(string $compilerName, PlatformBase $platform): CompilerBackend public static function createByName(string $compilerName, PlatformBase $platform): CompilerBackend
{ {
@ -93,17 +93,17 @@ class CompilerFactory
} }
/** /**
* 自动检测并创建编译器和平台 * Auto-detect and create the compiler and platform.
*/ */
public static function autoDetect(string $compilerName = '', ?PlatformBase $platform = null): array public static function autoDetect(string $compilerName = '', ?PlatformBase $platform = null): array
{ {
// 创建平台 // Create the platform.
$platform ??= \TypePhp\Platform\PlatformFactory::create(); $platform ??= \TypePhp\Platform\PlatformFactory::create();
// 创建编译器 // Create the compiler.
$compilerName = self::detectCompilerName($platform, $compilerName); $compilerName = self::detectCompilerName($platform, $compilerName);
$compiler = self::createByName($compilerName, $platform); $compiler = self::createByName($compilerName, $platform);
return [ return [
'platform' => $platform, 'platform' => $platform,
'compiler' => $compiler, 'compiler' => $compiler,

@ -5,7 +5,7 @@ namespace TypePhp\Backend;
use TypePhp\Platform\PlatformBase; use TypePhp\Platform\PlatformBase;
/** /**
* GCC 编译器后端实现 * GCC compiler backend implementation.
*/ */
class Gcc extends GccLikeBackend class Gcc extends GccLikeBackend
{ {

@ -6,9 +6,9 @@ use TypePhp\Platform\PlatformBase;
use TypePhp\Platform\Windows; use TypePhp\Platform\Windows;
/** /**
* GCC/Clang 共享后端基类 * Shared backend base class for GCC/Clang.
* 包含 Unix-like 编译器(GCC、Clang)的通用命令行构建逻辑。 * Contains the common command-line construction logic for Unix-like compilers (GCC, Clang).
* 子类只需覆盖平台差异的钩子方法。 * Subclasses only need to override the platform-specific hook methods.
*/ */
abstract class GccLikeBackend extends CompilerBackend abstract class GccLikeBackend extends CompilerBackend
{ {
@ -37,21 +37,21 @@ abstract class GccLikeBackend extends CompilerBackend
return $headerFile . '.gch'; return $headerFile . '.gch';
} }
// ──── 钩子方法(子类覆盖点) ──── // ──── Hook methods (subclass override points) ────
/** 编译器特定的前缀标志(如 MSVC 兼容模式) */ /** Compiler-specific prefix flags (e.g. MSVC compatibility mode). */
protected function getCompilerPrefixFlags(): string protected function getCompilerPrefixFlags(): string
{ {
return ''; return '';
} }
/** 链接器输出标志(-o vs /OUT:) */ /** Linker output flag (-o vs /OUT:). */
protected function getLinkerOutputFlag(): string protected function getLinkerOutputFlag(): string
{ {
return '-o'; return '-o';
} }
/** 格式化 sanitizer 标志 */ /** Format the sanitizer flag. */
protected function formatSanitizerFlag(string $sanitizer): string protected function formatSanitizerFlag(string $sanitizer): string
{ {
return match ($sanitizer) { return match ($sanitizer) {
@ -61,7 +61,7 @@ abstract class GccLikeBackend extends CompilerBackend
}; };
} }
/** 获取 PIC 标志 */ /** Get the PIC flag. */
protected function getPICFlag(array $config): string protected function getPICFlag(array $config): string
{ {
if ((!empty($config['build_mode']) && ($config['build_mode'] === 'ext' || $config['build_mode'] === 'lib')) || !empty($config['pic'])) { if ((!empty($config['build_mode']) && ($config['build_mode'] === 'ext' || $config['build_mode'] === 'lib')) || !empty($config['pic'])) {
@ -70,7 +70,7 @@ abstract class GccLikeBackend extends CompilerBackend
return ''; return '';
} }
/** 构建 GCC/Clang 共享编译选项,C 和 C++ 编译路径都复用这里 */ /** Build the shared GCC/Clang compile flags; reused by both the C and C++ compilation paths. */
protected function buildSharedCompileFlags(array $config, bool $includeCppStd = false): string protected function buildSharedCompileFlags(array $config, bool $includeCppStd = false): string
{ {
$cmd = ''; $cmd = '';
@ -146,7 +146,7 @@ abstract class GccLikeBackend extends CompilerBackend
return ' -include ' . escapeshellarg($precompiledHeader['header']); return ' -include ' . escapeshellarg($precompiledHeader['header']);
} }
/** 获取平台特定的链接选项 */ /** Get platform-specific link options. */
protected function getPlatformLinkFlags(array $config): string protected function getPlatformLinkFlags(array $config): string
{ {
$flags = ''; $flags = '';
@ -181,7 +181,7 @@ abstract class GccLikeBackend extends CompilerBackend
return $flags; return $flags;
} }
// ──── 抽象方法实现 ──── // ──── Abstract method implementations ────
public function buildCompileCommand(string $sourceFile, string $outputFile, array $options = []): string public function buildCompileCommand(string $sourceFile, string $outputFile, array $options = []): string
{ {

@ -5,7 +5,7 @@ namespace TypePhp\Backend;
use TypePhp\Platform\Windows; use TypePhp\Platform\Windows;
/** /**
* MSVC 编译器后端实现 * MSVC compiler backend implementation.
*/ */
class Msvc extends CompilerBackend class Msvc extends CompilerBackend
{ {
@ -131,7 +131,7 @@ class Msvc extends CompilerBackend
} }
/** /**
* 构建 C 文件的编译命令(不包含 C++ 特定选项) * Build the compile command for C files (excludes C++-specific options).
*/ */
public function buildCCompileCommand(string $sourceFile, string $outputFile, array $options = []): string public function buildCCompileCommand(string $sourceFile, string $outputFile, array $options = []): string
{ {
@ -145,20 +145,20 @@ class Msvc extends CompilerBackend
$cmd .= ' ' . $this->formatIncludePaths($options['include_paths']); $cmd .= ' ' . $this->formatIncludePaths($options['include_paths']);
} }
// 平台宏定义 // Platform macro definitions.
$cmd .= $this->buildCommonCompileFlags($options, false); $cmd .= $this->buildCommonCompileFlags($options, false);
// 注意:C 文件不使用 /EHsc, /std:c++17, /MD 等 C++ 特定选项 // Note: C files do not use C++-specific options such as /EHsc, /std:c++17, /MD.
return $cmd; return $cmd;
} }
/** /**
* 构建原生源文件的编译命令 * Build the compile command for native source files.
* *
* MSVC 仅支持 C 文件(/TC),汇编和 ObjC 文件不受支持 * MSVC only supports C files (/TC); assembly and ObjC files are not supported.
* *
* @param string $language 语言标识 * @param string $language Language identifier.
*/ */
public function buildNativeCompileCommand(string $sourceFile, string $outputFile, array $options = [], string $language = ''): string public function buildNativeCompileCommand(string $sourceFile, string $outputFile, array $options = [], string $language = ''): string
{ {
@ -214,42 +214,42 @@ class Msvc extends CompilerBackend
} }
/** /**
* 构建编译选项(实现抽象方法) * Build compile options (implements the abstract method).
*/ */
public function buildCompileOptions(array $config = []): string public function buildCompileOptions(array $config = []): string
{ {
return $this->buildCommonCompileFlags($config, true); return $this->buildCommonCompileFlags($config, true);
} }
/** /**
* 构建链接选项(实现抽象方法) * Build link options (implements the abstract method).
*/ */
public function buildLinkOptions(array $config = []): string public function buildLinkOptions(array $config = []): string
{ {
$cmd = ''; $cmd = '';
// 调试 // Debug.
if (!empty($config['debug'])) { if (!empty($config['debug'])) {
$cmd .= ' /DEBUG'; $cmd .= ' /DEBUG';
} }
// Windows 子系统 // Windows subsystem.
if (!empty($config['no_console'])) { if (!empty($config['no_console'])) {
$cmd .= ' ' . $this->platform->getSubsystemOptions(true); $cmd .= ' ' . $this->platform->getSubsystemOptions(true);
} }
// CRT 配置 // CRT configuration.
$cmd .= ' ' . $this->platform->getCrtConfig(); $cmd .= ' ' . $this->platform->getCrtConfig();
// 扩展模块选项 // Extension module options.
if (!empty($config['build_mode']) && ($config['build_mode'] === 'ext' || $config['build_mode'] === 'lib')) { if (!empty($config['build_mode']) && ($config['build_mode'] === 'ext' || $config['build_mode'] === 'lib')) {
$cmd .= ' /DLL'; $cmd .= ' /DLL';
} }
// nologo // nologo.
$cmd .= ' /nologo'; $cmd .= ' /nologo';
// LTO(链接时代码生成) // LTO (Link Time Code Generation).
if (!empty($config['lto'])) { if (!empty($config['lto'])) {
$cmd .= ' /LTCG'; $cmd .= ' /LTCG';
} }
@ -258,20 +258,20 @@ class Msvc extends CompilerBackend
} }
/** /**
* 编译 Windows 资源文件 (.rc) 为目标文件 (.res) * Compile a Windows resource file (.rc) into an object file (.res).
* *
* 使用 rc.exe(MSVC 资源编译器)将 .rc 文件编译为 .res 文件 * Uses rc.exe (the MSVC resource compiler) to compile a .rc file into a .res file.
* .res 文件可以直接传给 link.exe 作为输入 * The .res file can be passed directly to link.exe as input.
* *
* @param string $rcFile 资源文件路径 (.rc) * @param string $rcFile Resource file path (.rc).
* @param string $resFile 输出资源文件路径 (.res) * @param string $resFile Output resource file path (.res).
* @return string 编译命令 * @return string The compile command.
*/ */
public function compileResourceFile(string $rcFile, string $resFile): string public function compileResourceFile(string $rcFile, string $resFile): string
{ {
// rc.exe 是 MSVC 自带的资源编译器 // rc.exe is the resource compiler bundled with MSVC.
// /nologo: 不显示版权信息 // /nologo: suppress the copyright banner.
// /fo: 指定输出文件 // /fo: specify the output file.
$cmd = 'rc.exe /nologo'; $cmd = 'rc.exe /nologo';
$cmd .= ' /fo ' . escapeshellarg($resFile); $cmd .= ' /fo ' . escapeshellarg($resFile);
$cmd .= ' ' . escapeshellarg($rcFile); $cmd .= ' ' . escapeshellarg($rcFile);

@ -20,7 +20,7 @@ trait NativeBuildConfigurationTrait
$this->getPhpxDir() . '/src/misc', $this->getPhpxDir() . '/src/misc',
]; ];
// 根据平台添加 PHP 包含路径 // Add the platform-specific PHP include paths
if ($platform instanceof Windows) { if ($platform instanceof Windows) {
$phpSdkPaths = $platform->buildPhpSdkIncludePaths($this->getPhpDir()); $phpSdkPaths = $platform->buildPhpSdkIncludePaths($this->getPhpDir());
$includePaths = array_merge($includePaths, $phpSdkPaths); $includePaths = array_merge($includePaths, $phpSdkPaths);
@ -28,7 +28,7 @@ trait NativeBuildConfigurationTrait
// Linux/macOS // Linux/macOS
$phpPaths = $platform->buildPhpIncludePaths($this->getPhpDir()); $phpPaths = $platform->buildPhpIncludePaths($this->getPhpDir());
$includePaths = array_merge($includePaths, $phpPaths); $includePaths = array_merge($includePaths, $phpPaths);
// 内置 mpdecimal 头文件目录 // Bundled mpdecimal header directories
$includePaths[] = $this->getPhpxDir() . '/thirdparty/mpdecimal/libmpdec'; $includePaths[] = $this->getPhpxDir() . '/thirdparty/mpdecimal/libmpdec';
$includePaths[] = $this->getPhpxDir() . '/thirdparty/mpdecimal/libmpdec++'; $includePaths[] = $this->getPhpxDir() . '/thirdparty/mpdecimal/libmpdec++';
} }
@ -43,7 +43,7 @@ trait NativeBuildConfigurationTrait
$this->getPhpxDir() . '/lib', $this->getPhpxDir() . '/lib',
]; ];
// 根据平台添加 PHP 库路径 // Add the platform-specific PHP library paths
if ($platform instanceof Windows) { if ($platform instanceof Windows) {
$phpLibPaths = $platform->buildPhpSdkLibPaths($this->getPhpDir()); $phpLibPaths = $platform->buildPhpSdkLibPaths($this->getPhpDir());
$libraryPaths = array_merge($libraryPaths, $phpLibPaths); $libraryPaths = array_merge($libraryPaths, $phpLibPaths);
@ -57,45 +57,45 @@ trait NativeBuildConfigurationTrait
} }
/** /**
* 获取库文件 * Get the library files to link against
*/ */
protected function getLibraries(): array protected function getLibraries(): array
{ {
$platform = $this->getPlatform(); $platform = $this->getPlatform();
$libraries = []; $libraries = [];
// phpx 库(根据平台使用不同的文件名格式) // phpx library (file name format differs by platform)
$phpxLibPath = $this->findPhpxLibrary(); $phpxLibPath = $this->findPhpxLibrary();
if ($phpxLibPath === null) { if ($phpxLibPath === null) {
$this->error($this->getPhpxLibraryErrorMessage()); $this->error($this->getPhpxLibraryErrorMessage());
} }
$libraries[] = $phpxLibPath; $libraries[] = $phpxLibPath;
// extension 和 bin 模式都需要链接 PHP 库 // Both extension and bin modes need to link the PHP library
if ($platform instanceof Windows) { if ($platform instanceof Windows) {
// Windows: 根据构建模式选择不同的库 // Windows: pick different libraries based on the build mode
if ($this->isBuildModeEmbed()) { if ($this->isBuildModeEmbed()) {
// bin 模式:需要同时链接 php8ts.lib 和 php8embed.lib // bin mode: link both php8ts.lib and php8embed.lib
// 注意:php8ts.lib 必须在 php8embed.lib 之前,因为 embed 依赖 core // Note: php8ts.lib must come before php8embed.lib because embed depends on core
// php8ts.lib 提供 PHP 核心全局符号(executor_globals, compiler_globals, sapi_globals) // php8ts.lib provides the PHP core global symbols (executor_globals, compiler_globals, sapi_globals)
if (!empty($this->windowsPhpCoreLib)) { if (!empty($this->windowsPhpCoreLib)) {
$libraries[] = $this->windowsPhpCoreLib; // 不添加引号 $libraries[] = $this->windowsPhpCoreLib; // do not quote
} }
// php8embed.lib 提供嵌入 API // php8embed.lib provides the embed API
if (!empty($this->windowsPhpEmbedLib)) { if (!empty($this->windowsPhpEmbedLib)) {
$libraries[] = $this->windowsPhpEmbedLib; // 不添加引号 $libraries[] = $this->windowsPhpEmbedLib; // do not quote
} }
} else { } else {
// ext 模式:只使用 php8ts.lib 或 php8.lib(PHP 扩展) // ext mode: use only php8ts.lib or php8.lib (PHP extension)
if (!empty($this->windowsPhpCoreLib)) { if (!empty($this->windowsPhpCoreLib)) {
$libraries[] = $this->windowsPhpCoreLib; // 不添加引号 $libraries[] = $this->windowsPhpCoreLib; // do not quote
} }
} }
// 添加 Windows API 库(Win32 GUI 程序需要) // Add the Windows API libraries (required by Win32 GUI programs)
$libraries[] = 'user32.lib'; // Windows UI 函数(CreateWindow, MessageBox 等) $libraries[] = 'user32.lib'; // Windows UI functions (CreateWindow, MessageBox, etc.)
$libraries[] = 'gdi32.lib'; // GDI 图形函数 $libraries[] = 'gdi32.lib'; // GDI graphics functions
$libraries[] = 'kernel32.lib'; // 核心 Windows API $libraries[] = 'kernel32.lib'; // Core Windows API
$libraries[] = 'gmp.lib'; $libraries[] = 'gmp.lib';
$libraries[] = 'gmpxx.lib'; $libraries[] = 'gmpxx.lib';
$libraries[] = 'mpfr.lib'; $libraries[] = 'mpfr.lib';
@ -117,10 +117,12 @@ trait NativeBuildConfigurationTrait
} }
/** /**
* 解析 phpx 库文件路径,库不存在时返回 null。 * Resolve the phpx library file path, returning null when the library does
* not exist.
* *
* Windows 使用 phpx.lib(无 lib 前缀);其他平台优先使用共享库 * Windows uses phpx.lib (no lib prefix); other platforms prefer the shared
* (libphpx.so / libphpx.dylib),找不到时回退到静态库 libphpx.a。 * library (libphpx.so / libphpx.dylib) and fall back to the static library
* libphpx.a when it is not found.
*/ */
protected function findPhpxLibrary(): ?string protected function findPhpxLibrary(): ?string
{ {
@ -131,8 +133,8 @@ trait NativeBuildConfigurationTrait
return is_file($phpxLibPath) ? $phpxLibPath : null; return is_file($phpxLibPath) ? $phpxLibPath : null;
} }
// Linux/macOS:共享库优先,静态库兜底 // Linux/macOS: prefer the shared library, fall back to the static library
// getSharedLibraryExtension() 返回的值可能带点或不带点,需要统一处理 // getSharedLibraryExtension() may or may not include a leading dot, so normalize it
$sharedLibExt = ltrim($platform->getSharedLibraryExtension(), '.'); $sharedLibExt = ltrim($platform->getSharedLibraryExtension(), '.');
$phpxLibPath = $this->getPhpxDir() . '/lib/libphpx.' . $sharedLibExt; $phpxLibPath = $this->getPhpxDir() . '/lib/libphpx.' . $sharedLibExt;
if (is_file($phpxLibPath)) { if (is_file($phpxLibPath)) {
@ -152,7 +154,7 @@ trait NativeBuildConfigurationTrait
} }
/** /**
* 生成 phpx 库缺失时的错误信息 * Generate the error message shown when the phpx library is missing
*/ */
protected function getPhpxLibraryErrorMessage(): string protected function getPhpxLibraryErrorMessage(): string
{ {
@ -175,8 +177,9 @@ trait NativeBuildConfigurationTrait
} }
/** /**
* 前置检测 phpx 库是否可用,在编译开始前报错, * Verify the phpx library is available up front and fail before compilation
* 避免所有源文件编译完成后才在链接阶段失败。 * starts, rather than only failing at link time after all source files have
* been compiled.
*/ */
protected function validatePhpxLibrary(): void protected function validatePhpxLibrary(): void
{ {

@ -34,7 +34,7 @@ trait SourcePipelineTrait
$path = $realpath; $path = $realpath;
if (is_dir($path)) { if (is_dir($path)) {
// 目录模式:不解析 YAML // Directory mode: no YAML parsing
$list = $this->getFilesFromDir($path); $list = $this->getFilesFromDir($path);
$targetName = basename($path); $targetName = basename($path);
$this->setTargetName($targetName); $this->setTargetName($targetName);
@ -42,10 +42,10 @@ trait SourcePipelineTrait
} else { } else {
$ext = pathinfo($path, PATHINFO_EXTENSION); $ext = pathinfo($path, PATHINFO_EXTENSION);
if ($ext === 'yml' || $ext === 'yaml') { if ($ext === 'yml' || $ext === 'yaml') {
// YAML 配置模式:先解析 YAML // YAML config mode: parse the YAML first
$list = $this->parseProjectYaml($path); $list = $this->parseProjectYaml($path);
} elseif ($ext === 'php') { } elseif ($ext === 'php') {
// 单文件模式:不解析 YAML // Single-file mode: no YAML parsing
$list = [$path]; $list = [$path];
$targetName = FileScanner::getFileName($path); $targetName = FileScanner::getFileName($path);
$this->setTargetName($targetName); $this->setTargetName($targetName);
@ -55,7 +55,8 @@ trait SourcePipelineTrait
} }
} }
// 在所有配置加载完成后,应用命令行参数(确保优先级最高) // Apply command-line arguments after all configuration is loaded (so they
// take the highest precedence)
$this->applyCommandLineArguments(); $this->applyCommandLineArguments();
// The generated public import stub is an output artifact, not an input // The generated public import stub is an output artifact, not an input
@ -100,19 +101,22 @@ trait SourcePipelineTrait
} }
} }
// 仅在 PHP 脚本入口(bin/tpc.php)前置检测 phpx 库:缺少库立即 fatal, // Pre-check the phpx library only at the PHP script entry (bin/tpc.php):
// 避免继续向下执行到文件处理/编译阶段才报错。已编译的 tpc 可执行文件 // a missing library fails immediately rather than surfacing later during
// 在进入 main() 前就由动态链接器加载 libphpx,无需(也无法)在此检测。 // file processing/compilation. The compiled tpc executable has libphpx
// loaded by the dynamic linker before entering main(), so checking here
// is neither needed nor possible.
if (defined('TYPEPHP_PHP_SCRIPT_ENTRY') && !($this->getPlatform() instanceof Wasi)) { if (defined('TYPEPHP_PHP_SCRIPT_ENTRY') && !($this->getPlatform() instanceof Wasi)) {
$this->validatePhpxLibrary(); $this->validatePhpxLibrary();
} }
$this->validateCompilerToolchain(); $this->validateCompilerToolchain();
// shell_exec 和 define 已通过 php::fn:: 直接调用,无需动态符号表 // shell_exec and define are already called directly via php::fn::, so no
// dynamic symbol table is needed
// Windows 的所有构建模式都依赖 PHPX 导入库和运行库。 // All Windows build modes depend on the PHPX import library and runtime.
// 其他平台仅在嵌入式构建模式下执行现有检查。 // Other platforms only run the existing checks in embedded build mode.
if ($this->isBuildModeEmbed() || $this->getPlatform() instanceof Windows) { if ($this->isBuildModeEmbed() || $this->getPlatform() instanceof Windows) {
foreach ($this->getPlatform()->getBuildLibraryWarnings( foreach ($this->getPlatform()->getBuildLibraryWarnings(
$this->getPhpDir(), $this->getPhpDir(),
@ -136,7 +140,7 @@ trait SourcePipelineTrait
$files = $this->filterIgnoredFiles($files); $files = $this->filterIgnoredFiles($files);
$this->discoverNativeClassDeclarations($files); $this->discoverNativeClassDeclarations($files);
// 分析 PHP 文件,预处理 // Analyze and preprocess the PHP files
foreach ($files as $k => $file) { foreach ($files as $k => $file) {
if (FileScanner::isPhpFile($file)) { if (FileScanner::isPhpFile($file)) {
try { try {
@ -258,7 +262,7 @@ trait SourcePipelineTrait
$sourceFiles = []; $sourceFiles = [];
$validSourceCount = 0; $validSourceCount = 0;
// 生成 C++ 文件 // Generate the C++ files
foreach ($files as $k => $file) { foreach ($files as $k => $file) {
try { try {
if (FileScanner::isPhpFile($file)) { if (FileScanner::isPhpFile($file)) {
@ -293,10 +297,11 @@ trait SourcePipelineTrait
$this->genLibraryImportStub($files); $this->genLibraryImportStub($files);
} }
// 生成构建期内部头文件:函数声明、运行时数据声明 // Generate the build-time internal headers: function declarations and
// runtime data declarations
$this->genFunctionDeclarations($this->getIncludeDir() . "/php_{$this->targetName}_func_decl.h"); $this->genFunctionDeclarations($this->getIncludeDir() . "/php_{$this->targetName}_func_decl.h");
$this->genDataDeclarations($this->getIncludeDir() . "/php_{$this->targetName}_data_decl.h"); $this->genDataDeclarations($this->getIncludeDir() . "/php_{$this->targetName}_data_decl.h");
// 生成扩展模块源文件 // Generate the extension module source file
$sourceFiles[] = $this->genExtension(); $sourceFiles[] = $this->genExtension();
return $sourceFiles; return $sourceFiles;

@ -288,12 +288,14 @@ class CompilerBase implements PropertyAccessContext
protected int $classIndex = 0; protected int $classIndex = 0;
/** /**
* 用户定义(请求生命周期)类名 → ID,运行期为 THREAD_LOCAL 缓存,RSHUTDOWN 清理 * User-defined (request-lifetime) class name → ID. Backed by a THREAD_LOCAL
* cache at runtime and cleared on RSHUTDOWN.
* @var array<string, int> * @var array<string, int>
*/ */
protected array $classMap = []; protected array $classMap = [];
/** /**
* 内置/编译产物(模块生命周期)类名 → ID,PHP 启动完成后惰性填充,RSHUTDOWN 不清理 * Built-in / compiled-output (module-lifetime) class name → ID. Lazily
* populated after PHP startup and NOT cleared on RSHUTDOWN.
* @var array<string, int> * @var array<string, int>
*/ */
protected array $persistentClassMap = []; protected array $persistentClassMap = [];
@ -305,21 +307,26 @@ class CompilerBase implements PropertyAccessContext
protected int $funcIndex = 0; protected int $funcIndex = 0;
/** /**
* 用户定义(请求生命周期)函数/方法 → ID,运行期为 THREAD_LOCAL 缓存,RSHUTDOWN 清理 * User-defined (request-lifetime) function/method → ID. Backed by a
* key 为函数名或 `Class::method` * THREAD_LOCAL cache at runtime and cleared on RSHUTDOWN.
* Key is a function name or `Class::method`.
* @var array<string, int> * @var array<string, int>
*/ */
protected array $funcMap = []; protected array $funcMap = [];
/** /**
* 内置/编译产物(模块生命周期)函数/方法 → ID,PHP 启动完成后惰性填充,RSHUTDOWN 不清理 * Built-in / compiled-output (module-lifetime) function/method → ID. Lazily
* key 为函数名或 `Class::method` * populated after PHP startup and NOT cleared on RSHUTDOWN.
* Key is a function name or `Class::method`.
* @var array<string, int> * @var array<string, int>
*/ */
protected array $persistentFuncMap = []; protected array $persistentFuncMap = [];
protected int $persistentFuncIndex = 0; protected int $persistentFuncIndex = 0;
/** /**
* 内置/编译产物类的声明属性 offset 缓存,key 为 `Class::prop`,惰性填充,RSHUTDOWN 不清理。 * Declared-property offset cache for built-in / compiled-output classes.
* 属性解析仅覆盖编译类与内置类的声明属性(进程级稳定),用户类属性走字符串路径,不进缓存。 * Key is `Class::prop`; lazily populated and NOT cleared on RSHUTDOWN.
* Property resolution only covers declared properties of compiled classes
* and built-in classes (process-stable). User-class properties go through
* the string path and never enter this cache.
*/ */
protected array $persistentPropMap = []; protected array $persistentPropMap = [];
protected int $persistentPropIndex = 0; protected int $persistentPropIndex = 0;
@ -345,10 +352,11 @@ class CompilerBase implements PropertyAccessContext
'mixed' => Type::VAR, 'mixed' => Type::VAR,
'null' => Type::VAR, 'null' => Type::VAR,
'any' => Type::VAR, 'any' => Type::VAR,
// callable 类型,可以是字符串、数组、对象 // The callable type can be a string, array, or object:
// 1) 'foo' 函数名称字符串, 2) [ $obj, 'bar' ] 对象方法数组, 3) Closure 对象, 4) [ 'class', 'staticMethod'] 类名+静态方法数组 // 1) 'foo' function-name string, 2) [ $obj, 'bar' ] object-method array,
// 3) a Closure object, 4) [ 'class', 'staticMethod' ] class + static-method array.
'callable' => Type::VAR, 'callable' => Type::VAR,
// iterable 类型,可以是数组或者对象 // The iterable type can be an array or an object.
'iterable' => Type::VAR, 'iterable' => Type::VAR,
'stream' => Type::STREAM, 'stream' => Type::STREAM,
'bigint' => Type::BIGINT, 'bigint' => Type::BIGINT,
@ -361,13 +369,15 @@ class CompilerBase implements PropertyAccessContext
protected array $internalConstants = []; protected array $internalConstants = [];
/** /**
* 存储所有函数、类方法的声明,key 是 符号名称,Value 是函数、类方法所在的文件名称 * Stores the declaration of every function and class method. Key is the
* symbol name; value is the file in which the function or method is declared.
* @var array<string, string> * @var array<string, string>
*/ */
protected array $symbolDeclInFile = []; protected array $symbolDeclInFile = [];
/** /**
* 存储所有函数、类方法的调用,key 是 文件名称,Value 是函数、类方法调用的列表数组 * Stores every function / class-method call. Key is the file name; value is
* a list of the functions / class methods called within that file.
* @var array<string, array<string>> * @var array<string, array<string>>
*/ */
protected array $symbolCallInFile = []; protected array $symbolCallInFile = [];
@ -400,7 +410,7 @@ class CompilerBase implements PropertyAccessContext
protected string $dir; protected string $dir;
/** /**
* 原始值,可能包含 `\\` 多层空间. * The raw namespace value, which may contain `\\` multi-level separators.
*/ */
protected string $namespace = ''; protected string $namespace = '';
protected string $method = ''; protected string $method = '';
@ -413,7 +423,7 @@ class CompilerBase implements PropertyAccessContext
protected array $useImportAliases = []; protected array $useImportAliases = [];
/** /**
* 原始类名,不包含命名空间. * The raw class name, without the namespace.
*/ */
protected string $class = ''; protected string $class = '';
protected string $parentClass = ''; protected string $parentClass = '';
@ -469,7 +479,7 @@ class CompilerBase implements PropertyAccessContext
protected bool $bigintTypes = false; protected bool $bigintTypes = false;
protected string $rootPath; protected string $rootPath;
protected string $buildDir; protected string $buildDir;
protected string $outputDir = ''; // -o 参数指定的输出目录 protected string $outputDir = ''; // Output directory specified by the -o option
protected int $debugLine = 0; protected int $debugLine = 0;
protected CLImate $climate; protected CLImate $climate;
protected bool $stubFile = false; protected bool $stubFile = false;
@ -483,26 +493,28 @@ class CompilerBase implements PropertyAccessContext
protected Parser $parser; protected Parser $parser;
protected string $phpVersion = self::DEFAULT_PHP_VERSION; protected string $phpVersion = self::DEFAULT_PHP_VERSION;
protected PrettyPrinter $printer; protected PrettyPrinter $printer;
protected bool $isPhpZts = false; // PHP 是否为线程安全版本 protected bool $isPhpZts = false; // Whether the PHP build is thread-safe (ZTS)
// Windows 平台:保存检测到的 PHP lib 文件路径 // Windows platform: store the detected PHP lib file paths.
protected string $windowsPhpEmbedLib = ''; // php8embed.lib 路径 protected string $windowsPhpEmbedLib = ''; // Path to php8embed.lib
protected string $windowsPhpCoreLib = ''; // php8ts.lib 或 php8.lib 路径 protected string $windowsPhpCoreLib = ''; // Path to php8ts.lib or php8.lib
// 新的平台和编译器抽象层(可选使用) // New platform and compiler abstraction layers (optional to use).
protected ?PlatformBase $platform = null; protected ?PlatformBase $platform = null;
protected ?CompilerBackend $compilerBackend = null; protected ?CompilerBackend $compilerBackend = null;
/** /**
* 在预处理阶段获取所有类的方法名称,检测子类和父类中存在的同名方法,解决动态绑定方法调用的问题 * Records all class method names collected during the preprocessing phase.
* `static::methodCall()` * Used to detect methods with the same name declared in both a child class
* `$this->methodCall()` 子类和父类中存在同名方法 * and its parent class, resolving dynamic method-binding calls such as
* `static::methodCall()` and `$this->methodCall()` where a parent and a
* child class both define the method.
* @var array<string, bool> * @var array<string, bool>
*/ */
protected array $classMethodOverride = []; protected array $classMethodOverride = [];
/** /**
* 存储所有类继承关系,类名必须全部为小写 * Stores all class inheritance relationships. Class names must be all lowercase.
* @var array<string, string> * @var array<string, string>
*/ */
protected SymbolRepository $symbols; protected SymbolRepository $symbols;
@ -1183,8 +1195,10 @@ class CompilerBase implements PropertyAccessContext
} }
/** /**
* 判断类的符号指针是否在 PHP 模块生命周期内稳定(MINIT 注册,跨请求缓存安全)。 * Determine whether a class's symbol pointer is stable across the PHP
* 编译产物(本单元编译的类/接口)与 PHP 内置类/接口均满足条件。 * module lifetime (registered at MINIT, safe to cache across requests).
* Compiled output (classes/interfaces compiled in this unit) and PHP
* built-in classes/interfaces both satisfy this condition.
*/ */
protected function isProcessStableClass(string $className): bool protected function isProcessStableClass(string $className): bool
{ {
@ -1196,8 +1210,9 @@ class CompilerBase implements PropertyAccessContext
} }
/** /**
* 判断函数/方法符号指针是否在 PHP 模块生命周期内稳定。 * Determine whether a function/method symbol pointer is stable across the
* `Class::method` 形式的 key 以其所属类的稳定性为准。 * PHP module lifetime. For a `Class::method` key, stability is determined
* by the class it belongs to.
*/ */
protected function isProcessStableFunction(string $funcName): bool protected function isProcessStableFunction(string $funcName): bool
{ {
@ -1251,13 +1266,15 @@ class CompilerBase implements PropertyAccessContext
} }
/** /**
* @param string $className 必须是带有命名空间的完整类名 * @param string $className Must be a fully-qualified class name (with namespace).
* *
* 注意:不存在与用户定义类对应的动态 propMap(区别于 classMap/funcMap)。 * Note: there is no dynamic propMap for user-defined classes (unlike
* 属性 offset 缓存的前提是编译期能解析出声明属性(PropertyAccessResolver * classMap/funcMap). The property-offset cache assumes declared properties
* 只接受编译类的 ClassDef 或内置类的反射声明属性),用户类在编译期不可见, * can be resolved at compile time (PropertyAccessResolver only accepts a
* 其属性访问一律走 `.attr(name)` 字符串路径,因此所有条目必然进程级稳定, * compiled ClassDef or the reflected declared properties of a built-in
* 全部进入 persistentPropMap。 * class). User classes are not visible at compile time, so their property
* accesses always go through the `.attr(name)` string path. As a result,
* every entry is necessarily process-stable and goes into persistentPropMap.
*/ */
protected function getPropertyId(string $className, string $propName): int protected function getPropertyId(string $className, string $propName): int
{ {
@ -1555,10 +1572,12 @@ class CompilerBase implements PropertyAccessContext
} }
return $this->withoutLocalClassEntryHoisting(function () use ($default): string { return $this->withoutLocalClassEntryHoisting(function () use ($default): string {
/* /*
* 函数参数默认值只能为字面量,无法使用表达式获取值。 * Function parameter default values may only be literals; they
* 但 PHP 自 5.6 起支持在默认参数值中使用常量表达式,包括 * cannot be obtained through an expression. Since PHP 5.6, however,
* 类常量(self::FOO、ClassName::BAR、\Full\Class::BAZ), * constant expressions are allowed in default parameter values,
* 编译器需要在编译期将其折叠为对应的字面量。 * including class constants (self::FOO, ClassName::BAR,
* \Full\Class::BAZ). The compiler must fold these into the
* corresponding literal at compile time.
* *
* PHP 8.1 also permits `new` in selected default-value contexts. * PHP 8.1 also permits `new` in selected default-value contexts.
* These expressions are emitted into standalone helper functions, * These expressions are emitted into standalone helper functions,
@ -1585,8 +1604,9 @@ class CompilerBase implements PropertyAccessContext
} }
/** /**
* 在 for/foreach 等包含子语句的语句,之前检查当前待添加的代码是否为空, * For statements containing sub-statements (for/foreach, etc.), check
* 如果不为空,需要将语句追加到 {} 作用域符号之前. * whether the currently pending code is empty. If not, the pending
* statements must be emitted before the opening `{` scope brace.
*/ */
protected function parseBeforeStmtLines(): string protected function parseBeforeStmtLines(): string
{ {
@ -1914,7 +1934,7 @@ class CompilerBase implements PropertyAccessContext
} }
/** /**
* 尽可能转为数字,优先级 浮点 > 整数 > 字符串. * Convert to a number whenever possible, with priority float > integer > string.
*/ */
protected function parseNumericIdentifier(NodeAbstract $expr): string protected function parseNumericIdentifier(NodeAbstract $expr): string
{ {
@ -2002,7 +2022,7 @@ class CompilerBase implements PropertyAccessContext
if ($this->classDef?->nativeObject) { if ($this->classDef?->nativeObject) {
$this->fatalError($expr, 'Native classes do not support `new static()`'); $this->fatalError($expr, 'Native classes do not support `new static()`');
} }
// 无法在编译期获得 static 类的准确类名 // The exact class name of a `static` class cannot be obtained at compile time.
return ''; return '';
} else { } else {
return $this->getNamespacedClassName($class); return $this->getNamespacedClassName($class);
@ -2104,10 +2124,10 @@ class CompilerBase implements PropertyAccessContext
protected function detectDeclaredClassOfExpr(NodeAbstract $expr): string protected function detectDeclaredClassOfExpr(NodeAbstract $expr): string
{ {
// 对象表达式有两类类型信息: // Object expressions carry two kinds of type information:
// 1. detectClassOfExpr() 返回“实际可推断的类”,例如 new Foo()、typed object 变量; // 1. detectClassOfExpr() returns the "actually inferable class", e.g. new Foo() or a typed object variable;
// 2. getDeclaredObjectType() 返回变量声明/首次赋值记录的 declared type,可能是接口或抽象类。 // 2. getDeclaredObjectType() returns the declared type recorded at declaration/first assignment, which may be an interface or abstract class.
// 参数和属性赋值检查需要先使用实际类;实际类不可知时才退回 declared type。 // Parameter and property-assignment checks prefer the actual class, falling back to the declared type only when the actual class is unknown.
$class = $this->detectClassOfExpr($expr); $class = $this->detectClassOfExpr($expr);
if ($class !== '') { if ($class !== '') {
return $class; return $class;
@ -2120,13 +2140,22 @@ class CompilerBase implements PropertyAccessContext
protected function isObjectClassStaticallyAssignableTo(string $class, string $expected): bool protected function isObjectClassStaticallyAssignableTo(string $class, string $expected): bool
{ {
// 这个函数只回答“编译器在静态阶段能否证明 $class is-a $expected”。 // This function only answers "can the compiler prove at the static
// 这里禁止使用 class_exists()/interface_exists()/is_a() 去查询当前运行编译器的 PHP 进程: // stage that $class is-a $expected". It must not use
// - 编译器进程已加载的 Composer/工具类,不等价于被编译项目运行时可用的类; // class_exists()/interface_exists()/is_a() to query the PHP process
// - 自举编译时还会把编译器自身依赖的外部库误判为项目静态类; // currently running the compiler:
// - AOT 的静态判断必须只依赖 hasClass()/hasInterface() 记录的项目类图,或明确的内置类/接口。 // - Composer/tool classes already loaded in the compiler process are not
// 如果类不属于这些集合,说明它是动态类/外部库类,不能在这里静态判定,应返回 false, // equivalent to classes available at runtime for the compiled project;
// 由调用处决定是延迟到运行时 php::toObject()/TypeCheck,还是因为确定 concrete mismatch 而 fatal。 // - during bootstrapping, the compiler's own external dependencies would
// be mistaken for the project's static classes;
// - AOT static analysis must rely only on the project class graph
// recorded by hasClass()/hasInterface(), or on explicitly built-in
// classes/interfaces.
// If a class is not in one of these sets, it is a dynamic / external
// library class and cannot be statically determined here. Return false
// and let the caller decide whether to defer to runtime
// php::toObject()/TypeCheck, or to fail fatally because of a
// determined concrete mismatch.
$class = ltrim($class, '\\'); $class = ltrim($class, '\\');
$expected = ltrim($expected, '\\'); $expected = ltrim($expected, '\\');
if (strcasecmp($class, $expected) === 0) { if (strcasecmp($class, $expected) === 0) {
@ -2146,10 +2175,13 @@ class CompilerBase implements PropertyAccessContext
protected function isKnownConcreteObjectExpr(NodeAbstract $expr, string $class): bool protected function isKnownConcreteObjectExpr(NodeAbstract $expr, string $class): bool
{ {
// “已知 concrete object” 的要求比“表达式写着 new SomeClass”更严格: // "Known concrete object" is stricter than "the expression literally
// 只有 AOT 项目类图中的类或内置类,编译器才能在静态阶段确认其继承关系。 // says new SomeClass": only classes in the AOT project class graph or
// 外部库类即使出现在 new 表达式中,也不能用当前编译器进程的反射信息判定, // built-in classes allow the compiler to confirm inheritance at the
// 否则会把编译器/Composer 运行环境泄漏进被编译项目的类型系统。 // static stage. Even if an external library class appears in a new
// expression, it cannot be determined using the reflection info of the
// current compiler process; doing so would leak the compiler/Composer
// runtime environment into the type system of the compiled project.
if ($class === '' || $this->isInterface($class) || $this->isAbstractClass($class)) { if ($class === '' || $this->isInterface($class) || $this->isAbstractClass($class)) {
return false; return false;
} }
@ -2390,7 +2422,7 @@ class CompilerBase implements PropertyAccessContext
$lines[] = 'return ' . $tuple . ';'; $lines[] = 'return ' . $tuple . ';';
return implode(PHP_EOL . $this->getIndent(), $lines); return implode(PHP_EOL . $this->getIndent(), $lines);
} }
// 实际函数的返回值 // The return value of the actual function.
$type = $this->detectTypeOfExpr($v->expr); $type = $this->detectTypeOfExpr($v->expr);
// In ordinary PHP mode, int +/−/* int is only conditionally an int: // In ordinary PHP mode, int +/−/* int is only conditionally an int:
// runtime overflow promotes the result to float. Keep the Variant // runtime overflow promotes the result to float. Keep the Variant
@ -2488,7 +2520,7 @@ class CompilerBase implements PropertyAccessContext
$expr = $this->parseExprAsValue($v->expr); $expr = $this->parseExprAsValue($v->expr);
$returnType = $this->getReturnType(); $returnType = $this->getReturnType();
// 匿名函数的返回值一定是 var // The return value of an anonymous function is always var.
if (!$this->context->inClosure) { if (!$this->context->inClosure) {
if ($returnType === Type::VOID) { if ($returnType === Type::VOID) {
$this->fatalError($v, 'The return type is void, cannot return any value'); $this->fatalError($v, 'The return type is void, cannot return any value');
@ -2511,7 +2543,7 @@ class CompilerBase implements PropertyAccessContext
} }
$returnObjectCheckClass = ''; $returnObjectCheckClass = '';
// 返回值的表达式是一个类的对象 // The return-value expression is an instance of a class.
$objectClass = $this->detectDeclaredClassOfExpr($v->expr); $objectClass = $this->detectDeclaredClassOfExpr($v->expr);
$returnClass = $this->context->inClosure ? '' : $this->getReturnClass(); $returnClass = $this->context->inClosure ? '' : $this->getReturnClass();
if ($returnClass) { if ($returnClass) {
@ -2537,13 +2569,17 @@ class CompilerBase implements PropertyAccessContext
[$code, $tmpVar] = $this->genUnionCheckedReturnAssignment($exprCode); [$code, $tmpVar] = $this->genUnionCheckedReturnAssignment($exprCode);
$this->context->afterStmtLines[] = $this->getIndent() . 'return ' . $tmpVar . ';'; $this->context->afterStmtLines[] = $this->getIndent() . 'return ' . $tmpVar . ';';
} elseif (!$this->isVarExpr($v->expr) and !$this->isScalar($v->expr)) { } elseif (!$this->isVarExpr($v->expr) and !$this->isScalar($v->expr)) {
// return 如果使用了 Indirect 语句,可能会导致变量提前析构,出现悬空指针 // If return uses an Indirect statement, the variable may be
// 将 Indirect 赋值给临时变量后,使用 Ctor::Copy 解除了 Indirect,保证内存安全 // destructed early, producing a dangling pointer. Assign the
// Indirect to a temporary variable; Ctor::Copy releases the
// Indirect, guaranteeing memory safety.
$tmpVar = $this->genTmpVarName(); $tmpVar = $this->genTmpVarName();
// 必须提前声明变量,否则在末尾声明并 return 可能会被 gcc 优化掉 // The variable must be declared up front; otherwise declaring it at
// the end and returning it could be optimized away by gcc.
$this->addLocalVar($tmpVar, $returnType); $this->addLocalVar($tmpVar, $returnType);
$code = $tmpVar . ' = (' . $exprCode . ');' . PHP_EOL; $code = $tmpVar . ' = (' . $exprCode . ');' . PHP_EOL;
// 解析表达式后可能会插入语句,因此需要在末尾添加 return 语句,而不是直接返回 // Parsing the expression may insert statements, so the return
// statement must be appended at the end rather than returned directly.
$this->context->afterStmtLines[] = $this->getIndent() . 'return ' . $tmpVar . ';'; $this->context->afterStmtLines[] = $this->getIndent() . 'return ' . $tmpVar . ';';
} else { } else {
$code = 'return ' . $exprCode . ';'; $code = 'return ' . $exprCode . ';';
@ -2715,7 +2751,8 @@ class CompilerBase implements PropertyAccessContext
$classDef = $this->getClass($class); $classDef = $this->getClass($class);
$methodDef = null; $methodDef = null;
// 递归查找,若子类中未定义方法,则尝试查找父类是否存在此方法 // Search recursively: if the method is not defined in the child class,
// try to find it in the parent class.
while (true) { while (true) {
if (!$classDef->hasMethod($method)) { if (!$classDef->hasMethod($method)) {
if (!$classDef->extends) { if (!$classDef->extends) {
@ -2743,7 +2780,7 @@ class CompilerBase implements PropertyAccessContext
if (!$this->checkAccessible($classDef, $methodDef->flags)) { if (!$this->checkAccessible($classDef, $methodDef->flags)) {
$this->fatalError($expr, 'Method `' . $classDef->getNamespacedName() . '::' . $method . '()` is not accessible'); $this->fatalError($expr, 'Method `' . $classDef->getNamespacedName() . '::' . $method . '()` is not accessible');
} }
// 函数调用占位符,不是真实的函数调用 // A function-call placeholder, not a real function call.
if (count($expr->args) === 1 and $this->isPlaceholderExpr($expr->args[0])) { if (count($expr->args) === 1 and $this->isPlaceholderExpr($expr->args[0])) {
return false; return false;
} }
@ -2767,7 +2804,8 @@ class CompilerBase implements PropertyAccessContext
$classDef = $this->getClass($class); $classDef = $this->getClass($class);
$originClassDef = $classDef; $originClassDef = $classDef;
$constDef = null; $constDef = null;
// 递归查找,若子类中未定义方法,则尝试查找父类是否存在此方法 // Search recursively: if the constant is not defined in the child class,
// try to find it in the parent class.
while (true) { while (true) {
if (!$classDef->hasConstant($const)) { if (!$classDef->hasConstant($const)) {
if (!$classDef->extends) { if (!$classDef->extends) {
@ -3276,12 +3314,15 @@ class CompilerBase implements PropertyAccessContext
} }
/** /**
* $GLOBALS['var'] 等价于 global $var; $var ,将字符串常量转为变量名称即可 * Resolve a PHP function name to its native (compiled) name by trying
* 仅限于字面量字符串可以转为变量名称,其他则使用 php::global() 函数获取 * every candidate form: absolute names, qualified names resolved through
* the class/namespace import table, unqualified names in the current
* namespace, and `use function` imports. Returns false when no compiled
* function matches.
*/ */
protected function findNativeFunction(string $funcName): string|false protected function findNativeFunction(string $funcName): string|false
{ {
// 绝对命名空间的函数 // Absolutely-qualified function name.
if ($funcName[0] == '\\') { if ($funcName[0] == '\\') {
$funcName = ltrim($funcName, '\\'); $funcName = ltrim($funcName, '\\');
$possibleFunctionNames = [$this->escapeName($funcName)]; $possibleFunctionNames = [$this->escapeName($funcName)];
@ -3759,13 +3800,14 @@ class CompilerBase implements PropertyAccessContext
$this->assertNotNativeObjectDynamicClassTarget($expr->class, $expr); $this->assertNotNativeObjectDynamicClassTarget($expr->class, $expr);
} }
$ctorClassName = ''; $ctorClassName = '';
// 匿名类 // Anonymous class.
if ($expr->class instanceof Node\Stmt\Class_) { if ($expr->class instanceof Node\Stmt\Class_) {
if ($expr->class->name === null) { if ($expr->class->name === null) {
$classDef = $expr->class; $classDef = $expr->class;
$className = $this->genAnonClassName(); $className = $this->genAnonClassName();
$classDef->name = new Node\Identifier($className); $classDef->name = new Node\Identifier($className);
// 继承父类和接口可能是 use 的名称,需要转换成全限定名称 // The inherited parent class and interfaces may be `use` names
// and need to be converted to fully-qualified names.
if ($classDef->extends !== null) { if ($classDef->extends !== null) {
$parentClass = $this->getNamespacedClassName($this->parseIdentifier($classDef->extends)); $parentClass = $this->getNamespacedClassName($this->parseIdentifier($classDef->extends));
$classDef->extends = new Node\Name\FullyQualified($parentClass); $classDef->extends = new Node\Name\FullyQualified($parentClass);
@ -3777,7 +3819,9 @@ class CompilerBase implements PropertyAccessContext
} }
} }
$this->flattenEmbeddedClassTraits($classDef); $this->flattenEmbeddedClassTraits($classDef);
// 匿名类由根命名空间中的 eval 定义,内部导入的符号必须转为全限定名称。 // Anonymous classes are defined by eval in the root namespace,
// so symbols imported inside them must be converted to
// fully-qualified names.
$this->resolveAnonClassNames($classDef); $this->resolveAnonClassNames($classDef);
$this->context->beforeStmtLines[] = 'static THREAD_LOCAL bool ' . $className . '_defined = false;'; $this->context->beforeStmtLines[] = 'static THREAD_LOCAL bool ' . $className . '_defined = false;';
$classCode = $this->genEmbeddedCode($classDef); $classCode = $this->genEmbeddedCode($classDef);
@ -4144,7 +4188,7 @@ class CompilerBase implements PropertyAccessContext
protected function parseEval(Expr\Eval_ $expr): string protected function parseEval(Expr\Eval_ $expr): string
{ {
$this->assertExprCanBeUsedAsValue($expr->expr, 'eval operand'); $this->assertExprCanBeUsedAsValue($expr->expr, 'eval operand');
// 对 eval() 指令的 PHP 代码段禁止字面量优化 // Disable literal-string optimization for the PHP code passed to eval().
$expr->expr->setAttribute('noLiteralString', true); $expr->expr->setAttribute('noLiteralString', true);
$source = $this->isNativeObjectClass($this->detectClassOfExpr($expr->expr)) $source = $this->isNativeObjectClass($this->detectClassOfExpr($expr->expr))
? $this->parseExprToString($expr->expr) ? $this->parseExprToString($expr->expr)
@ -4239,7 +4283,8 @@ class CompilerBase implements PropertyAccessContext
} }
/** /**
* 左值只能为变量、数组、对象属性、对象静态属性 * The left value may only be a variable, array element, object property,
* or class static property.
*/ */
protected function checkLeftValue(NodeAbstract $expr): void protected function checkLeftValue(NodeAbstract $expr): void
{ {
@ -4282,7 +4327,8 @@ class CompilerBase implements PropertyAccessContext
return $nativePresence; return $nativePresence;
} }
} }
// TypePHP 编译器不允许操作未定义的变量,PHP 的 isset($var) 可能 $var 未定义 // The TypePHP compiler disallows operating on undefined variables;
// in PHP, isset($var) may be used with an undefined $var.
$this->checkVarMustExist($node, $this->parseIdentifier($node)); $this->checkVarMustExist($node, $this->parseIdentifier($node));
$fn = $this->getChainedFunc($op); $fn = $this->getChainedFunc($op);
$expr = $node; $expr = $node;
@ -4301,7 +4347,7 @@ class CompilerBase implements PropertyAccessContext
// $getValue is true: fall through to use the chain+result mechanism, // $getValue is true: fall through to use the chain+result mechanism,
// which ensures the result type is TYPE_VAR (compatible with ternaries). // which ensures the result type is TYPE_VAR (compatible with ternaries).
} }
// 单属性读取(非链式) // Single property read (non-chained).
if ($this->isPropertyFetch($expr) and $this->isVarExpr($expr->var) and $this->isIdExpr($expr->name)) { if ($this->isPropertyFetch($expr) and $this->isVarExpr($expr->var) and $this->isIdExpr($expr->name)) {
$prop = $this->parsePropertyFetch($expr); $prop = $this->parsePropertyFetch($expr);
if ($this->isNativePropertyAccess($expr)) { if ($this->isNativePropertyAccess($expr)) {
@ -4371,7 +4417,8 @@ class CompilerBase implements PropertyAccessContext
$node->setAttribute('chainOpResult', $result); $node->setAttribute('chainOpResult', $result);
return $fn . '(' . $var . ', {' . implode(', ', $list) . '}, ' . $result . ')'; return $fn . '(' . $var . ', {' . implode(', ', $list) . '}, ' . $result . ')';
} else { } else {
// toReference(var, {}) 返回空引用,空链时改用成员函数形式 // toReference(var, {}) returns an empty reference; use the member
// function form instead when the chain is empty.
if ($op === self::OP_REFVAL && empty($list)) { if ($op === self::OP_REFVAL && empty($list)) {
return $var . '.toReference()'; return $var . '.toReference()';
} }
@ -4854,19 +4901,20 @@ class CompilerBase implements PropertyAccessContext
if ($toType === Type::VAR or $fromType === Type::VAR) { if ($toType === Type::VAR or $fromType === Type::VAR) {
return true; return true;
} }
// 引用当前没有类型信息,按照 var 处理 // References currently carry no type information, so treat them as var.
if ($toType === Type::REF or $fromType === Type::REF) { if ($toType === Type::REF or $fromType === Type::REF) {
return true; return true;
} }
// 类型一致,可以互相赋值 // Types are identical, so they can be assigned to each other.
if ($toType === $fromType) { if ($toType === $fromType) {
return true; return true;
} }
// 原生类型可以互相转换,由 C++ 底层完成 // Native types can be converted between each other, handled by the C++ layer.
if ($this->isNativeType($toType) and $this->isNativeType($fromType)) { if ($this->isNativeType($toType) and $this->isNativeType($fromType)) {
return true; return true;
} }
// BigInt/BigFloat/Decimal 与原生类型之间可能发生隐式转换,允许重新赋值 // Implicit conversions between BigInt/BigFloat/Decimal and native types
// are possible, so re-assignment is allowed.
$bigTypes = [Type::BIGINT, Type::DECIMAL, Type::BIGFLOAT]; $bigTypes = [Type::BIGINT, Type::DECIMAL, Type::BIGFLOAT];
if (in_array($toType, $bigTypes, true) or in_array($fromType, $bigTypes, true)) { if (in_array($toType, $bigTypes, true) or in_array($fromType, $bigTypes, true)) {
return true; return true;
@ -4911,12 +4959,12 @@ class CompilerBase implements PropertyAccessContext
$scopeClassDef = $this->getClass($this->functionDef->attributeFactoryScope); $scopeClassDef = $this->getClass($this->functionDef->attributeFactoryScope);
} }
} }
// 私有方法,只能当前的类使用 // Private methods can only be used by the current class.
if ($flags & Modifiers::PRIVATE) { if ($flags & Modifiers::PRIVATE) {
return $scopeClassDef !== null return $scopeClassDef !== null
&& $this->isSameClassName($declaringClass, $scopeClassDef->getNamespacedName(false)); && $this->isSameClassName($declaringClass, $scopeClassDef->getNamespacedName(false));
} }
// 保护方法,只能当前类和子类使用 // Protected methods can only be used by the current class and its subclasses.
if ($flags & Modifiers::PROTECTED) { if ($flags & Modifiers::PROTECTED) {
if (!$scopeClassDef) { if (!$scopeClassDef) {
return false; return false;
@ -4926,12 +4974,14 @@ class CompilerBase implements PropertyAccessContext
$declaringClass $declaringClass
); );
} }
// 类外部调用,只允许调用 public 方法 // Calls from outside the class are only allowed for public methods.
return true; return true;
} }
/** /**
* 沿继承链查找实际调用的构造函数,包括项目类继承的内部类构造函数。 * Walk the inheritance chain to find the constructor that is actually
* invoked, including constructors of internal classes inherited by project
* classes.
* *
* @return array{className: string, flags: int}|null * @return array{className: string, flags: int}|null
*/ */

@ -45,7 +45,7 @@ trait CompilationStateTrait
$this->fatalError($var, 'Duplicate variable `$' . $var->name . '`'); $this->fatalError($var, 'Duplicate variable `$' . $var->name . '`');
} }
$this->context->staticVars[$name] = $type; $this->context->staticVars[$name] = $type;
// 静态变量实际上是一个全局变量的引用 // A static variable is actually a reference to a global variable.
$globalVar = $this->escapeStaticVar($name); $globalVar = $this->escapeStaticVar($name);
$this->addGlobalVar($globalVar, $type); $this->addGlobalVar($globalVar, $type);
return $globalVar; return $globalVar;
@ -165,7 +165,7 @@ trait CompilationStateTrait
} }
/** /**
* @param string $name 必须传入带有完整命名空间的类名,将会自动转义为 native name * @param string $name Must be a fully qualified class name including the namespace; it will be automatically escaped to a native name.
*/ */
protected function hasFunction(string $name): bool protected function hasFunction(string $name): bool
{ {
@ -222,8 +222,8 @@ trait CompilationStateTrait
protected function checkFunction(string $name): void protected function checkFunction(string $name): void
{ {
// 在预处理阶段检测到函数声明,但是未定义,说明在当前文件,但是顺序错误 // The function declaration was detected during the preprocessing stage but is not yet defined,
// 跳过,稍后再处理 // meaning it is in the current file but appears in the wrong order. Skip it and handle it later.
if (isset($this->symbolDeclInFile[$name]) if (isset($this->symbolDeclInFile[$name])
and $this->symbolDeclInFile[$name] === $this->file and $this->symbolDeclInFile[$name] === $this->file
and !$this->hasFunction($name)) { and !$this->hasFunction($name)) {

@ -64,7 +64,7 @@ class FunctionDef
public string $displayName = ''; public string $displayName = '';
/** /**
* @var string 必须是带有命名空间的完整类名 * @var string Must be a fully qualified class name including the namespace
*/ */
public string $returnClass = ''; public string $returnClass = '';
/** Whether a Native object return may be represented by nullptr. */ /** Whether a Native object return may be represented by nullptr. */

@ -18,12 +18,12 @@ class Extractor
} }
/** /**
* 提取函数定义. * Extract function definitions.
* *
* @param string $filename 文件路径 * @param string $filename File path
* @param array $prefixes 函数名前缀列表 * @param array $prefixes List of function-name prefixes
* *
* @return array 函数列表 * @return array List of functions
*/ */
public function extractFunctions(string $filename, array $prefixes = ['php_']): array public function extractFunctions(string $filename, array $prefixes = ['php_']): array
{ {
@ -34,10 +34,10 @@ class Extractor
$this->info("分析文件: {$filename}"); $this->info("分析文件: {$filename}");
$this->info('函数前缀: ' . implode(', ', $prefixes)); $this->info('函数前缀: ' . implode(', ', $prefixes));
// 运行 ctags // Run ctags.
$tags = $this->runCtags($filename); $tags = $this->runCtags($filename);
// 过滤和解析函数 // Filter and parse functions.
$functions = []; $functions = [];
foreach ($tags as $tag) { foreach ($tags as $tag) {
if ($tag['kind'] !== 'function') { if ($tag['kind'] !== 'function') {
@ -46,7 +46,7 @@ class Extractor
$funcName = $tag['name'] ?? ''; $funcName = $tag['name'] ?? '';
// 检查前缀 // Check the prefix.
$matched = false; $matched = false;
foreach ($prefixes as $prefix) { foreach ($prefixes as $prefix) {
if (str_starts_with($funcName, $prefix)) { if (str_starts_with($funcName, $prefix)) {
@ -59,7 +59,7 @@ class Extractor
continue; continue;
} }
// 解析函数详细信息 // Parse the detailed function information.
$funcInfo = $this->parseFunction($filename, $tag); $funcInfo = $this->parseFunction($filename, $tag);
if ($funcInfo) { if ($funcInfo) {
$functions[] = $funcInfo; $functions[] = $funcInfo;
@ -72,7 +72,7 @@ class Extractor
} }
/** /**
* 批量提取多个文件. * Extract functions from multiple files in bulk.
*/ */
public function extractFromFiles(array $files, array $prefixes = ['php_']): array public function extractFromFiles(array $files, array $prefixes = ['php_']): array
{ {
@ -91,7 +91,7 @@ class Extractor
} }
/** /**
* 检查 ctags 是否可用. * Check whether ctags is available.
*/ */
private function checkCtags(): void private function checkCtags(): void
{ {
@ -107,7 +107,7 @@ class Extractor
} }
/** /**
* 运行 ctags 命令. * Run the ctags command.
*/ */
private function runCtags(string $filename): array private function runCtags(string $filename): array
{ {
@ -123,7 +123,7 @@ class Extractor
throw new \RuntimeException('ctags 执行失败'); throw new \RuntimeException('ctags 执行失败');
} }
// 解析 JSON 输出 // Parse the JSON output.
$tags = []; $tags = [];
$lines = explode("\n", trim($output)); $lines = explode("\n", trim($output));
@ -144,7 +144,7 @@ class Extractor
} }
/** /**
* 解析单个函数的详细信息. * Parse the detailed information of a single function.
*/ */
private function parseFunction(string $filename, array $tag): ?array private function parseFunction(string $filename, array $tag): ?array
{ {
@ -155,17 +155,17 @@ class Extractor
return null; return null;
} }
// 提取完整的函数签名 // Extract the complete function signature.
$signature = $this->extractSignature($filename, $lineNum, $funcName); $signature = $this->extractSignature($filename, $lineNum, $funcName);
if (empty($signature)) { if (empty($signature)) {
return null; return null;
} }
// 解析返回类型 // Parse the return type.
$returnType = $this->parseReturnType($signature, $funcName); $returnType = $this->parseReturnType($signature, $funcName);
// 解析参数 // Parse the parameters.
$parameters = $this->parseParameters($signature, $funcName); $parameters = $this->parseParameters($signature, $funcName);
return [ return [
@ -183,7 +183,7 @@ class Extractor
} }
/** /**
* 从源文件中提取完整的函数签名. * Extract the complete function signature from the source file.
*/ */
private function extractSignature(string $filename, int $lineNum, string $funcName): string private function extractSignature(string $filename, int $lineNum, string $funcName): string
{ {
@ -193,7 +193,7 @@ class Extractor
return ''; return '';
} }
// 从函数声明行开始收集,直到遇到 { 或 ; // Collect lines starting from the function declaration until a { or ; is reached.
$signatureLines = []; $signatureLines = [];
$maxLines = min($lineNum + 20, count($lines)); $maxLines = min($lineNum + 20, count($lines));
@ -201,37 +201,37 @@ class Extractor
$line = $lines[$i]; $line = $lines[$i];
$signatureLines[] = $line; $signatureLines[] = $line;
// 检查是否到达函数体或声明结束 // Check whether the function body or the end of the declaration has been reached.
if (strpos($line, '{') !== false || strpos($line, ';') !== false) { if (strpos($line, '{') !== false || strpos($line, ';') !== false) {
break; break;
} }
} }
// 合并并清理 // Join and clean up.
$signature = implode(' ', $signatureLines); $signature = implode(' ', $signatureLines);
// 移除 { 或 ; 之后的内容 // Remove everything after the { or ;.
$signature = preg_replace('/[{;].*$/', '', $signature); $signature = preg_replace('/[{;].*$/', '', $signature);
// 合并多个空白字符 // Collapse multiple whitespace characters.
$signature = preg_replace('/\s+/', ' ', $signature); $signature = preg_replace('/\s+/', ' ', $signature);
// 清理首尾空白 // Trim leading and trailing whitespace.
return trim($signature); return trim($signature);
} }
/** /**
* 解析返回类型. * Parse the return type.
*/ */
private function parseReturnType(string $signature, string $funcName): string private function parseReturnType(string $signature, string $funcName): string
{ {
// 匹配: <返回类型> <函数名>( // Match: <return type> <function name>(
$pattern = '/^(.+?)\s+' . preg_quote($funcName, '/') . '\s*\(/'; $pattern = '/^(.+?)\s+' . preg_quote($funcName, '/') . '\s*\(/';
if (preg_match($pattern, $signature, $matches)) { if (preg_match($pattern, $signature, $matches)) {
$returnType = trim($matches[1]); $returnType = trim($matches[1]);
// 移除可能的修饰符 // Remove possible modifiers.
$returnType = preg_replace('/\b(static|inline|extern|virtual|explicit)\b/', '', $returnType); $returnType = preg_replace('/\b(static|inline|extern|virtual|explicit)\b/', '', $returnType);
$returnType = preg_replace('/\s+/', ' ', $returnType); $returnType = preg_replace('/\s+/', ' ', $returnType);
$returnType = trim($returnType); $returnType = trim($returnType);
@ -243,11 +243,11 @@ class Extractor
} }
/** /**
* 解析参数列表. * Parse the parameter list.
*/ */
private function parseParameters(string $signature, string $funcName): array private function parseParameters(string $signature, string $funcName): array
{ {
// 提取括号内的参数 // Extract the parameters inside the parentheses.
$pattern = '/' . preg_quote($funcName, '/') . '\s*\((.*?)\)/s'; $pattern = '/' . preg_quote($funcName, '/') . '\s*\((.*?)\)/s';
if (!preg_match($pattern, $signature, $matches)) { if (!preg_match($pattern, $signature, $matches)) {
@ -256,12 +256,12 @@ class Extractor
$paramsStr = trim($matches[1]); $paramsStr = trim($matches[1]);
// 空参数或 void // Empty parameters or void.
if (empty($paramsStr) || $paramsStr === 'void') { if (empty($paramsStr) || $paramsStr === 'void') {
return []; return [];
} }
// 分割参数(处理嵌套的模板和括号) // Split the parameters (handling nested templates and parentheses).
$params = $this->splitParameters($paramsStr); $params = $this->splitParameters($paramsStr);
$parameters = []; $parameters = [];
@ -282,7 +282,7 @@ class Extractor
} }
/** /**
* 智能分割参数(处理嵌套的模板和括号). * Intelligently split parameters (handling nested templates and parentheses).
*/ */
private function splitParameters(string $paramsStr): array private function splitParameters(string $paramsStr): array
{ {
@ -316,17 +316,17 @@ class Extractor
} }
/** /**
* 解析单个参数. * Parse a single parameter.
*/ */
private function parseParameter(string $param): ?array private function parseParameter(string $param): ?array
{ {
$param = trim($param); $param = trim($param);
// 移除默认值 // Remove the default value.
$param = preg_replace('/\s*=\s*.*$/', '', $param); $param = preg_replace('/\s*=\s*.*$/', '', $param);
// 尝试匹配: <类型> <名称> // Try to match: <type> <name>.
// 支持复杂类型如: const char*, std::string&, int**, etc. // Supports complex types such as: const char*, std::string&, int**, etc.
if (preg_match('/^(.+?)\s+(\w+)\s*$/', $param, $matches)) { if (preg_match('/^(.+?)\s+(\w+)\s*$/', $param, $matches)) {
return [ return [
'type' => trim($matches[1]), 'type' => trim($matches[1]),
@ -334,7 +334,7 @@ class Extractor
]; ];
} }
// 只有类型,没有名称 // Only a type, no name.
return [ return [
'type' => $param, 'type' => $param,
'name' => '', 'name' => '',
@ -342,7 +342,7 @@ class Extractor
} }
/** /**
* 输出信息. * Output an informational message.
*/ */
private function info(string $message): void private function info(string $message): void
{ {
@ -350,7 +350,7 @@ class Extractor
} }
/** /**
* 输出警告. * Output a warning.
*/ */
private function warn(string $message): void private function warn(string $message): void
{ {
@ -358,7 +358,7 @@ class Extractor
} }
/** /**
* 输出错误并退出. * Output an error and exit.
*/ */
private function error(string $message): void private function error(string $message): void
{ {

@ -35,7 +35,7 @@ trait CallArgumentGenerator
$hasNamedArg = false; $hasNamedArg = false;
$argNameIndex = $this->getFunctionArgNameIndex($functionDef); $argNameIndex = $this->getFunctionArgNameIndex($functionDef);
$variadicArgIndex = $this->getVariadicArgIndex($functionDef); $variadicArgIndex = $this->getVariadicArgIndex($functionDef);
// 对命名参数进行重排 // Reorder the named arguments into their declared positions
foreach ($callArgs as $i => $arg) { foreach ($callArgs as $i => $arg) {
if ($this->isPlaceholderExpr($arg)) { if ($this->isPlaceholderExpr($arg)) {
throw new PlaceHolder(); throw new PlaceHolder();
@ -76,7 +76,7 @@ trait CallArgumentGenerator
if ($deferTrailingDefaults && $variadicArgCount > 0) { if ($deferTrailingDefaults && $variadicArgCount > 0) {
$lastProvidedIndex = $variadicArgIndex; $lastProvidedIndex = $variadicArgIndex;
} }
// 命名参数中间存在空洞,需要使用默认参数填充 // Holes left between named arguments must be filled with default arguments
foreach ($functionDef->argInfoList as $k => $argInfo) { foreach ($functionDef->argInfoList as $k => $argInfo) {
if ($k < $parameterOffset) { if ($k < $parameterOffset) {
continue; continue;
@ -118,7 +118,8 @@ trait CallArgumentGenerator
} }
} }
// 函数只接受一个变长参数,且调用参数为空,直接传入空数组 // If the function only accepts a single variadic parameter and the call
// supplies no arguments, pass an empty array directly
if (count($sourceArgs) === 0 if (count($sourceArgs) === 0
and count($functionDef->argInfoList) === $parameterOffset + 1 and count($functionDef->argInfoList) === $parameterOffset + 1
and $functionDef->argInfoList[$parameterOffset]->variadic) { and $functionDef->argInfoList[$parameterOffset]->variadic) {
@ -203,7 +204,8 @@ trait CallArgumentGenerator
} }
if ($className) { if ($className) {
// 动态调用类方法,无法判断参数是否为引用 // For dynamically called class methods, whether the parameter is
// passed by reference cannot be determined
if ($className === self::DYNAMIC_CALLED_CLASS) { if ($className === self::DYNAMIC_CALLED_CLASS) {
return false; return false;
} }
@ -216,7 +218,8 @@ trait CallArgumentGenerator
return $param->isPassedByReference(); return $param->isPassedByReference();
} }
// 参数索引超出声明范围,检查最后一个参数是否为变长引用参数(如 &...$rest) // The argument index exceeds the declared range; check whether the last
// parameter is a by-reference variadic parameter (e.g. &...$rest)
$variadicParam = Reflection::getVariadicParameter($funcName, $className); $variadicParam = Reflection::getVariadicParameter($funcName, $className);
return $variadicParam !== null && $variadicParam->isPassedByReference(); return $variadicParam !== null && $variadicParam->isPassedByReference();
} }
@ -492,7 +495,7 @@ trait CallArgumentGenerator
$array = $this->parseIdentifier($arg->value->var); $array = $this->parseIdentifier($arg->value->var);
if ($array === 'GLOBALS') { if ($array === 'GLOBALS') {
$globalVar = $this->parseGlobalsArrayDimFetch($arg->value); $globalVar = $this->parseGlobalsArrayDimFetch($arg->value);
// 全局变量作为引用参数 // Global variable passed as a by-reference argument
if ($byRef) { if ($byRef) {
$ref = $this->addTmpVar(Type::REF); $ref = $this->addTmpVar(Type::REF);
$this->context->beforeStmtLines[] = $ref . ' = ' . $globalVar . '.toReference();'; $this->context->beforeStmtLines[] = $ref . ' = ' . $globalVar . '.toReference();';
@ -749,8 +752,9 @@ trait CallArgumentGenerator
} }
/** /**
* 展开 refval() 调用中的数组元素或对象属性,返回对应的 C++ 引用表达式。 * Expand an array element or object property inside a refval() call into its
* 若为普通变量则返回 null,由调用方自行处理。 * corresponding C++ reference expression. Returns null for a plain variable,
* which the caller then handles itself.
*/ */
protected function expandRefvalExpr(NodeAbstract $inner, Node\Arg $arg): ?string protected function expandRefvalExpr(NodeAbstract $inner, Node\Arg $arg): ?string
{ {
@ -777,27 +781,31 @@ trait CallArgumentGenerator
} }
/** /**
* 仅用于动态调用的参数解析 * Argument parsing used only for dynamic calls
*/ */
protected function parseArgRefVar(Node\Arg $arg, string $name): string protected function parseArgRefVar(Node\Arg $arg, string $name): string
{ {
if (!$this->hasVar($name)) { if (!$this->hasVar($name)) {
// 若参数是引用类型,可以传入未定义变量,将立即创建变量作为引用 // For a by-reference parameter, an undefined variable may be passed;
// it is created immediately as a reference
$this->addLocalVar($name, Type::REF); $this->addLocalVar($name, Type::REF);
} elseif ($this->getVarType($name) === Type::REF) { } elseif ($this->getVarType($name) === Type::REF) {
return '&' . $name; return '&' . $name;
} else { } else {
// 本地变量,且是原生类型,则转为普通变量 // A local variable of native type is converted to a plain variable
if ($this->hasLocalVar($name) and $this->isNativeType($this->getVarType($name))) { if ($this->hasLocalVar($name) and $this->isNativeType($this->getVarType($name))) {
$this->context->localVars[$name] = Type::VAR; $this->context->localVars[$name] = Type::VAR;
} }
// 需要引用类型的参数,使用临时变量作为引用,并替换掉实际的参数 // For a by-reference parameter, use a temporary variable as the reference
// and replace the actual argument with it
$tmpVar = $this->genTmpVarName(); $tmpVar = $this->genTmpVarName();
$this->addLocalVar($tmpVar, Type::REF); $this->addLocalVar($tmpVar, Type::REF);
$this->context->beforeStmtLines[] = $tmpVar . ' = ' . $this->parseExpr($arg->value) . '.toReference();'; $this->context->beforeStmtLines[] = $tmpVar . ' = ' . $this->parseExpr($arg->value) . '.toReference();';
$name = $tmpVar; $name = $tmpVar;
} }
// 动态调用,参数列表是 Variant 类型而不是 Reference,必须使用 & 符号取地址,传递指针,以保持引用传递 // For dynamic calls, the argument list is Variant rather than Reference,
// so the & operator must be used to take the address and pass a pointer
// in order to preserve pass-by-reference semantics
return '&' . $name; return '&' . $name;
} }

@ -285,7 +285,8 @@ trait ClosureGenerator
$this->fatalError($useItem->var, 'Incorrect Closure use syntax, only variable names are allowed'); $this->fatalError($useItem->var, 'Incorrect Closure use syntax, only variable names are allowed');
} }
if ($useItem->byRef) { if ($useItem->byRef) {
// 闭包的 use 语法,若为引用类型,可以就地创建变量 // For a closure use clause, a by-reference capture may create
// the variable in place if it does not exist yet
if (!isset($oriContext->localVars[$var]) if (!isset($oriContext->localVars[$var])
&& !isset($oriContext->staticVars[$var])) { && !isset($oriContext->staticVars[$var])) {
$oriContext->localVars[$var] = Type::REF; $oriContext->localVars[$var] = Type::REF;

@ -3,11 +3,12 @@
namespace TypePhp\Generator; namespace TypePhp\Generator;
/** /**
* Windows 资源文件 (.rc) 生成器 * Windows resource file (.rc) generator
* *
* 用于生成 Windows PE 资源文件,可将图标、版本信息等嵌入到 exe 中 * Generates Windows PE resource files, embedding icons, version information,
* and other resources into the exe
* *
* 配置示例(在 project.yml 中): * Configuration example (in project.yml):
* *
* resource: * resource:
* icon: path/to/icon.ico * icon: path/to/icon.ico
@ -28,18 +29,18 @@ namespace TypePhp\Generator;
* product-name: "My Product" * product-name: "My Product"
* comments: "Built with TypePHP" * comments: "Built with TypePHP"
* *
* # manifest 与 resource 同级(可选,Windows 平台缺省不携带) * # manifest sits at the same level as resource (optional; omitted by default on Windows)
* manifest: path/to/app.manifest * manifest: path/to/app.manifest
*/ */
class ResourceFileGenerator class ResourceFileGenerator
{ {
/** /**
* 资源配置 * Resource configuration
*/ */
private array $config; private array $config;
/** /**
* 项目目录(用于解析相对路径) * Project directory (used to resolve relative paths)
*/ */
private string $projectDir; private string $projectDir;
@ -50,7 +51,7 @@ class ResourceFileGenerator
} }
/** /**
* 检查是否有任何资源配置 * Check whether any resource is configured
*/ */
public function hasResource(): bool public function hasResource(): bool
{ {
@ -60,7 +61,7 @@ class ResourceFileGenerator
} }
/** /**
* 获取图标文件的绝对路径 * Get the absolute path of the icon file
*/ */
public function getIconPath(): ?string public function getIconPath(): ?string
{ {
@ -73,7 +74,7 @@ class ResourceFileGenerator
} }
/** /**
* 获取 manifest 文件的绝对路径 * Get the absolute path of the manifest file
*/ */
public function getManifestPath(): ?string public function getManifestPath(): ?string
{ {
@ -86,21 +87,21 @@ class ResourceFileGenerator
} }
/** /**
* 解析相对/绝对路径为绝对路径 * Resolve a relative/absolute path into an absolute path
*/ */
private function resolvePath(string $path): string private function resolvePath(string $path): string
{ {
// 如果是绝对路径,直接使用 // If it is already an absolute path, use it as-is
if (preg_match('/^[A-Za-z]:\\\\|^\//', $path)) { if (preg_match('/^[A-Za-z]:\\\\|^\//', $path)) {
return $path; return $path;
} }
// 相对路径,基于项目目录解析 // Otherwise resolve the relative path against the project directory
return $this->projectDir . DIRECTORY_SEPARATOR . $path; return $this->projectDir . DIRECTORY_SEPARATOR . $path;
} }
/** /**
* 生成 .rc 资源文件内容 * Generate the contents of the .rc resource file
*/ */
public function generate(): string public function generate(): string
{ {
@ -109,15 +110,15 @@ class ResourceFileGenerator
$content .= '// DO NOT EDIT - This file is auto-generated' . PHP_EOL; $content .= '// DO NOT EDIT - This file is auto-generated' . PHP_EOL;
$content .= PHP_EOL; $content .= PHP_EOL;
// 告诉 rc.exe 此文件使用 UTF-8 编码,避免中文乱码 // Tell rc.exe this file is UTF-8 encoded to avoid garbled Chinese text
$content .= '#pragma code_page(65001)' . PHP_EOL; $content .= '#pragma code_page(65001)' . PHP_EOL;
$content .= PHP_EOL; $content .= PHP_EOL;
// 包含 Windows 版本信息头文件 // Include the Windows header for version information
$content .= '#include <windows.h>' . PHP_EOL; $content .= '#include <windows.h>' . PHP_EOL;
$content .= PHP_EOL; $content .= PHP_EOL;
// Manifest 资源(Windows 清单文件,如 UAC、DPI 感知等) // Manifest resource (Windows application manifest, e.g. UAC, DPI awareness, etc.)
$manifestPath = $this->getManifestPath(); $manifestPath = $this->getManifestPath();
if ($manifestPath) { if ($manifestPath) {
$manifestPathRc = str_replace('\\', '/', $manifestPath); $manifestPathRc = str_replace('\\', '/', $manifestPath);
@ -126,17 +127,17 @@ class ResourceFileGenerator
$content .= PHP_EOL; $content .= PHP_EOL;
} }
// 图标资源 // Icon resource
$iconPath = $this->getIconPath(); $iconPath = $this->getIconPath();
if ($iconPath) { if ($iconPath) {
// 使用正斜杠,Windows RC 编译器更兼容 // Use forward slashes for better compatibility with the Windows RC compiler
$iconPathRc = str_replace('\\', '/', $iconPath); $iconPathRc = str_replace('\\', '/', $iconPath);
$content .= '// Icon Resource' . PHP_EOL; $content .= '// Icon Resource' . PHP_EOL;
$content .= 'MAINICON ICON "' . addslashes($iconPathRc) . '"' . PHP_EOL; $content .= 'MAINICON ICON "' . addslashes($iconPathRc) . '"' . PHP_EOL;
$content .= PHP_EOL; $content .= PHP_EOL;
} }
// 版本信息 // Version information
$versionInfo = $this->config['version-info'] ?? []; $versionInfo = $this->config['version-info'] ?? [];
if (!empty($versionInfo)) { if (!empty($versionInfo)) {
$content .= $this->generateVersionInfo($versionInfo); $content .= $this->generateVersionInfo($versionInfo);
@ -146,7 +147,7 @@ class ResourceFileGenerator
} }
/** /**
* 生成版本信息块 * Generate the version information block
*/ */
private function generateVersionInfo(array $info): string private function generateVersionInfo(array $info): string
{ {
@ -165,16 +166,17 @@ class ResourceFileGenerator
$content .= 'FILESUBTYPE ' . ($info['file-subtype'] ?? 'VFT2_UNKNOWN') . PHP_EOL; $content .= 'FILESUBTYPE ' . ($info['file-subtype'] ?? 'VFT2_UNKNOWN') . PHP_EOL;
$content .= 'BEGIN' . PHP_EOL; $content .= 'BEGIN' . PHP_EOL;
// StringFileInfo // StringFileInfo block
$content .= ' BLOCK "StringFileInfo"' . PHP_EOL; $content .= ' BLOCK "StringFileInfo"' . PHP_EOL;
$content .= ' BEGIN' . PHP_EOL; $content .= ' BEGIN' . PHP_EOL;
// 语言代码页(040904b0 = 英文/UTF-8,配合 #pragma code_page(65001) 正确显示中文) // Language code page (040904b0 = English/UTF-8, used with #pragma code_page(65001)
// so Chinese text renders correctly)
$langCodepage = $info['lang-codepage'] ?? '040904b0'; $langCodepage = $info['lang-codepage'] ?? '040904b0';
$content .= ' BLOCK "' . $langCodepage . '"' . PHP_EOL; $content .= ' BLOCK "' . $langCodepage . '"' . PHP_EOL;
$content .= ' BEGIN' . PHP_EOL; $content .= ' BEGIN' . PHP_EOL;
// 字符串值 // String values
$stringFields = [ $stringFields = [
'company-name' => 'CompanyName', 'company-name' => 'CompanyName',
'file-description' => 'FileDescription', 'file-description' => 'FileDescription',
@ -195,19 +197,20 @@ class ResourceFileGenerator
} }
} }
// 如果没有设置 FileVersion,从 file-version 字段自动填入 // If FileVersion was not set, it is auto-filled from the file-version field
if (!isset($info['file-version-str']) && $fileVersion) { if (!isset($info['file-version-str']) && $fileVersion) {
// 已在上面通过 file-version 键处理 // Already handled above via the file-version key
} }
$content .= ' END' . PHP_EOL; $content .= ' END' . PHP_EOL;
$content .= ' END' . PHP_EOL; $content .= ' END' . PHP_EOL;
// VarFileInfo // VarFileInfo block
$content .= ' BLOCK "VarFileInfo"' . PHP_EOL; $content .= ' BLOCK "VarFileInfo"' . PHP_EOL;
$content .= ' BEGIN' . PHP_EOL; $content .= ' BEGIN' . PHP_EOL;
// 0x0409 = English(US),1200 = Unicode(UTF-16) // 0x0409 = English(US), 1200 = Unicode(UTF-16)
// 配合 StringFileInfo 中的 040904b0 代码页,确保中文在 UTF-8 源文件中正确编码 // Combined with the 040904b0 code page in StringFileInfo, this ensures
// Chinese text in the UTF-8 source file is encoded correctly
$content .= ' VALUE "Translation", 0x0409, 1200' . PHP_EOL; $content .= ' VALUE "Translation", 0x0409, 1200' . PHP_EOL;
$content .= ' END' . PHP_EOL; $content .= ' END' . PHP_EOL;
@ -217,31 +220,31 @@ class ResourceFileGenerator
} }
/** /**
* 将版本号格式化为逗号分隔的格式(1,0,0,0) * Format a version number into comma-separated form (1,0,0,0).
* 支持以下输入格式: * Supported input formats:
* - "1.0.0.0" → "1,0,0,0" * - "1.0.0.0" → "1,0,0,0"
* - "1,0,0,0" → "1,0,0,0" * - "1,0,0,0" → "1,0,0,0"
* - "v1052" → "1052,0,0,0" (去掉 v 前缀) * - "v1052" → "1052,0,0,0" (strips the leading "v")
* - "1.0" → "1,0,0,0" * - "1.0" → "1,0,0,0"
*/ */
private function formatVersionDots(string $version): string private function formatVersionDots(string $version): string
{ {
// 去掉 v/V 前缀(如 v1052 → 1052) // Strip a leading v/V prefix (e.g. v1052 → 1052)
$version = ltrim($version, 'vV'); $version = ltrim($version, 'vV');
// 如果已经是逗号分隔格式,直接返回 // Return as-is if it is already comma-separated
if (str_contains($version, ',')) { if (str_contains($version, ',')) {
return $version; return $version;
} }
// 用点号分隔 // Split on dots
$parts = explode('.', $version); $parts = explode('.', $version);
// 确保每个部分都是数字(过滤掉非数字字符) // Keep only digits in each part (filter out non-numeric characters)
foreach ($parts as $i => $part) { foreach ($parts as $i => $part) {
$parts[$i] = preg_replace('/[^0-9]/', '', $part) ?: '0'; $parts[$i] = preg_replace('/[^0-9]/', '', $part) ?: '0';
} }
// 确保恰好有4个部分 // Ensure exactly four parts
while (count($parts) < 4) { while (count($parts) < 4) {
$parts[] = '0'; $parts[] = '0';
} }
@ -250,7 +253,7 @@ class ResourceFileGenerator
} }
/** /**
* 生成 resource.h 头文件内容(可选,供 C++ 代码引用资源 ID) * Generate the resource.h header contents (optional, lets C++ code reference resource IDs)
*/ */
public function generateHeader(): string public function generateHeader(): string
{ {

@ -176,10 +176,11 @@ final class LibPhpInstaller
private function currentConfigureOptions(): string private function currentConfigureOptions(): string
{ {
// 优先使用 PHP_BINARY -i 的 Configure Command:输出保留每个参数的 // Prefer the "Configure Command:" output from `PHP_BINARY -i`, which preserves
// 引号,能正确处理 `CFLAGS=-g -O2` 这类含空格的值。而 // the quoting of each argument and can therefore handle values containing spaces
// `php-config --configure-options` 会丢失引号,导致含空格的值被 // such as `CFLAGS=-g -O2`. In contrast, `php-config --configure-options` drops the
// 错误拆分(例如 `-O2` 被当作独立参数传给 configure)。 // quotes, causing space-containing values to be split incorrectly (for example,
// `-O2` being passed to configure as a separate argument).
$info = $this->capture([PHP_BINARY, '-n', '-i']); $info = $this->capture([PHP_BINARY, '-n', '-i']);
if (preg_match('/^Configure Command =>\s*(.+)$/mi', $info, $match)) { if (preg_match('/^Configure Command =>\s*(.+)$/mi', $info, $match)) {
$words = PhpBuildConfiguration::parseShellWords(trim($match[1])); $words = PhpBuildConfiguration::parseShellWords(trim($match[1]));
@ -189,8 +190,9 @@ final class LibPhpInstaller
return implode(' ', array_map('escapeshellarg', $words)); return implode(' ', array_map('escapeshellarg', $words));
} }
// 后备:php-config --configure-options。PPA 的多版本 PHP 共用 // Fallback: php-config --configure-options. On PPA multi-version installations
// /usr 前缀,因此 PHP_HOME=/usr 时必须优先 php-config8.x。 // several PHP versions share the /usr prefix, so when PHP_HOME=/usr the
// versioned php-config8.x must be preferred.
$versionedPhpConfig = $this->sourcePhpDir . '/bin/php-config' $versionedPhpConfig = $this->sourcePhpDir . '/bin/php-config'
. PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION; . PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION;
if ($this->sourcePhpDir !== null && is_executable($versionedPhpConfig)) { if ($this->sourcePhpDir !== null && is_executable($versionedPhpConfig)) {

@ -188,7 +188,7 @@ class Constants
'description' => 'Run the compiled binary after build', 'description' => 'Run the compiled binary after build',
'noValue' => true, 'noValue' => true,
], ],
// 内部开发选项,用于定位特定行的翻译问题,请勿写入用户文档 // Internal development option used to locate translation issues on a specific line. Do not write it into user documentation.
'debug-line' => [ 'debug-line' => [
'longPrefix' => 'debug-line', 'longPrefix' => 'debug-line',
'description' => 'Enable debug line', 'description' => 'Enable debug line',
@ -305,10 +305,10 @@ class Constants
]; ];
/** /**
* MSVC 编译器警告屏蔽列表 * MSVC compiler warning suppression list.
* 这些警告来自 Windows SDK 和 PHP SDK 头文件,都是编译器噪音,不影响功能 * These warnings come from Windows SDK and PHP SDK headers and are compiler noise that does not affect functionality.
* *
* @var array<string, string> 键为警告编号,值为说明 * @var array<string, string> key is the warning number, value is the description
*/ */
public const array MSVC_SUPPRESSED_WARNINGS = [ public const array MSVC_SUPPRESSED_WARNINGS = [
'4244' => '类型转换可能丢失数据 (int -> smaller type)', '4244' => '类型转换可能丢失数据 (int -> smaller type)',

@ -233,8 +233,9 @@ trait FuncCallOptimizer
} }
} }
// 检测参数中使用的变量是否已定义,若变量不存在则回退到动态调用路径 // Check whether the variables used in the arguments are defined; if a variable
// 动态路径中的 parseCallArgs() 会给出明确的错误信息 // does not exist, fall back to the dynamic call path, where parseCallArgs()
// produces a clear error message.
foreach ($expr->args as $arg) { foreach ($expr->args as $arg) {
if (!$arg instanceof Node\Arg) { if (!$arg instanceof Node\Arg) {
continue; continue;
@ -269,7 +270,7 @@ trait FuncCallOptimizer
protected function dispatchFuncCall(string $name, Node\Expr\FuncCall $expr, array $config): string|false protected function dispatchFuncCall(string $name, Node\Expr\FuncCall $expr, array $config): string|false
{ {
// 命名参数 / unpack(...)展开需要运行时处理,回退到动态调用路径 // Named arguments and unpack (...) expansion require runtime handling; fall back to the dynamic call path.
foreach ($expr->args as $arg) { foreach ($expr->args as $arg) {
if ($arg->name !== null || $arg->unpack) { if ($arg->name !== null || $arg->unpack) {
return false; return false;

@ -18,7 +18,7 @@ trait ArrayExpressionTrait
protected function parseArray(Expr\Array_ $node): string protected function parseArray(Expr\Array_ $node): string
{ {
$items = $node->items; $items = $node->items;
// 优化代码风格,空数组直接返回{},否则会产生一些空洞内容 // Optimize code style: return {} directly for an empty array, otherwise it would produce empty entries
if (count($items) === 0) { if (count($items) === 0) {
return Type::ARRAY . '{}'; return Type::ARRAY . '{}';
} }
@ -55,7 +55,7 @@ trait ArrayExpressionTrait
} }
} }
// 存在混合键,则需要拆分为多行插入 // Mixed keys are present, so split the insertion into multiple statements
if ($hasReference or $hasUnpack or $hasVarKey or ($hasNextInsert && $hasKey) or ($hasIntKey and $hasStrKey)) { if ($hasReference or $hasUnpack or $hasVarKey or ($hasNextInsert && $hasKey) or ($hasIntKey and $hasStrKey)) {
return $this->parseArrayMixed($node); return $this->parseArrayMixed($node);
} }
@ -82,7 +82,8 @@ trait ArrayExpressionTrait
} }
/** /**
* 获取包含路径 * Resolve a `$GLOBALS[...]` array-dim fetch to its static slot when the key
* is a known global name, or to a php::global() lookup otherwise.
*/ */
protected function parseGlobalsArrayDimFetch(Expr\ArrayDimFetch $node): string protected function parseGlobalsArrayDimFetch(Expr\ArrayDimFetch $node): string
@ -270,7 +271,7 @@ trait ArrayExpressionTrait
{ {
$tmpVar = $this->genTmpVarName(); $tmpVar = $this->genTmpVarName();
$this->addLocalVar($tmpVar, Type::ARRAY); $this->addLocalVar($tmpVar, Type::ARRAY);
// 释放临时变量,避免修改数组产生数组复制操作 // Release the temporary variable to avoid array copies when the array is modified
$this->context->beforeStmtLines[] = $this->getIndent() . $tmpVar . '.clean();'; $this->context->beforeStmtLines[] = $this->getIndent() . $tmpVar . '.clean();';
$items = $node->items; $items = $node->items;

@ -153,7 +153,7 @@ trait AssignOpTrait
$this->addLocalVar($tmpVar, Type::VAR); $this->addLocalVar($tmpVar, Type::VAR);
} }
// 翻转赋值链 // Reverse the assignment chain
$chain = array_reverse($chain); $chain = array_reverse($chain);
$list = []; $list = [];
@ -525,7 +525,7 @@ trait AssignOpTrait
return $copyAssign; return $copyAssign;
} }
} }
// 类型推断,获取对象的类名,如果不是对象则返回空字符串 // Infer the type and obtain the object's class name; return an empty string for non-objects
$rightClass = $this->detectClassOfExpr($right); $rightClass = $this->detectClassOfExpr($right);
$markNativeObjectNonNull = $this->context->scopeLevel <= 1 $markNativeObjectNonNull = $this->context->scopeLevel <= 1
&& !$this->hasScopeGlobalVar($var) && !$this->hasScopeGlobalVar($var)
@ -548,7 +548,7 @@ trait AssignOpTrait
$this->fatalError($right, "Cannot assign native object `{$rightClass}` to `{$leftClass}`"); $this->fatalError($right, "Cannot assign native object `{$rightClass}` to `{$leftClass}`");
} }
} }
// 右值是一个对象,已获得类的名称,左值必须与右值的类一致 // The right-hand value is an object and its class name is known; the left-hand side must match the right-hand side's class
if ($rightClass) { if ($rightClass) {
if (!$this->hasVar($var)) { if (!$this->hasVar($var)) {
if ($this->isNativeObjectClass($rightClass)) { if ($this->isNativeObjectClass($rightClass)) {
@ -641,7 +641,7 @@ trait AssignOpTrait
} }
} }
} }
// 变量第一次被赋值,确定其类型,由于 PHP 的变量作用域是 function 级的,在 for/while 块中声明的变量,可以在块外使用 // On first assignment the variable's type is determined. PHP variable scope is function-level, so variables declared in for/while blocks remain usable outside the block
if (!$this->hasVar($var)) { if (!$this->hasVar($var)) {
$finalVarType = $this->getNormalAssignType($type); $finalVarType = $this->getNormalAssignType($type);
$finalVarType = $this->isNativeType($finalVarType) ? $this->getNativeType($finalVarType) : $finalVarType; $finalVarType = $this->isNativeType($finalVarType) ? $this->getNativeType($finalVarType) : $finalVarType;
@ -944,9 +944,9 @@ trait AssignOpTrait
} }
/** /**
* $count[$r] -= 1; * $count[$r] -= 1;
* 需要转为下面语句: * must be lowered to the following statements:
* $tmp_var = $count[$r] - 1; * $tmp_var = $count[$r] - 1;
* $count[$r] = $tmp_var;. * $count[$r] = $tmp_var;
*/ */
$isGlobals = $this->isVarExpr($node->var->var) && $node->var->var->name === 'GLOBALS'; $isGlobals = $this->isVarExpr($node->var->var) && $node->var->var->name === 'GLOBALS';
$type = $isGlobals ? Type::VAR : $this->detectVarType($node->var); $type = $isGlobals ? Type::VAR : $this->detectVarType($node->var);

@ -24,7 +24,7 @@ trait BinaryOpTrait
$this->assertExprCanBeUsedAsValue($left, 'binary operand'); $this->assertExprCanBeUsedAsValue($left, 'binary operand');
$this->assertExprCanBeUsedAsValue($right, 'binary operand'); $this->assertExprCanBeUsedAsValue($right, 'binary operand');
// 运算逻辑,优先转为数字 // Arithmetic logic: convert to a numeric type first when possible
$leftExpr = $this->parseOrderedBinaryOperand($left); $leftExpr = $this->parseOrderedBinaryOperand($left);
$rightExpr = $this->parseOrderedBinaryOperand($right); $rightExpr = $this->parseOrderedBinaryOperand($right);
@ -927,7 +927,7 @@ trait BinaryOpTrait
protected function parseCompareExpr(NodeAbstract $expr): string protected function parseCompareExpr(NodeAbstract $expr): string
{ {
$this->assertExprCanBeUsedAsValue($expr, 'comparison operand'); $this->assertExprCanBeUsedAsValue($expr, 'comparison operand');
// PHPX 与 bool 值比较会出现重载错误,所以需要转换成 bool 值 // Comparing PHPX values with bool causes an overload error, so convert them to bool first
if ($this->isScalarBool($expr)) { if ($this->isScalarBool($expr)) {
return $this->getBoolValue($expr); return $this->getBoolValue($expr);
} }

@ -220,8 +220,8 @@ trait ForeachTrait
} }
/** /**
* 为了兼容已有代码,默认不使用原生类型,而是将整数和浮点数作为 php 变量处理 * For backward compatibility, native types are not used by default; integers and floats are treated as php variables.
* 原生 int/float/bool 类型,是不支持自动转换的,例如如果 int 计算超过最大值后,会自动转为 float,除法若不能除尽,则会转为 float * Native int/float/bool types do not support automatic conversion. For example, an int computation that exceeds its maximum value is promoted to float, and a division that does not divide evenly becomes float.
* 某些情况下高性能计算,可能需要使用原生类型,使用 $a = std::int(0) 来显式地使用原生类型 * In some cases high-performance computation may need native types; use `$a = std::int(0)` to explicitly opt into native types.
*/ */
} }

@ -177,7 +177,7 @@ trait FunctionCallTrait
) { ) {
$this->fatalError($expr, 'Native ABI functions cannot be converted to Zend closures'); $this->fatalError($expr, 'Native ABI functions cannot be converted to Zend closures');
} }
// 函数调用占位符,不是真实的函数调用 // Function call placeholder, not a real function call
if (count($expr->args) === 1 and $this->isPlaceholderExpr($expr->args[0])) { if (count($expr->args) === 1 and $this->isPlaceholderExpr($expr->args[0])) {
return $this->genPlaceHolder($this->identifierToStr($expr->name)); return $this->genPlaceHolder($this->identifierToStr($expr->name));
} }
@ -195,7 +195,7 @@ trait FunctionCallTrait
return $this->genPlaceHolder($this->identifierToStr($expr->name)); return $this->genPlaceHolder($this->identifierToStr($expr->name));
} }
} }
// 动态调用的函数,转换函数名为带有命名空间的全限定名称 // For dynamically dispatched functions, convert the function name to its fully qualified name including the namespace
$name = $this->getNamespacedFuncName($name); $name = $this->getNamespacedFuncName($name);
$this->checkInternalFunctionArgCount($name, $expr); $this->checkInternalFunctionArgCount($name, $expr);
$code = $this->parseFuncCallWithOptimizer($name, $expr); $code = $this->parseFuncCallWithOptimizer($name, $expr);

@ -252,7 +252,7 @@ trait MethodCallTrait
} }
$nativeFunc = $this->getNativeMethod($expr, $class, $method); $nativeFunc = $this->getNativeMethod($expr, $class, $method);
// 存在 Native 类,但是没有找到方法,可能是动态调用 // A Native class exists but the method was not found; this may be a dynamic call
if (!$nativeFunc) { if (!$nativeFunc) {
if ($this->hasClass($class) and $this->getNativeMethod($expr, $class, '__call', false)) { if ($this->hasClass($class) and $this->getNativeMethod($expr, $class, '__call', false)) {
throw new DynamicCall(); throw new DynamicCall();
@ -261,7 +261,7 @@ trait MethodCallTrait
$fullMethodName = $this->getOverrideMethodName($class, $method); $fullMethodName = $this->getOverrideMethodName($class, $method);
// 存在子类同名方法,尝试去虚化 // A subclass declares a method with the same name, so try to devirtualize
if ($this->isOverrideMethod($fullMethodName)) { if ($this->isOverrideMethod($fullMethodName)) {
if (!$this->canDevirtualize($object, $class, $method)) { if (!$this->canDevirtualize($object, $class, $method)) {
return false; return false;
@ -414,7 +414,7 @@ trait MethodCallTrait
if (empty($expr->args)) { if (empty($expr->args)) {
return 'this_.call(' . $methodPtr . ')'; return 'this_.call(' . $methodPtr . ')';
} }
// 传入方法名与父类名,以便在按引用参数检测时解析方法签名 // Pass the method name and parent class so the method signature can be resolved when detecting by-reference arguments
return 'this_.call(' . $methodPtr . ', ' . $this->parseCallArgs($expr->args, $method, $parentClass) . ')'; return 'this_.call(' . $methodPtr . ', ' . $this->parseCallArgs($expr->args, $method, $parentClass) . ')';
} }
@ -465,10 +465,10 @@ trait MethodCallTrait
if ($this->isTypedObject($object)) { if ($this->isTypedObject($object)) {
$class = $this->getObjectType($object); $class = $this->getObjectType($object);
} elseif ($object === 'this_') { } elseif ($object === 'this_') {
// $this 在构造函数/方法中静态类型为当前类,便于解析抽象方法等按引用参数签名 // $this is statically typed as the current class inside a constructor/method, so abstract methods and other by-reference parameter signatures can be resolved
$class = $this->classDef !== null ? $this->classDef->getNamespacedName(false) : $this->class; $class = $this->classDef !== null ? $this->classDef->getNamespacedName(false) : $this->class;
} else { } else {
// 接口和抽象类类型的变量没有具体对象类型,仍可从声明签名解析按引用参数。 // Variables of interface or abstract-class type have no concrete object type, but by-reference parameters can still be resolved from the declared signature.
$class = $this->getDeclaredObjectType($object); $class = $this->getDeclaredObjectType($object);
} }
} }
@ -591,7 +591,7 @@ trait MethodCallTrait
. $this->parseCallArgs($expr->args) . ')'; . $this->parseCallArgs($expr->args) . ')';
} }
// 可转为原生调用的 MethodCall // Method calls that can be lowered to a native call
if (($this->isVarExpr($expr->var) || $materializedNativeReceiver) and $this->isNamedMethod($expr->name)) { if (($this->isVarExpr($expr->var) || $materializedNativeReceiver) and $this->isNamedMethod($expr->name)) {
$type = $this->getVarType($object); $type = $this->getVarType($object);
if ($class !== '' && $this->isNativeObjectClass($class)) { if ($class !== '' && $this->isNativeObjectClass($class)) {
@ -647,9 +647,9 @@ trait MethodCallTrait
return self::PREFIX . $nativeFunc . '(' . $receiver . ', ' return self::PREFIX . $nativeFunc . '(' . $receiver . ', '
. $this->parseNativeCallArgs($expr->args, $nativeFunc) . ')'; . $this->parseNativeCallArgs($expr->args, $nativeFunc) . ')';
} }
// 引用参数允许方法调用:有class信息走原生调用,无class信息走动态调用 // Method calls are allowed on references: use a native call when class info is available, otherwise a dynamic call
if (!$this->checkArgType($type, Type::OBJECT) and $type !== Type::REF) { if (!$this->checkArgType($type, Type::OBJECT) and $type !== Type::REF) {
// 非对象类型可使用内置方法 // Non-object types can use built-in methods
$fn = $this->findUniversalMethodAnyType($type, $methodName); $fn = $this->findUniversalMethodAnyType($type, $methodName);
if ($fn) { if ($fn) {
if ($type === Type::STREAM) { if ($type === Type::STREAM) {
@ -705,7 +705,7 @@ trait MethodCallTrait
} }
} }
// 表达式返回值也可使用内置方法:fn()->method(), $obj->fn()->method(), Foo::fn()->method(), $obj->prop->method() // Expression results can also use built-in methods: fn()->method(), $obj->fn()->method(), Foo::fn()->method(), $obj->prop->method()
if (!$this->isVarExpr($expr->var) and $this->isNamedMethod($expr->name)) { if (!$this->isVarExpr($expr->var) and $this->isNamedMethod($expr->name)) {
$type = $this->detectTypeOfExpr($expr->var); $type = $this->detectTypeOfExpr($expr->var);
if ($type === Type::VOID) { if ($type === Type::VOID) {
@ -929,7 +929,7 @@ trait MethodCallTrait
); );
} }
$placeHolder = $this->genArray([Symbol::getCalledClass(), $methodPtr]); $placeHolder = $this->genArray([Symbol::getCalledClass(), $methodPtr]);
// 用于在按引用参数检测时解析方法签名(late static binding 在当前类层级中解析) // Used to resolve the method signature when detecting by-reference arguments (late static binding is resolved within the current class hierarchy)
$rtFunc = $method; $rtFunc = $method;
$rtClass = $this->getFullClassName(); $rtClass = $this->getFullClassName();
} else { } else {
@ -974,7 +974,7 @@ trait MethodCallTrait
} catch (PlaceHolder) { } catch (PlaceHolder) {
return $this->genPlaceHolder($this->genArray($callScope)); return $this->genPlaceHolder($this->genArray($callScope));
} }
// 在方法定义中使用了当前类的方法 self::method(),依然应该传递 this_ 指针 // When a method definition calls a current-class method via self::method(), the this_ pointer must still be passed
if ($this->methodDef and $self) { if ($this->methodDef and $self) {
$object = 'this_'; $object = 'this_';
} else { } else {

@ -595,13 +595,13 @@ trait PropertyAccessTrait
} }
if ($def->class === '' or $this->isAbstractClass($def->class) or $this->isInterface($def->class) or !$this->hasClass($def->class)) { if ($def->class === '' or $this->isAbstractClass($def->class) or $this->isInterface($def->class) or !$this->hasClass($def->class)) {
// 属性 declared class 若是接口、抽象类或动态类,当前属性布局优化无法静态确认最终对象类型。 // If the property's declared class is an interface, abstract class, or dynamic class, the current property layout optimization cannot statically determine the final object type.
// 不在这里 fatal;后续 wrapObjectPropertyAssignTypeCheck() 会在需要时插入运行时检查。 // Do not report a fatal error here; wrapObjectPropertyAssignTypeCheck() inserts a runtime check later when needed.
return; return;
} }
$rightClass = $this->detectClassOfExpr($right); $rightClass = $this->detectClassOfExpr($right);
// TODO 静态编译阶段无法获得准确的类型,需要在运行时检查 // TODO: the exact type cannot be determined at the static compilation stage; a runtime check is required
if ($rightClass === '') { if ($rightClass === '') {
return; return;
} }

@ -38,7 +38,7 @@ trait SwitchTrait
$var_def .= $type . ' ' . $tmp_var . ' = ' . $condExpr . ';' . PHP_EOL; $var_def .= $type . ' ' . $tmp_var . ' = ' . $condExpr . ';' . PHP_EOL;
$var_def .= $this->formatCapturedStmtLines($condAfterStmts); $var_def .= $this->formatCapturedStmtLines($condAfterStmts);
// 保存作用域,switch 可能会解析失败,在这个过程中会增加变量,需重置 // Save the scope; switch parsing may fail partway and add variables in the process, so it must be reset
$localVars = $this->context->localVars; $localVars = $this->context->localVars;
$code = $this->parseBeforeStmtLines() . PHP_EOL; $code = $this->parseBeforeStmtLines() . PHP_EOL;

@ -3,98 +3,98 @@
namespace TypePhp\Platform; namespace TypePhp\Platform;
/** /**
* 平台抽象基类 * Abstract platform base class.
* 定义所有平台必须实现的接口 * Defines the interface every platform must implement.
*/ */
abstract class PlatformBase abstract class PlatformBase
{ {
/** /**
* 获取平台名称 * Get the platform name.
*/ */
abstract public function getName(): string; abstract public function getName(): string;
/** /**
* 判断是否为当前平台 * Determine whether this is the current platform.
*/ */
abstract public function isCurrent(): bool; abstract public function isCurrent(): bool;
/** /**
* 获取编译器包含路径参数 * Get the compiler include-path flags.
*/ */
abstract public function getIncludeFlags(array $includePaths): string; abstract public function getIncludeFlags(array $includePaths): string;
/** /**
* 获取链接器库路径参数 * Get the linker library-path flags.
*/ */
abstract public function getLibraryPathFlags(array $libraryPaths): string; abstract public function getLibraryPathFlags(array $libraryPaths): string;
/** /**
* 获取链接库参数 * Get the link-library flags.
*/ */
abstract public function getLibraryFlags(array $libraries): string; abstract public function getLibraryFlags(array $libraries): string;
/** /**
* 获取文件扩展名 * Get the object file extension.
*/ */
abstract public function getObjectExtension(): string; abstract public function getObjectExtension(): string;
/** /**
* 获取可执行文件扩展名 * Get the executable file extension.
*/ */
abstract public function getExecutableExtension(): string; abstract public function getExecutableExtension(): string;
/** /**
* 获取动态库扩展名 * Get the shared library extension.
*/ */
abstract public function getSharedLibraryExtension(): string; abstract public function getSharedLibraryExtension(): string;
/** /**
* 获取生成共享库所需的链接器选项 * Get the linker options required to produce a shared library.
*/ */
abstract public function getSharedLinkFlag(): string; abstract public function getSharedLinkFlag(): string;
/** /**
* 获取无控制台程序的子系统选项;不适用的平台返回空字符串 * Get the subsystem options for a console-less program; platforms where this does not apply return an empty string.
*/ */
abstract public function getSubsystemOptions(bool $noConsole): string; abstract public function getSubsystemOptions(bool $noConsole): string;
/** /**
* 获取平台 C 运行库链接配置;不适用的平台返回空字符串 * Get the platform C runtime library link configuration; platforms where this does not apply return an empty string.
*/ */
abstract public function getCrtConfig(): string; abstract public function getCrtConfig(): string;
/** /**
* 获取路径分隔符 * Get the path separator.
*/ */
abstract public function getPathSeparator(): string; abstract public function getPathSeparator(): string;
/** /**
* 获取该平台默认使用的 C++ 编译器命令 * Get the default C++ compiler command for this platform.
*/ */
abstract public function getDefaultCompiler(): string; abstract public function getDefaultCompiler(): string;
/** /**
* 获取 PHP 安装目录 * Get the PHP installation directory.
*/ */
abstract public function getPhpDir(): string; abstract public function getPhpDir(): string;
/** /**
* 构建 PHP 包含路径 * Build the PHP include paths.
*/ */
abstract public function buildPhpIncludePaths(string $phpDir): array; abstract public function buildPhpIncludePaths(string $phpDir): array;
/** /**
* 构建 PHP 库路径 * Build the PHP library paths.
*/ */
abstract public function buildPhpLibPaths(string $phpDir): array; abstract public function buildPhpLibPaths(string $phpDir): array;
/** /**
* 检测 PHP 库文件 * Detect the PHP library files.
*/ */
abstract public function detectPhpLibs(string $phpDir): array; abstract public function detectPhpLibs(string $phpDir): array;
/** /**
* 获取指定构建模式的目标文件扩展名 * Get the target file extension for the given build mode.
*/ */
public function getTargetExtension(string $buildMode): string public function getTargetExtension(string $buildMode): string
{ {
@ -104,7 +104,7 @@ abstract class PlatformBase
} }
/** /**
* 获取构建前的运行库检查告警 * Get the runtime library check warnings issued before building.
*/ */
public function getBuildLibraryWarnings( public function getBuildLibraryWarnings(
string $phpDir, string $phpDir,
@ -141,7 +141,7 @@ abstract class PlatformBase
} }
/** /**
* 当前平台是否适合使用 pcntl_fork 并行编译 * Whether this platform is suitable for parallel compilation using pcntl_fork.
*/ */
public function supportsPcntlParallelCompile(): bool public function supportsPcntlParallelCompile(): bool
{ {
@ -154,7 +154,7 @@ abstract class PlatformBase
} }
/** /**
* 规范化路径 * Normalize a path.
*/ */
public function normalizePath(string $path): string public function normalizePath(string $path): string
{ {
@ -162,7 +162,7 @@ abstract class PlatformBase
} }
/** /**
* 组合路径 * Join path components.
*/ */
public function joinPath(string ...$parts): string public function joinPath(string ...$parts): string
{ {
@ -196,15 +196,15 @@ abstract class PlatformBase
} }
/** /**
* 获取默认的 RPATH 路径列表(仅 macOS 需要) * Get the default RPATH path list (only needed on macOS).
* *
* @param string|null $phpxDir phpx 目录路径 * @param string|null $phpxDir phpx directory path
* @param string|null $phpDir PHP 目录路径 * @param string|null $phpDir PHP directory path
* @return array RPATH 路径数组 * @return array RPATH path array
*/ */
public function getDefaultRpaths(?string $phpxDir = null, ?string $phpDir = null): array public function getDefaultRpaths(?string $phpxDir = null, ?string $phpDir = null): array
{ {
// 默认返回空数组,由子类重写 // Return an empty array by default; subclasses may override.
return []; return [];
} }
} }

@ -3,8 +3,8 @@
namespace TypePhp\Platform; namespace TypePhp\Platform;
/** /**
* Unix-like 平台基类(Linux, macOS) * Base class for Unix-like platforms (Linux, macOS).
* 包含 GCC/Clang 通用标志语法的共享实现 * Contains the shared implementation of common GCC/Clang flag syntax.
*/ */
abstract class UnixPlatform extends PlatformBase abstract class UnixPlatform extends PlatformBase
{ {
@ -107,9 +107,10 @@ abstract class UnixPlatform extends PlatformBase
return $phpDir; return $phpDir;
} }
// Ubuntu/PPA 多版本环境下 php8.4 与 php-config8.4 并存,而 // On Ubuntu/PPA multi-version installations, php8.4 and php-config8.4 coexist,
// php-config 可能被 update-alternatives 指向其它版本。优先依据 // while the unversioned php-config may be pointed at another version by
// PHP_BINARY 的版本后缀定位版本化 php-config,避免 ABI 错配。 // update-alternatives. Prefer locating the versioned php-config from the
// version suffix of PHP_BINARY to avoid an ABI mismatch.
$versionedConfig = $this->findVersionedPhpConfig(dirname(realpath(PHP_BINARY) ?: PHP_BINARY)); $versionedConfig = $this->findVersionedPhpConfig(dirname(realpath(PHP_BINARY) ?: PHP_BINARY));
if ($versionedConfig !== null) { if ($versionedConfig !== null) {
$prefix = $this->getPhpConfigValue($versionedConfig, '--prefix'); $prefix = $this->getPhpConfigValue($versionedConfig, '--prefix');
@ -144,7 +145,7 @@ abstract class UnixPlatform extends PlatformBase
} }
/** /**
* 获取 RPATH 选项 * Get the RPATH options.
*/ */
public function getRpathOptions(array $paths): string public function getRpathOptions(array $paths): string
{ {
@ -161,7 +162,7 @@ abstract class UnixPlatform extends PlatformBase
} }
/** /**
* 获取 PIC 选项 * Get the PIC option.
*/ */
public function getPicFlag(): string public function getPicFlag(): string
{ {
@ -169,7 +170,7 @@ abstract class UnixPlatform extends PlatformBase
} }
/** /**
* 构建 PHP 包含路径(使用 php-config 动态获取) * Build the PHP include paths (obtained dynamically via php-config).
*/ */
public function buildPhpIncludePaths(string $phpDir): array public function buildPhpIncludePaths(string $phpDir): array
{ {
@ -209,7 +210,7 @@ abstract class UnixPlatform extends PlatformBase
} }
/** /**
* 查找 php-config 可执行文件 * Locate the php-config executable.
*/ */
protected function findPhpConfig(string $phpDir): ?string protected function findPhpConfig(string $phpDir): ?string
{ {
@ -273,14 +274,14 @@ abstract class UnixPlatform extends PlatformBase
} }
} }
// 依次返回第一个与当前 PHP 主次版本匹配的候选 // Return the first candidate whose major/minor version matches the current PHP.
foreach (array_unique($candidates) as $config) { foreach (array_unique($candidates) as $config) {
if ($this->phpConfigMatchesCurrentPhp($config)) { if ($this->phpConfigMatchesCurrentPhp($config)) {
return $config; return $config;
} }
} }
// 存在候选但版本均不匹配时给出明确错误 // Report a clear error when candidates exist but none matches the version.
if ($candidates !== []) { if ($candidates !== []) {
$this->reportPhpConfigVersionMismatch($candidates[0]); $this->reportPhpConfigVersionMismatch($candidates[0]);
} }
@ -296,7 +297,7 @@ abstract class UnixPlatform extends PlatformBase
} }
/** /**
* 校验 php-config 的主次版本号是否与当前运行的 PHP 一致。 * Verify that php-config's major/minor version matches the currently running PHP.
*/ */
private function phpConfigMatchesCurrentPhp(string $phpConfig): bool private function phpConfigMatchesCurrentPhp(string $phpConfig): bool
{ {
@ -346,7 +347,7 @@ abstract class UnixPlatform extends PlatformBase
} }
/** /**
* 构建 PHP 库路径 * Build the PHP library paths.
*/ */
public function buildPhpLibPaths(string $phpDir): array public function buildPhpLibPaths(string $phpDir): array
{ {
@ -355,7 +356,7 @@ abstract class UnixPlatform extends PlatformBase
} }
/** /**
* 检测 PHP 库文件 * Detect the PHP library files.
*/ */
public function detectPhpLibs(string $phpDir): array public function detectPhpLibs(string $phpDir): array
{ {

@ -3,22 +3,22 @@
namespace TypePhp\Platform; namespace TypePhp\Platform;
/** /**
* Windows 平台实现 * Windows platform implementation.
*/ */
class Windows extends PlatformBase class Windows extends PlatformBase
{ {
/** /**
* PHP 库文件信息 * PHP library file information.
*/ */
private array $phpLibs = []; private array $phpLibs = [];
/** /**
* 是否为 ZTS 模式 * Whether this is a ZTS build.
*/ */
private bool $isZts = false; private bool $isZts = false;
/** /**
* PHP SDK 路径 * PHP SDK path.
*/ */
private string $phpSdkPath = ''; private string $phpSdkPath = '';
@ -137,7 +137,7 @@ class Windows extends PlatformBase
} }
/** /**
* 获取 PHP 库文件列表 * Get the list of PHP library files.
*/ */
public function getPhpLibs(): array public function getPhpLibs(): array
{ {
@ -145,7 +145,7 @@ class Windows extends PlatformBase
} }
/** /**
* 判断是否为 ZTS 模式 * Determine whether this is a ZTS build.
*/ */
public function isZts(): bool public function isZts(): bool
{ {
@ -153,7 +153,7 @@ class Windows extends PlatformBase
} }
/** /**
* 获取 PHP SDK 路径 * Get the PHP SDK path.
*/ */
public function getPhpSdkPath(): string public function getPhpSdkPath(): string
{ {
@ -161,7 +161,7 @@ class Windows extends PlatformBase
} }
/** /**
* 获取 Windows 子系统选项 * Get the Windows subsystem options.
*/ */
public function getSubsystemOptions(bool $noConsole): string public function getSubsystemOptions(bool $noConsole): string
{ {
@ -173,7 +173,7 @@ class Windows extends PlatformBase
} }
/** /**
* 获取 CRT 库配置 * Get the CRT library configuration.
*/ */
public function getCrtConfig(): string public function getCrtConfig(): string
{ {
@ -232,7 +232,7 @@ class Windows extends PlatformBase
} }
/** /**
* 获取调试选项 * Get the debug options.
*/ */
public function getDebugOptions(bool $debugInfo): string public function getDebugOptions(bool $debugInfo): string
{ {
@ -254,7 +254,7 @@ class Windows extends PlatformBase
} }
/** /**
* 构建 PHP SDK 包含路径 * Build the PHP SDK include paths.
*/ */
public function buildPhpSdkIncludePaths(string $phpDir): array public function buildPhpSdkIncludePaths(string $phpDir): array
{ {
@ -265,7 +265,7 @@ class Windows extends PlatformBase
$paths = [$phpSdkInclude]; $paths = [$phpSdkInclude];
// 添加子目录 // Add the subdirectories.
$subDirs = ['main', 'Zend', 'TSRM', 'ext']; $subDirs = ['main', 'Zend', 'TSRM', 'ext'];
foreach ($subDirs as $subDir) { foreach ($subDirs as $subDir) {
$subPath = $phpSdkInclude . '\\' . $subDir; $subPath = $phpSdkInclude . '\\' . $subDir;
@ -278,18 +278,18 @@ class Windows extends PlatformBase
} }
/** /**
* 构建 PHP SDK 库路径 * Build the PHP SDK library paths.
*/ */
public function buildPhpSdkLibPaths(string $phpDir): array public function buildPhpSdkLibPaths(string $phpDir): array
{ {
$paths = []; $paths = [];
// 优先从 SDK/lib 读取 // Prefer reading from SDK/lib.
$phpLib = $phpDir . '\\SDK\\lib'; $phpLib = $phpDir . '\\SDK\\lib';
if (is_dir($phpLib)) { if (is_dir($phpLib)) {
$paths[] = $phpLib; $paths[] = $phpLib;
} else { } else {
// 备选:尝试直接从 lib 目录 // Fallback: try the lib directory directly.
$phpLibAlt = $phpDir . '\\lib'; $phpLibAlt = $phpDir . '\\lib';
if (is_dir($phpLibAlt)) { if (is_dir($phpLibAlt)) {
$paths[] = $phpLibAlt; $paths[] = $phpLibAlt;
@ -300,7 +300,7 @@ class Windows extends PlatformBase
} }
/** /**
* 检测 PHP lib 文件并决定 ZTS/NTS 模式 * Detect the PHP lib files and decide the ZTS/NTS mode.
*/ */
public function detectPhpLibs(string $phpDir): array public function detectPhpLibs(string $phpDir): array
{ {

@ -180,7 +180,7 @@ class Preprocessor extends CompilerBase
$sorter = new StringSort(); $sorter = new StringSort();
$fileDeps = []; $fileDeps = [];
// 构建依赖关系图 // Build the dependency graph
foreach ($this->symbolCallInFile as $file => $symbols) { foreach ($this->symbolCallInFile as $file => $symbols) {
$deps = []; $deps = [];
foreach ($symbols as $symbol) { foreach ($symbols as $symbol) {
@ -198,7 +198,7 @@ class Preprocessor extends CompilerBase
$sortedFiles = $sorter->sort(); $sortedFiles = $sorter->sort();
// 添加未参与依赖管理的文件(非 stub 文件且不在已排序列表中) // Append files that do not participate in dependency management (non-stub files not present in the sorted list)
foreach ($list as $file) { foreach ($list as $file) {
if (!$this->isStubFile($file) and !in_array($file, $sortedFiles)) { if (!$this->isStubFile($file) and !in_array($file, $sortedFiles)) {
$sortedFiles[] = $file; $sortedFiles[] = $file;
@ -237,7 +237,7 @@ class Preprocessor extends CompilerBase
$info = pathinfo($cppFile); $info = pathinfo($cppFile);
$ext = $this->getPlatform()->getObjectExtension(); $ext = $this->getPlatform()->getObjectExtension();
// 保持与 cppFile 相同的路径分隔符 // Keep the same path separator as cppFile
$normalizedFile = str_replace('\\', '/', $cppFile); $normalizedFile = str_replace('\\', '/', $cppFile);
$normalizedMiscDir = str_replace('\\', '/', $this->getPhpxDir() . '/src/misc/'); $normalizedMiscDir = str_replace('\\', '/', $this->getPhpxDir() . '/src/misc/');
if (str_starts_with($normalizedFile, $normalizedMiscDir)) { if (str_starts_with($normalizedFile, $normalizedMiscDir)) {
@ -719,7 +719,7 @@ class Preprocessor extends CompilerBase
foreach ($functionCalls as $call) { foreach ($functionCalls as $call) {
if ($call->name instanceof Node\Name) { if ($call->name instanceof Node\Name) {
// 内置函数不参与依赖管理 // Internal functions do not participate in dependency management
$funcName = strtolower($call->name->toString()); $funcName = strtolower($call->name->toString());
if (!$this->isInternalFunction($funcName)) { if (!$this->isInternalFunction($funcName)) {
$this->symbolCallInFile[$this->file][] = $funcName; $this->symbolCallInFile[$this->file][] = $funcName;
@ -741,7 +741,7 @@ class Preprocessor extends CompilerBase
} }
} }
} }
// 依赖去重 // Deduplicate dependencies
$this->symbolCallInFile[$this->file] = array_unique($this->symbolCallInFile[$this->file]); $this->symbolCallInFile[$this->file] = array_unique($this->symbolCallInFile[$this->file]);
} }
@ -846,7 +846,7 @@ class Preprocessor extends CompilerBase
if ($this->stubFile && $this->stubImportLibrary === '' && !$param->type) { if ($this->stubFile && $this->stubImportLibrary === '' && !$param->type) {
throw new \RuntimeException('No type for ' . $phpName); throw new \RuntimeException('No type for ' . $phpName);
} }
// 构造方法属性定义语法(Constructor Property Promotion) // Constructor property promotion syntax
if ($param->isPromoted()) { if ($param->isPromoted()) {
if (!$this->classDef or !$this->methodDef or $this->methodDef->name !== '__construct') { if (!$this->classDef or !$this->methodDef or $this->methodDef->name !== '__construct') {
$this->fatalError($param, 'Promoted properties are not supported'); $this->fatalError($param, 'Promoted properties are not supported');
@ -910,7 +910,7 @@ class Preprocessor extends CompilerBase
$this->lowerArgumentDefault($param, $argInfo); $this->lowerArgumentDefault($param, $argInfo);
} }
} elseif ($param->variadic) { } elseif ($param->variadic) {
// 变长参数可以视为空数组默认值 // A variadic parameter can be treated as an empty-array default value
$argInfo->default = '{}'; $argInfo->default = '{}';
$argInfo->defaultValue = new Node\Expr\Array_(); $argInfo->defaultValue = new Node\Expr\Array_();
} }
@ -960,7 +960,7 @@ class Preprocessor extends CompilerBase
// Local stubs define C++ native functions and require an explicit ABI return type. // Local stubs define C++ native functions and require an explicit ABI return type.
// Generated external stubs may preserve an untyped PHP declaration as php::Var. // Generated external stubs may preserve an untyped PHP declaration as php::Var.
if ($this->stubFile && $this->stubImportLibrary === '' && !$v->returnType) { if ($this->stubFile && $this->stubImportLibrary === '' && !$v->returnType) {
// 以下魔术方法都不能声明返回值类型 __construct()/__destruct()/__clone() // The following magic methods must not declare a return type: __construct()/__destruct()/__clone()
if (($this->method and !in_array($this->method, ['__construct', '__destruct', '__clone'])) or !$this->method) { if (($this->method and !in_array($this->method, ['__construct', '__destruct', '__clone'])) or !$this->method) {
$name = $this->class ? $this->class . '::' . $v->name : $v->name; $name = $this->class ? $this->class . '::' . $v->name : $v->name;
$this->fatalError($v, 'The return type of the function `' . $name . '` must be specified'); $this->fatalError($v, 'The return type of the function `' . $name . '` must be specified');
@ -997,7 +997,7 @@ class Preprocessor extends CompilerBase
if ($nullableNativeReturn !== null) { if ($nullableNativeReturn !== null) {
[$returnType, $class] = $nullableNativeReturn; [$returnType, $class] = $nullableNativeReturn;
} }
// 构造、析构、克隆方法不能有返回值 // Constructor, destructor, and clone methods cannot have a return value
if ($this->method and in_array($this->method, ['__construct', '__destruct', '__clone'])) { if ($this->method and in_array($this->method, ['__construct', '__destruct', '__clone'])) {
$returnType = Type::VOID; $returnType = Type::VOID;
} }
@ -1077,7 +1077,7 @@ class Preprocessor extends CompilerBase
$this->fatalError($v, 'Zend-backed constructors cannot accept or return native objects'); $this->fatalError($v, 'Zend-backed constructors cannot accept or return native objects');
} }
// main 函数,返回值必须为 void 类型,参数必须为空或者 argc, argv 两个参数 // The main function must return void and take either no parameters or the two parameters argc and argv
if (!$this->class and !$this->namespace and $fnName === self::ENTRY_FUNCTION) { if (!$this->class and !$this->namespace and $fnName === self::ENTRY_FUNCTION) {
if (count($v->params) > 0) { if (count($v->params) > 0) {
if (count($v->params) != 2) { if (count($v->params) != 2) {
@ -1158,7 +1158,7 @@ class Preprocessor extends CompilerBase
} }
$this->fatalError($v, "Duplicate function `{$name}`"); $this->fatalError($v, "Duplicate function `{$name}`");
} }
// 禁止重定义内置函数 // Forbid redefining built-in functions
if (!$this->methodDef and $this->isInternalFunction($name)) { if (!$this->methodDef and $this->isInternalFunction($name)) {
$this->fatalError($v, "The function `{$name}` is a built-in function and cannot be redefined"); $this->fatalError($v, "The function `{$name}` is a built-in function and cannot be redefined");
} }
@ -1241,7 +1241,7 @@ class Preprocessor extends CompilerBase
$this->symbolCallInFile[$this->file][] = $parentClassLower; $this->symbolCallInFile[$this->file][] = $parentClassLower;
} }
$this->classDef->extends = $this->parentClass; $this->classDef->extends = $this->parentClass;
// 是否继承了内置类 // Whether it inherits from an internal class
$this->classDef->inheritedFromInternalClass = $this->isInternalClass($parentClassLower); $this->classDef->inheritedFromInternalClass = $this->isInternalClass($parentClassLower);
} }
@ -2146,11 +2146,11 @@ class Preprocessor extends CompilerBase
$fullMethodNameLower = strtolower($fullMethodName); $fullMethodNameLower = strtolower($fullMethodName);
$fullClassNameLower = strtolower($fullClassName); $fullClassNameLower = strtolower($fullClassName);
// 检查子类是否已覆盖此方法(子类先于父类被预处理的情况) // Check whether a subclass already overrides this method (when the subclass is preprocessed before the parent)
$isOverridden = $this->isMethodOverriddenInSubClasses($fullClassNameLower, $this->method); $isOverridden = $this->isMethodOverriddenInSubClasses($fullClassNameLower, $this->method);
$this->classMethodOverride[$fullMethodNameLower] = $isOverridden; $this->classMethodOverride[$fullMethodNameLower] = $isOverridden;
// 查找父类是否有同名方法,递归向上标记父类方法已被覆盖 // Find whether a parent class has a method with the same name, and recursively mark the parent method as overridden
while (($parentClass = $this->symbols->parent($fullClassNameLower)) !== '') { while (($parentClass = $this->symbols->parent($fullClassNameLower)) !== '') {
$parentMethodLower = strtolower($parentClass . '::' . $this->method); $parentMethodLower = strtolower($parentClass . '::' . $this->method);
if (isset($this->classMethodOverride[$parentMethodLower])) { if (isset($this->classMethodOverride[$parentMethodLower])) {
@ -2182,7 +2182,7 @@ class Preprocessor extends CompilerBase
} }
/** /**
* 递归检查所有子类(及子类的子类)是否已定义了同名方法,用于处理子类先于父类被预处理的情况。 * Recursively check whether any subclass (and its subclasses) has defined a method with the same name; handles the case where a subclass is preprocessed before its parent.
*/ */
private function isMethodOverriddenInSubClasses(string $classNameLower, string $method): bool private function isMethodOverriddenInSubClasses(string $classNameLower, string $method): bool
{ {
@ -2400,7 +2400,7 @@ class Preprocessor extends CompilerBase
// use THello1, THello2 { // use THello1, THello2 {
// hello as hello3; // hello as hello3;
// } // }
// 未指定 trait,将添加所有 trait 的别名映射,在预处理阶段无法获取 trait 的方法列表 // No trait specified: add alias mappings for all traits, since the trait's method list is unavailable during preprocessing
$traits = $traitUse->traits; $traits = $traitUse->traits;
} else { } else {
$traits[] = $adaptation->trait; $traits[] = $adaptation->trait;
@ -2409,9 +2409,9 @@ class Preprocessor extends CompilerBase
$traitName = $this->getNamespacedClassName($this->parseIdentifier($trait)); $traitName = $this->getNamespacedClassName($this->parseIdentifier($trait));
$methodName = $adaptation->method->toString(); $methodName = $adaptation->method->toString();
/* /*
* 例如: * For example:
* use TraitA { TraitA::method as newMethod} * use TraitA { TraitA::method as newMethod}
* 这表示 TraitA::method() 会被重命名为 TraitA::newMethod() * This means TraitA::method() is renamed to TraitA::newMethod()
*/ */
$aliases[$this->getFullMethodName($traitName, $methodName)][] = [ $aliases[$this->getFullMethodName($traitName, $methodName)][] = [
'newName' => $adaptation->newName ? $adaptation->newName->toString() : $methodName, 'newName' => $adaptation->newName ? $adaptation->newName->toString() : $methodName,
@ -2425,9 +2425,9 @@ class Preprocessor extends CompilerBase
} }
$methodName = $adaptation->method->toString(); $methodName = $adaptation->method->toString();
/* /*
* 例如: * For example:
* use TraitA { TraitA::method insteadof TraitB} * use TraitA { TraitA::method insteadof TraitB}
* 这表示 TraitB::method() 将会被忽略,真正执行的是 TraitA::method() * This means TraitB::method() is ignored, and TraitA::method() is actually executed
*/ */
foreach ($adaptation->insteadof as $trait2) { foreach ($adaptation->insteadof as $trait2) {
$traitName = $this->getNamespacedClassName($this->parseIdentifier($trait2)); $traitName = $this->getNamespacedClassName($this->parseIdentifier($trait2));

@ -5,8 +5,8 @@ namespace TypePhp\PythonTools\Converter;
use RuntimeException; use RuntimeException;
/** /**
* 非 final:测试可子类化注入预制 AST 或模拟解析失败, * Intentionally non-final so tests can subclass it to inject a canned AST or simulate a parse failure;
* 见 phpunit/src/PythonTools/PythonAstLoaderTest.php。 * see phpunit/src/PythonTools/PythonAstLoaderTest.php.
*/ */
class PythonAstLoader class PythonAstLoader
{ {

@ -15,7 +15,7 @@ final class PythonToTypePhpConverter
/** @var array<string, true> */ /** @var array<string, true> */
private array $definedFunctions = []; private array $definedFunctions = [];
/** @var array<string, true> 被装饰的函数:调用点必须经变量间接调用装饰结果 */ /** @var array<string, true> Decorated functions: call sites must invoke the decorator result indirectly through a variable */
private array $decoratedFunctions = []; private array $decoratedFunctions = [];
/** @var array<string, true> */ /** @var array<string, true> */
@ -53,12 +53,12 @@ final class PythonToTypePhpConverter
foreach ($tree['body'] ?? [] as $node) { foreach ($tree['body'] ?? [] as $node) {
if (in_array($node['_type'] ?? '', ['Assign', 'AnnAssign', 'AugAssign'], true)) { if (in_array($node['_type'] ?? '', ['Assign', 'AnnAssign', 'AugAssign'], true)) {
// 纯注解声明没有运行期值,不登记为模块全局变量 // An annotation-only declaration has no runtime value, so it is not registered as a module global.
$annotationOnly = ($node['_type'] ?? '') === 'AnnAssign' && ($node['value'] ?? null) === null; $annotationOnly = ($node['_type'] ?? '') === 'AnnAssign' && ($node['value'] ?? null) === null;
if (!$annotationOnly) { if (!$annotationOnly) {
$targets = ($node['_type'] ?? '') === 'Assign' ? ($node['targets'] ?? []) : [$node['target'] ?? []]; $targets = ($node['_type'] ?? '') === 'Assign' ? ($node['targets'] ?? []) : [$node['target'] ?? []];
foreach ($targets as $target) { foreach ($targets as $target) {
// 解构赋值展开为其中的名称元素 // A destructuring assignment expands into its individual name elements.
$elements = in_array($target['_type'] ?? '', ['Tuple', 'List'], true) $elements = in_array($target['_type'] ?? '', ['Tuple', 'List'], true)
? ($target['elts'] ?? []) ? ($target['elts'] ?? [])
: [$target]; : [$target];
@ -77,7 +77,7 @@ final class PythonToTypePhpConverter
$name = (string) $node['name']; $name = (string) $node['name'];
$this->definedFunctions[$name] = true; $this->definedFunctions[$name] = true;
if (($node['decorator_list'] ?? []) !== []) { if (($node['decorator_list'] ?? []) !== []) {
// 装饰结果绑定到模块级变量,函数内调用需要 global 注入 // The decorator result is bound to a module-level variable, so calls inside functions need a global injection.
$this->decoratedFunctions[$name] = true; $this->decoratedFunctions[$name] = true;
$this->moduleGlobals[$name] = true; $this->moduleGlobals[$name] = true;
} }
@ -108,7 +108,7 @@ final class PythonToTypePhpConverter
if ($this->moduleGlobals !== []) { if ($this->moduleGlobals !== []) {
$lines[] = $this->line('global ' . implode(', ', $this->variables(array_keys($this->moduleGlobals))) . ';'); $lines[] = $this->line('global ' . implode(', ', $this->variables(array_keys($this->moduleGlobals))) . ';');
} }
// 装饰器重绑定先于其他顶层语句执行,使后续调用拿到装饰结果 // Decorator rebinding runs before other top-level statements so that subsequent calls observe the decorated result.
foreach ($functions as $function) { foreach ($functions as $function) {
foreach ($this->decoratorRebindings($function) as $rebinding) { foreach ($this->decoratorRebindings($function) as $rebinding) {
$lines[] = $this->line($rebinding); $lines[] = $this->line($rebinding);
@ -202,7 +202,7 @@ final class PythonToTypePhpConverter
} }
/** /**
* Python 的 main 函数与 TypePHP 入口点冲突,重命名为 main_。 * Python's main function conflicts with the TypePHP entry point, so it is renamed to main_.
*/ */
private function functionName(string $name): string private function functionName(string $name): string
{ {
@ -210,8 +210,8 @@ final class PythonToTypePhpConverter
} }
/** /**
* 生成装饰器的重绑定语句(Python 自底向上应用装饰器)。 * Generate the rebinding statements for a function's decorators (Python applies decorators bottom-up).
* 装饰结果存入同名模块变量,调用点经变量间接调用。 * The decorated result is stored in a module variable of the same name, and call sites invoke it indirectly through that variable.
* *
* @param array<string, mixed> $function @return list<string> * @param array<string, mixed> $function @return list<string>
*/ */
@ -233,7 +233,7 @@ final class PythonToTypePhpConverter
/** @param array<string, mixed> $node */ /** @param array<string, mixed> $node */
private function decoratorCallable(array $node): string private function decoratorCallable(array $node): string
{ {
// @dec(args):装饰器工厂,先求值再调用其返回值 // @dec(args): a decorator factory; evaluate it first, then call its return value.
if (($node['_type'] ?? '') === 'Call') { if (($node['_type'] ?? '') === 'Call') {
return $this->call($node); return $this->call($node);
} }
@ -312,7 +312,7 @@ final class PythonToTypePhpConverter
$parts[] = match ($element['_type'] ?? '') { $parts[] = match ($element['_type'] ?? '') {
'Name' => $this->variable((string) $element['id']), 'Name' => $this->variable((string) $element['id']),
'Attribute', 'Subscript' => $this->target($element), 'Attribute', 'Subscript' => $this->target($element),
// PHP 的 list 赋值不支持展开,嵌套元组的元素仍是 PyObject 无法直接解构 // PHP list assignment does not support spreading, and nested tuple elements remain PyObject and cannot be destructured directly.
'Starred' => $this->unsupported($owner, 'starred destructuring is not supported'), 'Starred' => $this->unsupported($owner, 'starred destructuring is not supported'),
default => $this->unsupported($owner, 'nested destructuring is not supported'), default => $this->unsupported($owner, 'nested destructuring is not supported'),
}; };
@ -337,7 +337,7 @@ final class PythonToTypePhpConverter
{ {
$target = $this->target($node['target']); $target = $this->target($node['target']);
$operator = $node['op']['_type'] ?? ''; $operator = $node['op']['_type'] ?? '';
// PHP 没有 //= 与 @=,展开为对应的运算符函数调用 // PHP has no //= or @= operators, so these expand into the corresponding operator function calls.
if ($operator === 'FloorDiv' || $operator === 'MatMult') { if ($operator === 'FloorDiv' || $operator === 'MatMult') {
$function = $operator === 'FloorDiv' ? 'python\\operator\\floordiv' : 'python\\operator\\matmul'; $function = $operator === 'FloorDiv' ? 'python\\operator\\floordiv' : 'python\\operator\\matmul';
return [$this->line($target . ' = ' . $function . '(' . $target . ', ' . $this->expression($node['value']) . ');')]; return [$this->line($target . ' = ' . $function . '(' . $target . ', ' . $this->expression($node['value']) . ');')];
@ -530,7 +530,7 @@ final class PythonToTypePhpConverter
{ {
$targets = []; $targets = [];
$walk = function (array $target) use (&$walk, &$targets, $node): void { $walk = function (array $target) use (&$walk, &$targets, $node): void {
// del (a, b) / del [a, b] 逐项展开 // del (a, b) / del [a, b] expands element by element.
if (in_array($target['_type'] ?? '', ['Tuple', 'List'], true)) { if (in_array($target['_type'] ?? '', ['Tuple', 'List'], true)) {
foreach ($target['elts'] ?? [] as $element) { foreach ($target['elts'] ?? [] as $element) {
$walk($element); $walk($element);
@ -585,7 +585,7 @@ final class PythonToTypePhpConverter
if (($function['_type'] ?? '') === 'Name') { if (($function['_type'] ?? '') === 'Name') {
$name = (string) $function['id']; $name = (string) $function['id'];
if (isset($this->decoratedFunctions[$name])) { if (isset($this->decoratedFunctions[$name])) {
// 装饰结果绑定在同名变量上,必须经变量间接调用 // The decorator result is bound to a variable of the same name, so it must be invoked indirectly through that variable.
$callable = $this->variable($name); $callable = $this->variable($name);
} elseif (isset($this->importedSymbols[$name])) { } elseif (isset($this->importedSymbols[$name])) {
$symbol = $this->importedSymbols[$name]; $symbol = $this->importedSymbols[$name];

@ -201,7 +201,7 @@ trait MagicMethodDetector
} }
} }
// 重建 params 字符串,使 C++ 函数签名使用 auto-fill 后的类型 // Rebuild the params string so the C++ function signature uses the auto-filled types
$list = []; $list = [];
foreach ($fnDef->argInfoList as $argInfo) { foreach ($fnDef->argInfoList as $argInfo) {
if ($argInfo->variadic) { if ($argInfo->variadic) {

@ -69,12 +69,12 @@ trait NameResolutionTrait
} }
/** /**
* 将 trait 方法参数中的类名 Name 节点升级为 Name\FullyQualified。 * Upgrade the class-name Name node in a trait method parameter to Name\FullyQualified.
* 对于已由 parseTypeDecl() 解析的限定名(含 \),直接升级节点类型; * For qualified names (containing \) already resolved by parseTypeDecl(), upgrade the node type directly;
* 对于尚未解析的非限定名(如 NullableType 内层,parseTypeDecl 返回 TYPE_VAR 跳过了解析), * for unresolved unqualified names (such as the inner type of a NullableType, which parseTypeDecl skips by returning TYPE_VAR),
* 先通过 useAliases/useNamespaces 解析再升级。 * resolve them via useAliases/useNamespaces first and then upgrade.
* gen_stub.php 的 SimpleType::fromNode() 依赖 isFullyQualified() 判断是否需要再次解析, * gen_stub.php's SimpleType::fromNode() relies on isFullyQualified() to decide whether to re-resolve;
* 若不升级为 FullyQualified,在上下文丢失后会被错误地追加当前 namespace 前缀。 * if the name is not upgraded to FullyQualified, the current namespace prefix is wrongly appended once the context is lost.
*/ */
protected function upgradeToFullyQualifiedName(?NodeAbstract $type): ?NodeAbstract protected function upgradeToFullyQualifiedName(?NodeAbstract $type): ?NodeAbstract
{ {
@ -130,7 +130,7 @@ trait NameResolutionTrait
} }
/** /**
* 函数名称处理,补齐 namespace * Process the function name and prepend the namespace when required.
*/ */
public function getNamespacedFuncName(string $funcName): string public function getNamespacedFuncName(string $funcName): string
{ {
@ -144,7 +144,7 @@ trait NameResolutionTrait
} }
/** /**
* @param string $class 一定是带有命名空间的完整类名 * @param string $class must be a fully qualified class name including the namespace
*/ */
protected function resolveTypeDecl(?NodeAbstract $type, int $what): array protected function resolveTypeDecl(?NodeAbstract $type, int $what): array
{ {
@ -155,17 +155,17 @@ trait NameResolutionTrait
protected function parseTypeDecl(?NodeAbstract $type, int $what, string &$class): string protected function parseTypeDecl(?NodeAbstract $type, int $what, string &$class): string
{ {
// 未定义类型视为 var (mixed, any) // An undefined type is treated as var (mixed, any)
if ($type === null) { if ($type === null) {
return Type::VAR; return Type::VAR;
} }
if ($type instanceof UnionType || $type instanceof NullableType || $type instanceof IntersectionType) { if ($type instanceof UnionType || $type instanceof NullableType || $type instanceof IntersectionType) {
// 复杂类型静态阶段统一按 mixed/var 处理,运行时再由 typeCheck 兜底。 // Complex types are uniformly treated as mixed/var at the static stage; the runtime typeCheck provides the fallback.
return Type::VAR; return Type::VAR;
} else { } else {
$typeName = $this->parseIdentifier($type); $typeName = $this->parseIdentifier($type);
$typeNameLower = strtolower($typeName); $typeNameLower = strtolower($typeName);
// 属性和类常量的类型不能声明为 void/never ,只有返回值可以 // Property and class-constant types cannot be declared void/never; only return types can
if ($what !== self::DECL_TYPE_OF_RETURN and ($typeNameLower === 'void' or $typeNameLower === 'never')) { if ($what !== self::DECL_TYPE_OF_RETURN and ($typeNameLower === 'void' or $typeNameLower === 'never')) {
$this->fatalError($type, 'The type `void`/`never` is allowed only for return type'); $this->fatalError($type, 'The type `void`/`never` is allowed only for return type');
} elseif (isset($this->zendTypeMap[$typeNameLower])) { } elseif (isset($this->zendTypeMap[$typeNameLower])) {
@ -179,12 +179,12 @@ trait NameResolutionTrait
} }
$class = $this->classDef->extends; $class = $this->classDef->extends;
} elseif ($typeName === 'static') { } elseif ($typeName === 'static') {
// static 类无法在编译期获取 // The static class cannot be determined at compile time
$class = ''; $class = '';
} else { } else {
$class = $this->getNamespacedClassName($typeName); $class = $this->getNamespacedClassName($typeName);
} }
// Trait 在注入 class 需要使用完整类名 // When a trait is injected into a class, the fully qualified class name is required
if ($class and $this->classDef and $this->classDef->trait) { if ($class and $this->classDef and $this->classDef->trait) {
$type->name = $class; $type->name = $class;
} }

@ -70,7 +70,7 @@ final class PropertyAccessResolver
while (true) { while (true) {
$classDef = $this->compiler->getClassDef($findClass); $classDef = $this->compiler->getClassDef($findClass);
if ($classDef === null) { if ($classDef === null) {
// 非编译单元内的类:尝试按内置类的声明属性解析(offset 缓存) // Class outside the compilation unit: attempt to resolve it by the internal class's declared property (offset cache)
return $this->resolveInternalClassProperty($expr, $property, $findClass, $class, $scope, $static); return $this->resolveInternalClassProperty($expr, $property, $findClass, $class, $scope, $static);
} }
@ -160,11 +160,11 @@ final class PropertyAccessResolver
} }
/** /**
* 解析 PHP 内置类的声明属性,使其可以进入稳定属性 offset 缓存。 * Resolve the declared property of a PHP internal class so it can enter the stable property offset cache.
* *
* 仅处理反射可见的声明属性:动态属性、魔术属性(__get/__set)反射不可见, * Only declared properties visible to reflection are handled: dynamic properties and magic properties (__get/__set) are not visible to reflection,
* 返回 null 回退到按名字符串查找路径。内置类在 MINIT 注册、进程级存活, * so return null to fall back to the by-name string lookup path. Internal classes are registered at MINIT and live for the whole process,
* 其声明属性的 offset 终身不变,缓存安全。 * so the offset of their declared properties never changes and caching is safe.
*/ */
private function resolveInternalClassProperty( private function resolveInternalClassProperty(
NodeAbstract $expr, NodeAbstract $expr,
@ -182,7 +182,7 @@ final class PropertyAccessResolver
return null; return null;
} }
$propRef = $ref->getProperty($property); $propRef = $ref->getProperty($property);
// PHP 8.4 属性钩子必须由引擎调用,offset 直读会绕过钩子,回退字符串路径 // PHP 8.4 property hooks must be invoked by the engine; reading the offset directly would bypass the hook, so fall back to the string path
if ($propRef->hasHooks()) { if ($propRef->hasHooks()) {
return null; return null;
} }
@ -209,8 +209,8 @@ final class PropertyAccessResolver
$this->fatal($expr, "Cannot access private property `{$property}` of class `{$displayClass}`"); $this->fatal($expr, "Cannot access private property `{$property}` of class `{$displayClass}`");
} }
// 复合类型(union/intersection)的运行时检查结构依赖 AST 构建, // The runtime check structure for composite types (union/intersection) depends on the AST,
// 无法从反射便捷还原,回退字符串路径以保证类型安全 // which cannot be conveniently reconstructed from reflection, so fall back to the string path to preserve type safety
$propType = $propRef->getType(); $propType = $propRef->getType();
if ($propType !== null && !$propType instanceof \ReflectionNamedType) { if ($propType !== null && !$propType instanceof \ReflectionNamedType) {
return null; return null;

@ -188,8 +188,8 @@ class Reflection
} }
/** /**
* 当参数索引超出声明范围时,尝试将最后一个参数作为变长参数(...$rest)获取。 * When the parameter index exceeds the declared range, try to obtain the last parameter as a variadic parameter (...$rest).
* 返回变长参数对象或 null。 * Returns the variadic parameter object, or null.
*/ */
public static function getVariadicParameter(string $funcName, string $className = ''): ?\ReflectionParameter public static function getVariadicParameter(string $funcName, string $className = ''): ?\ReflectionParameter
{ {

@ -9,12 +9,13 @@ use TypePhp\Entity\InterfaceDef;
final class SymbolRepository final class SymbolRepository
{ {
/** /**
* 存储所有函数、类方法的定义,key 是 native name,命名空间需要转为 `_`,并且必须为小写 * Stores the definitions of all functions and class methods. The key is the native name:
* the namespace must be converted to `_` and the result must be lowercase.
* @var array<string, FunctionDef> * @var array<string, FunctionDef>
*/ */
private array $functions = []; private array $functions = [];
/** /**
* key 类名,包含命名空间 * Keyed by class name, including the namespace.
* @var array<string, ClassDef> * @var array<string, ClassDef>
*/ */
private array $classes = []; private array $classes = [];

@ -80,7 +80,7 @@ class Translator extends Preprocessor
protected array $argInfoHeaderFiles = []; protected array $argInfoHeaderFiles = [];
protected array $registerSymbols = []; protected array $registerSymbols = [];
// Windows 资源文件配置(图标、版本信息等) // Windows resource file configuration (icon, version info, etc.)
protected array $resourceConfig = []; protected array $resourceConfig = [];
protected array $globalHeaders = [ protected array $globalHeaders = [
'cstring', 'cstring',
@ -112,8 +112,9 @@ class Translator extends Preprocessor
$this->preprocessArgvAdvanced(); $this->preprocessArgvAdvanced();
$this->climate->arguments->parse(); $this->climate->arguments->parse();
// 只读取命令行参数,不立即应用(等待 YAML 解析后再应用) // Only read the command-line arguments here; do not apply them yet
// 这样可以确保优先级:命令行 > YAML > 默认值 // (they are applied after YAML parsing). This preserves the priority:
// command line > YAML > defaults.
$this->internalFunctions = []; $this->internalFunctions = [];
foreach (get_defined_functions()['internal'] as $functionName) { foreach (get_defined_functions()['internal'] as $functionName) {
$function = Reflection::getFunction($functionName); $function = Reflection::getFunction($functionName);
@ -133,13 +134,13 @@ class Translator extends Preprocessor
exit(0); exit(0);
} }
// 提前处理 --no-color,确保后续所有输出均为无颜色模式 // Handle --no-color early so all subsequent output is colorless.
if ($this->climate->arguments->defined('no-color')) { if ($this->climate->arguments->defined('no-color')) {
$this->climate->forceAnsiOff(); $this->climate->forceAnsiOff();
} }
// 检测操作系统、编译器以及 Windows 平台的 PHP lib 文件 // Detect the OS, the compiler, and (on Windows) the PHP lib files.
$this->detectPlatform(); $this->detectPlatform();
} }
@ -152,7 +153,8 @@ class Translator extends Preprocessor
$constants = []; $constants = [];
foreach ($groups as $groupName => $group) { foreach ($groups as $groupName => $group) {
// 编译器进程中的用户常量属于被编译程序的运行时状态,不能在静态阶段展开。 // User constants in the compiler process belong to the compiled
// program's runtime state and must not be expanded in the static phase.
if (strcasecmp((string) $groupName, 'user') === 0 if (strcasecmp((string) $groupName, 'user') === 0
|| Reflection::isTypePhpExtension($groupName) || Reflection::isTypePhpExtension($groupName)
|| !is_array($group)) { || !is_array($group)) {
@ -166,7 +168,7 @@ class Translator extends Preprocessor
} }
/** /**
* 检测操作系统、编译器以及 Windows 平台的 PHP lib 文件 * Detect the OS, the compiler, and (on Windows) the PHP lib files.
*/ */
protected function detectPlatform(): void protected function detectPlatform(): void
{ {
@ -295,43 +297,45 @@ class Translator extends Preprocessor
} }
/** /**
* 应用命令行参数(在 YAML 解析后调用,确保命令行参数优先级最高) * Apply command-line arguments (called after YAML parsing so command-line
* arguments take the highest priority).
*/ */
protected function applyCommandLineArguments(): void protected function applyCommandLineArguments(): void
{ {
$this->applyPhpVersionCommandLineArgument(); $this->applyPhpVersionCommandLineArgument();
// 优化级别 // Optimization level
if ($this->climate->arguments->defined('optimize')) { if ($this->climate->arguments->defined('optimize')) {
$this->optimizeLevel = $this->climate->arguments->get('optimize'); $this->optimizeLevel = $this->climate->arguments->get('optimize');
} }
// 构建模式 // Build mode
if ($this->climate->arguments->defined('mode')) { if ($this->climate->arguments->defined('mode')) {
$this->setBuildMode($this->climate->arguments->get('mode')); $this->setBuildMode($this->climate->arguments->get('mode'));
} }
// 调试行号 // Debug line number
if ($this->climate->arguments->defined('debug-line')) { if ($this->climate->arguments->defined('debug-line')) {
$this->debugLine = intval($this->climate->arguments->get('debug-line')); $this->debugLine = intval($this->climate->arguments->get('debug-line'));
} }
// 最大并行任务数 // Maximum number of parallel jobs
if ($this->climate->arguments->defined('job')) { if ($this->climate->arguments->defined('job')) {
$this->maxJob = intval($this->climate->arguments->get('job')); $this->maxJob = intval($this->climate->arguments->get('job'));
} }
// 调试模式 // Debug mode
if ($this->climate->arguments->defined('debug')) { if ($this->climate->arguments->defined('debug')) {
$this->debug = true; $this->debug = true;
} }
// 禁用字面量字符串优化 // Disable literal string optimization
if ($this->climate->arguments->defined('no-literal-strings')) { if ($this->climate->arguments->defined('no-literal-strings')) {
$this->noLiteralStrings = true; $this->noLiteralStrings = true;
} }
// 启用性能分析(需强制重编译 misc 文件以确保 PPROF_ON 宏生效,仅 Linux 支持) // Enable profiling (forces recompilation of misc files so the PPROF_ON
// macro takes effect; Linux only)
if ($this->climate->arguments->defined('profile')) { if ($this->climate->arguments->defined('profile')) {
if (!$this->isLinux()) { if (!$this->isLinux()) {
$this->climate->error('--profile is only supported on Linux (requires gperftools)'); $this->climate->error('--profile is only supported on Linux (requires gperftools)');
@ -340,12 +344,12 @@ class Translator extends Preprocessor
$this->enableProfiler = true; $this->enableProfiler = true;
} }
// 禁用进度条 // Disable the progress bar
if ($this->climate->arguments->defined('no-progress')) { if ($this->climate->arguments->defined('no-progress')) {
$this->noProgress = true; $this->noProgress = true;
} }
// 隐藏控制台窗口 // Hide the console window
if ($this->climate->arguments->defined('no-console')) { if ($this->climate->arguments->defined('no-console')) {
$this->noConsole = true; $this->noConsole = true;
} }
@ -355,27 +359,27 @@ class Translator extends Preprocessor
$this->sanitize = $this->climate->arguments->get('sanitize'); $this->sanitize = $this->climate->arguments->get('sanitize');
} }
// C++ 标准版本 // C++ standard version
if ($this->climate->arguments->defined('cxx-std')) { if ($this->climate->arguments->defined('cxx-std')) {
$this->cxxStd = $this->climate->arguments->get('cxx-std'); $this->cxxStd = $this->climate->arguments->get('cxx-std');
} }
// 目标 CPU 指令集 // Target CPU instruction set
if ($this->climate->arguments->defined('march')) { if ($this->climate->arguments->defined('march')) {
$this->march = $this->climate->arguments->get('march'); $this->march = $this->climate->arguments->get('march');
} }
// 交叉编译目标平台 // Cross-compilation target platform
if ($this->climate->arguments->defined('target-platform')) { if ($this->climate->arguments->defined('target-platform')) {
$this->targetPlatform = $this->climate->arguments->get('target-platform'); $this->targetPlatform = $this->climate->arguments->get('target-platform');
} }
// 输出文件名/路径 // Output file name/path
if ($this->climate->arguments->defined('output')) { if ($this->climate->arguments->defined('output')) {
$this->setOutputPath($this->climate->arguments->get('output')); $this->setOutputPath($this->climate->arguments->get('output'));
} }
// 构建目录 // Build directory
if ($this->climate->arguments->defined('build-dir')) { if ($this->climate->arguments->defined('build-dir')) {
$buildDir = $this->climate->arguments->get('build-dir'); $buildDir = $this->climate->arguments->get('build-dir');
if (!empty($buildDir)) { if (!empty($buildDir)) {
@ -383,35 +387,39 @@ class Translator extends Preprocessor
} }
} }
// 干运行模式 // Dry-run mode
if ($this->climate->arguments->defined('dry')) { if ($this->climate->arguments->defined('dry')) {
$this->dryRun = true; $this->dryRun = true;
} }
// 用户自定义 C++ include 路径(直接从 argv 解析以支持多值) // User-defined C++ include paths (parsed directly from argv to support
// multiple values)
if ($this->hasRepeatableArgvFlag(['-I', '--include-path'])) { if ($this->hasRepeatableArgvFlag(['-I', '--include-path'])) {
$this->userIncludePaths = $this->parseRepeatableArgv(['-I', '--include-path']); $this->userIncludePaths = $this->parseRepeatableArgv(['-I', '--include-path']);
} }
// 用户自定义预处理器宏(直接从 argv 解析以支持多值) // User-defined preprocessor macros (parsed directly from argv to support
// multiple values)
if ($this->hasRepeatableArgvFlag(['-D', '--define'])) { if ($this->hasRepeatableArgvFlag(['-D', '--define'])) {
$this->userDefines = $this->parseRepeatableArgv(['-D', '--define']); $this->userDefines = $this->parseRepeatableArgv(['-D', '--define']);
} }
// 链接时优化 // Link-time optimization
if ($this->climate->arguments->defined('lto')) { if ($this->climate->arguments->defined('lto')) {
$this->enableLto = true; $this->enableLto = true;
} }
// clang-format 代码格式化(默认关闭,需显式 --format 开启) // clang-format code formatting (disabled by default; requires explicit --format)
if ($this->climate->arguments->defined('format')) { if ($this->climate->arguments->defined('format')) {
$this->enableCodeFormattingIfAvailable('--format'); $this->enableCodeFormattingIfAvailable('--format');
} }
// 用户自定义链接库(直接从 argv 解析以支持多值) // User-defined link libraries (parsed directly from argv to support
// multiple values)
if ($this->hasRepeatableArgvFlag(['-l', '--link-lib'])) { if ($this->hasRepeatableArgvFlag(['-l', '--link-lib'])) {
$this->linkLibs = $this->parseRepeatableArgv(['-l', '--link-lib']); $this->linkLibs = $this->parseRepeatableArgv(['-l', '--link-lib']);
} }
// 用户自定义库搜索路径(直接从 argv 解析以支持多值) // User-defined library search paths (parsed directly from argv to support
// multiple values)
if ($this->hasRepeatableArgvFlag(['-L', '--link-path'])) { if ($this->hasRepeatableArgvFlag(['-L', '--link-path'])) {
$this->linkPaths = $this->parseRepeatableArgv(['-L', '--link-path']); $this->linkPaths = $this->parseRepeatableArgv(['-L', '--link-path']);
} }
@ -426,25 +434,26 @@ class Translator extends Preprocessor
} }
/** /**
* 从原始 $argv 中解析可重复参数,支持 -X val 和 --long val 两种形式。 * Parse repeatable arguments from the raw $argv, supporting both the
* CLImate 的 multiple 选项只能保留最后一个值,因此需要手动解析。 * "-X val" and "--long val" forms. CLImate's "multiple" option only keeps
* the last value, so these must be parsed manually.
* *
* @param string[] $flags 要匹配的标志列表,如 ['-I', '--include-path'] * @param string[] $flags Flags to match, e.g. ['-I', '--include-path']
* @return string[] 收集到的所有值 * @return string[] All collected values
*/ */
protected function parseRepeatableArgv(array $flags): array protected function parseRepeatableArgv(array $flags): array
{ {
global $argv; global $argv;
$values = []; $values = [];
for ($i = 1; $i < count($argv); $i++) { for ($i = 1; $i < count($argv); $i++) {
// 精确匹配标志(如 -I, --include-path) // Exact flag match (e.g. -I, --include-path)
if (in_array($argv[$i], $flags, true) && isset($argv[$i + 1]) && $argv[$i + 1] !== '' && $argv[$i + 1][0] !== '-') { if (in_array($argv[$i], $flags, true) && isset($argv[$i + 1]) && $argv[$i + 1] !== '' && $argv[$i + 1][0] !== '-') {
$values[] = $argv[$i + 1]; $values[] = $argv[$i + 1];
$i++; // 跳过值 $i++; // Skip the value
} }
// 合并形式:-I/path 或 --include-path=/path // Combined form: -I/path or --include-path=/path
elseif (!$this->isLongFlagWithEquals($argv[$i], $flags, $values)) { elseif (!$this->isLongFlagWithEquals($argv[$i], $flags, $values)) {
// 检查短标志合并:-I/path // Check short-flag combined form: -I/path
foreach ($flags as $flag) { foreach ($flags as $flag) {
if (strlen($flag) === 2 && $flag[0] === '-') { if (strlen($flag) === 2 && $flag[0] === '-') {
$short = substr($flag, 1); $short = substr($flag, 1);
@ -485,7 +494,7 @@ class Translator extends Preprocessor
} }
/** /**
* 处理 --flag=value 格式的长标志 * Handle long flags in the --flag=value form.
*/ */
private function isLongFlagWithEquals(string $arg, array $flags, array &$values): bool private function isLongFlagWithEquals(string $arg, array $flags, array &$values): bool
{ {
@ -551,7 +560,8 @@ class Translator extends Preprocessor
} else { } else {
$this->save($cppCode, $cppFile); $this->save($cppCode, $cppFile);
} }
// 生成 stub 文件,依赖 convert 阶段的 use 等信息 // Generate the stub file, which depends on the use statements
// and other info collected during the convert phase.
$this->genStubFile($this->file); $this->genStubFile($this->file);
return $cppCode === '' ? null : $cppFile; return $cppCode === '' ? null : $cppFile;
} catch (Redo $e) { } catch (Redo $e) {
@ -583,8 +593,8 @@ class Translator extends Preprocessor
} }
/** /**
* 初始化新的 Platform 和 Backend 抽象层 * Initialize the new Platform and Backend abstraction layers.
* 这是一个渐进式迁移,保持向后兼容 * This is an incremental migration that preserves backward compatibility.
*/ */
protected function initializeNewArchitecture(): void protected function initializeNewArchitecture(): void
{ {
@ -592,7 +602,7 @@ class Translator extends Preprocessor
$platform = $this->platform ?? PlatformFactory::create(); $platform = $this->platform ?? PlatformFactory::create();
$this->platform = $platform; $this->platform = $platform;
// 自动检测平台和编译器 // Auto-detect the platform and compiler
$result = CompilerFactory::autoDetect($this->cppCompiler, $platform); $result = CompilerFactory::autoDetect($this->cppCompiler, $platform);
$this->platform = $result['platform']; $this->platform = $result['platform'];
$this->compilerBackend = $result['compiler']; $this->compilerBackend = $result['compiler'];
@ -601,7 +611,7 @@ class Translator extends Preprocessor
"Initialized new architecture: {$this->platform->getName()} + {$this->compilerBackend->getName()}" "Initialized new architecture: {$this->platform->getName()} + {$this->compilerBackend->getName()}"
); );
} catch (\Exception $e) { } catch (\Exception $e) {
// 如果初始化失败,回退到旧逻辑 // Fall back to the legacy logic if initialization fails
$this->climate->warning( $this->climate->warning(
"Failed to initialize new architecture: {$e->getMessage()}. Using legacy mode." "Failed to initialize new architecture: {$e->getMessage()}. Using legacy mode."
); );
@ -611,14 +621,14 @@ class Translator extends Preprocessor
} }
/** /**
* 设置 C++ 编译器(从配置文件读取) * Set the C++ compiler (read from the config file).
*/ */
public function setCppCompiler(string $compiler): void public function setCppCompiler(string $compiler): void
{ {
$this->cppCompiler = $compiler; $this->cppCompiler = $compiler;
$this->climate->info("Using compiler from config: {$this->cppCompiler}"); $this->climate->info("Using compiler from config: {$this->cppCompiler}");
// 重新初始化 Backend // Re-initialize the Backend
$this->initializeNewArchitecture(); $this->initializeNewArchitecture();
} }
@ -641,7 +651,8 @@ class Translator extends Preprocessor
public function setTargetName(string $name): void public function setTargetName(string $name): void
{ {
// 如果指定了路径(包含目录分隔符),提取目录和文件名 // If a path was given (contains a directory separator), split it into
// directory and file name.
if (str_contains($name, '/') || str_contains($name, '\\')) { if (str_contains($name, '/') || str_contains($name, '\\')) {
$this->outputDir = dirname($name); $this->outputDir = dirname($name);
$name = basename($name); $name = basename($name);
@ -874,7 +885,7 @@ class Translator extends Preprocessor
} }
$code .= "// class entry \n"; $code .= "// class entry \n";
// 确保数组大小至少为 1,避免 C/C++ 编译错误 // Ensure the array has at least one element to avoid C/C++ compile errors.
$code .= 'static THREAD_LOCAL zend_class_entry *' . self::PREFIX . self::CLASS_MAP . '[' . max(1, count($this->classMap)) . '];' . PHP_EOL; $code .= 'static THREAD_LOCAL zend_class_entry *' . self::PREFIX . self::CLASS_MAP . '[' . max(1, count($this->classMap)) . '];' . PHP_EOL;
// Internal/compiled symbols have module lifetime. They are initialized // Internal/compiled symbols have module lifetime. They are initialized
// lazily after PHP startup, so disable_functions/disable_classes have // lazily after PHP startup, so disable_functions/disable_classes have
@ -888,7 +899,8 @@ class Translator extends Preprocessor
$code .= $this->genPythonModuleStorage(); $code .= $this->genPythonModuleStorage();
$code .= "// property \n"; $code .= "// property \n";
// 无动态 propMap:属性 offset 缓存仅覆盖编译类/内置类的声明属性(见 getPropertyId) // No dynamic propMap: the property offset cache only covers declared
// properties of compiled/built-in classes (see getPropertyId).
$code .= 'static php::PersistentCacheSlot<uint32_t> ' . self::PREFIX . self::PERSISTENT_PROP_MAP . '[' . max(1, count($this->persistentPropMap)) . ']{};' . PHP_EOL; $code .= 'static php::PersistentCacheSlot<uint32_t> ' . self::PREFIX . self::PERSISTENT_PROP_MAP . '[' . max(1, count($this->persistentPropMap)) . ']{};' . PHP_EOL;
$code .= "// functions \n"; $code .= "// functions \n";
@ -1331,8 +1343,10 @@ CODE;
} }
/** /**
* 检查 phpx/src/misc/ 下的源文件是否已有有效缓存,始终生效(除非指定 --force)。 * Check whether source files under phpx/src/misc/ have a valid cache, always
* 缓存必须匹配编译命令和 PHP ABI,且 .o 文件必须不早于源文件和 phpx 头文件。 * effective (unless --force is specified). The cache must match the compile
* command and the PHP ABI, and the .o file must not be older than the source
* files and phpx headers.
*/ */
public function hasMiscObjectFileCache(string $cppFile): bool public function hasMiscObjectFileCache(string $cppFile): bool
{ {
@ -1427,7 +1441,7 @@ CODE;
} }
/** /**
* 判断文件是否为 C++ 源文件 * Determine whether a file is a C++ source file.
*/ */
protected function isCppFile(string $filePath): bool protected function isCppFile(string $filePath): bool
{ {
@ -1436,10 +1450,10 @@ CODE;
} }
/** /**
* 根据文件扩展名获取语言类型标识(用于 -x 参数). * Get the language type identifier from the file extension (used for the -x flag).
* *
* @return string|null 语言标识(c, assembler, objective-c, objective-c++), * @return string|null Language identifier (c, assembler, objective-c, objective-c++),
* 或 null 表示使用默认检测(C++ 文件) * or null to use the default detection (C++ files).
*/ */
protected function getLanguageFromExtension(string $filePath): ?string protected function getLanguageFromExtension(string $filePath): ?string
{ {
@ -1455,7 +1469,7 @@ CODE;
} }
/** /**
* 判断文件是否为原生编译型源文件(C/C++/汇编/ObjC 等). * Determine whether a file is a natively compiled source file (C/C++/asm/ObjC, etc.).
*/ */
protected function isNativeSourceFile(string $filePath): bool protected function isNativeSourceFile(string $filePath): bool
{ {
@ -1521,7 +1535,7 @@ CODE;
{ {
$job = $this->maxJob; $job = $this->maxJob;
// embed 需要 main 函数,以及 cli 的内置函数定义 // The embed build needs the main function and the CLI's built-in function definitions.
if ($this->isBuildModeEmbed()) { if ($this->isBuildModeEmbed()) {
$runtimeSource = $this->getPhpxDir() . '/src/misc/typephp_runtime.cc'; $runtimeSource = $this->getPhpxDir() . '/src/misc/typephp_runtime.cc';
// PHPX 2.6.3 keeps the common runtime in typephp_main.cc. Newer // PHPX 2.6.3 keeps the common runtime in typephp_main.cc. Newer
@ -1540,14 +1554,14 @@ CODE;
$this->preparePhpXPrecompiledHeader(); $this->preparePhpXPrecompiledHeader();
// Windows 平台:编译资源文件(图标、版本信息等) // Windows: compile the resource file (icon, version info, etc.)
$this->compileResourceFile(); $this->compileResourceFile();
if (!$this->getPlatform()->supportsPcntlParallelCompile() or $job <= 1) { if (!$this->getPlatform()->supportsPcntlParallelCompile() or $job <= 1) {
return $this->compileSourceFile($sourceFiles); return $this->compileSourceFile($sourceFiles);
} }
// Unix/Linux/macOS 使用 pcntl 并行编译 // Unix/Linux/macOS compile in parallel using pcntl
return $this->compileWithPcntl($sourceFiles, $job); return $this->compileWithPcntl($sourceFiles, $job);
} }
@ -1637,7 +1651,7 @@ CODE;
} }
/** /**
* Unix/Linux/macOS 平台并行编译(使用 pcntl) * Parallel compilation on Unix/Linux/macOS (using pcntl).
*/ */
protected function pcntlWait(?int &$status): int protected function pcntlWait(?int &$status): int
{ {
@ -1755,7 +1769,7 @@ CODE;
{ {
$targetFile = $this->getTargetFileName(); $targetFile = $this->getTargetFileName();
// Windows 平台:将 .res 资源文件加入链接 // Windows: add the .res resource file to the link
if ($this->isWindows() && $this->hasResourceFile()) { if ($this->isWindows() && $this->hasResourceFile()) {
$resFile = $this->getResourceResFile(); $resFile = $this->getResourceResFile();
if (file_exists($resFile)) { if (file_exists($resFile)) {
@ -2558,7 +2572,7 @@ CODE;
$this->sanitize = (string) $sanitize; $this->sanitize = (string) $sanitize;
} }
// 读取 cxx-flags // Read cxx-flags
$cxxFlags = $cfg['cxx-flags'] ?? null; $cxxFlags = $cfg['cxx-flags'] ?? null;
if (!empty($cxxFlags)) { if (!empty($cxxFlags)) {
if (is_array($cxxFlags)) { if (is_array($cxxFlags)) {
@ -2568,25 +2582,25 @@ CODE;
} }
} }
// 读取 cxx-std // Read cxx-std
$cxxStd = $cfg['cxx-std'] ?? null; $cxxStd = $cfg['cxx-std'] ?? null;
if (!empty($cxxStd)) { if (!empty($cxxStd)) {
$this->cxxStd = $cxxStd; $this->cxxStd = $cxxStd;
} }
// 读取 march(目标 CPU 指令集) // Read march (target CPU instruction set)
$march = $cfg['march'] ?? null; $march = $cfg['march'] ?? null;
if (!empty($march)) { if (!empty($march)) {
$this->march = $march; $this->march = $march;
} }
// 读取 target-platform // Read target-platform
$targetPlatform = $cfg['target-platform'] ?? null; $targetPlatform = $cfg['target-platform'] ?? null;
if (!empty($targetPlatform)) { if (!empty($targetPlatform)) {
$this->targetPlatform = (string) $targetPlatform; $this->targetPlatform = (string) $targetPlatform;
} }
// 读取 build-dir // Read build-dir
$buildDir = $cfg['build-dir'] ?? null; $buildDir = $cfg['build-dir'] ?? null;
if (!empty($buildDir)) { if (!empty($buildDir)) {
$this->setBuildDir($this->resolvePath((string) $buildDir, $projectDir, 'Build path')); $this->setBuildDir($this->resolvePath((string) $buildDir, $projectDir, 'Build path'));
@ -2596,7 +2610,7 @@ CODE;
$this->dryRun = true; $this->dryRun = true;
} }
// 读取 ld-flags // Read ld-flags
$ldflags = $cfg['ld-flags'] ?? null; $ldflags = $cfg['ld-flags'] ?? null;
if (!empty($ldflags)) { if (!empty($ldflags)) {
if (is_array($ldflags)) { if (is_array($ldflags)) {
@ -2606,7 +2620,7 @@ CODE;
} }
} }
// 读取 include-paths // Read include-paths
$includePaths = $cfg['include-paths'] ?? null; $includePaths = $cfg['include-paths'] ?? null;
if (!empty($includePaths) && is_array($includePaths)) { if (!empty($includePaths) && is_array($includePaths)) {
foreach ($includePaths as $includePath) { foreach ($includePaths as $includePath) {
@ -2614,7 +2628,7 @@ CODE;
} }
} }
// 读取 defines // Read defines
$defines = $cfg['defines'] ?? null; $defines = $cfg['defines'] ?? null;
if (!empty($defines) && is_array($defines)) { if (!empty($defines) && is_array($defines)) {
foreach ($defines as $define) { foreach ($defines as $define) {
@ -2622,17 +2636,17 @@ CODE;
} }
} }
// 读取 lto // Read lto
if (!empty($cfg['lto'])) { if (!empty($cfg['lto'])) {
$this->enableLto = true; $this->enableLto = true;
} }
// 读取 format // Read format
if (!empty($cfg['format'])) { if (!empty($cfg['format'])) {
$this->enableCodeFormattingIfAvailable('YAML format'); $this->enableCodeFormattingIfAvailable('YAML format');
} }
// 读取 link-libs // Read link-libs
$linkLibs = $cfg['link-libs'] ?? null; $linkLibs = $cfg['link-libs'] ?? null;
if (!empty($linkLibs) && is_array($linkLibs)) { if (!empty($linkLibs) && is_array($linkLibs)) {
foreach ($linkLibs as $lib) { foreach ($linkLibs as $lib) {
@ -2640,7 +2654,7 @@ CODE;
} }
} }
// 读取 link-paths // Read link-paths
$linkPaths = $cfg['link-paths'] ?? null; $linkPaths = $cfg['link-paths'] ?? null;
if (!empty($linkPaths) && is_array($linkPaths)) { if (!empty($linkPaths) && is_array($linkPaths)) {
foreach ($linkPaths as $linkPath) { foreach ($linkPaths as $linkPath) {
@ -2675,7 +2689,8 @@ CODE;
} }
} }
// 读取 output/name。name 只表示目标名,不能按 YAML 目录解析成输出路径。 // Read output/name. `name` only denotes the target name; it must not be
// resolved against the YAML directory as an output path.
$output = $cfg['output'] ?? null; $output = $cfg['output'] ?? null;
if (!empty($output)) { if (!empty($output)) {
$this->setOutputPath($this->resolvePath((string) $output, $projectDir, 'Output path')); $this->setOutputPath($this->resolvePath((string) $output, $projectDir, 'Output path'));
@ -2683,19 +2698,19 @@ CODE;
$this->setTargetName((string) $cfg['name']); $this->setTargetName((string) $cfg['name']);
} }
// 读取 cpp-compiler // Read cpp-compiler
$cppCompiler = $cfg['cpp-compiler'] ?? null; $cppCompiler = $cfg['cpp-compiler'] ?? null;
if (!empty($cppCompiler)) { if (!empty($cppCompiler)) {
$this->setCppCompiler($cppCompiler); $this->setCppCompiler($cppCompiler);
} }
// 读取 mode/type/build-mode(支持 CLI/YAML 两套命名) // Read mode/type/build-mode (supports both the CLI and YAML naming)
$buildMode = $cfg['mode'] ?? $cfg['build-mode'] ?? $cfg['type'] ?? null; $buildMode = $cfg['mode'] ?? $cfg['build-mode'] ?? $cfg['type'] ?? null;
if (!empty($buildMode)) { if (!empty($buildMode)) {
$this->setBuildMode((string) $buildMode); $this->setBuildMode((string) $buildMode);
} }
// 读取 ignore(支持中横线和下划线) // Read ignore (supports both hyphen and underscore)
$ignore = $cfg['ignore'] ?? null; $ignore = $cfg['ignore'] ?? null;
if (!empty($ignore)) { if (!empty($ignore)) {
if (!is_array($ignore)) { if (!is_array($ignore)) {
@ -2713,13 +2728,13 @@ CODE;
} }
} }
// 读取 resource(Windows 资源配置:图标、版本信息) // Read resource (Windows resource config: icon, version info)
$resource = $cfg['resource'] ?? null; $resource = $cfg['resource'] ?? null;
if (!empty($resource)) { if (!empty($resource)) {
if (!is_array($resource)) { if (!is_array($resource)) {
$this->error('`resource` must be array'); $this->error('`resource` must be array');
} }
// 验证图标文件是否存在 // Verify that the icon file exists
if (!empty($resource['icon'])) { if (!empty($resource['icon'])) {
$iconPath = $resource['icon']; $iconPath = $resource['icon'];
if (!preg_match('/^[A-Za-z]:\\|^\//', $iconPath)) { if (!preg_match('/^[A-Za-z]:\\|^\//', $iconPath)) {
@ -2733,7 +2748,7 @@ CODE;
$this->resourceConfig['_projectDir'] = $projectDir; $this->resourceConfig['_projectDir'] = $projectDir;
} }
// 读取 manifest(Windows 清单文件,与 resource 同级,缺省不携带) // Read manifest (Windows manifest file, same level as resource, omitted by default)
$manifest = $cfg['manifest'] ?? null; $manifest = $cfg['manifest'] ?? null;
if (!empty($manifest)) { if (!empty($manifest)) {
if (!is_string($manifest)) { if (!is_string($manifest)) {
@ -2893,7 +2908,7 @@ CODE;
foreach ($interfaceDef->extendsList ?: ($interfaceDef->extends ? [$interfaceDef->extends] : []) as $parent) { foreach ($interfaceDef->extendsList ?: ($interfaceDef->extends ? [$interfaceDef->extends] : []) as $parent) {
$tmpCe = self::PREFIX . 'class_entry_' . $this->escapeCeName($parent); $tmpCe = self::PREFIX . 'class_entry_' . $this->escapeCeName($parent);
// 不存在的接口,说明可能是内置接口 // A non-existent interface is likely a built-in interface
if (!$this->hasInterface($parent)) { if (!$this->hasInterface($parent)) {
$sorter->add($tmpCe); $sorter->add($tmpCe);
} }
@ -2917,7 +2932,7 @@ CODE;
$deps = []; $deps = [];
$parent = $classDef->extends; $parent = $classDef->extends;
if ($parent) { if ($parent) {
// 不存在的父类,说明可能是内置类 // A non-existent parent is likely a built-in class
$tmpCe = $this->getParentClassCe($classDef); $tmpCe = $this->getParentClassCe($classDef);
if (!$this->hasClass($parent)) { if (!$this->hasClass($parent)) {
$sorter->add($tmpCe); $sorter->add($tmpCe);
@ -3567,8 +3582,9 @@ CODE;
); );
} }
// 如果不是继承自内置类,需要检查父类是否存在,在预处理阶段只需检查了是否继承内置类 // When not inheriting from a built-in class, verify the parent exists.
// 目前不允许继承自动态加载的自定义类 // The preprocess phase only checks whether a built-in class is inherited.
// Currently, inheriting from an autoloaded custom class is not allowed.
if ($this->classDef->extends and !$this->classDef->inheritedFromInternalClass) { if ($this->classDef->extends and !$this->classDef->inheritedFromInternalClass) {
$parentClass = $this->getNamespacedClassName($this->parseIdentifier($class->extends)); $parentClass = $this->getNamespacedClassName($this->parseIdentifier($class->extends));
if ($this->hasClass($parentClass)) { if ($this->hasClass($parentClass)) {
@ -3579,7 +3595,7 @@ CODE;
'Native and ZendVM-backed classes cannot inherit from each other' 'Native and ZendVM-backed classes cannot inherit from each other'
); );
} }
// 父类是 final 无法继承 // The parent class is final and cannot be extended
if ($parent->flags & Modifiers::FINAL) { if ($parent->flags & Modifiers::FINAL) {
$this->fatalError($class, "Class `{$this->class}` cannot extend final class `{$parentClass}`"); $this->fatalError($class, "Class `{$this->class}` cannot extend final class `{$parentClass}`");
} }
@ -3787,7 +3803,9 @@ CODE;
$isEntryFunction = $this->hasFunction(self::ENTRY_FUNCTION) $isEntryFunction = $this->hasFunction(self::ENTRY_FUNCTION)
&& $functionDef === $this->getFunction(self::ENTRY_FUNCTION); && $functionDef === $this->getFunction(self::ENTRY_FUNCTION);
if ($this->isBuildModeBin() && $isEntryFunction) { if ($this->isBuildModeBin() && $isEntryFunction) {
// $_SERVER 的初始化必须置于 main 入口函数内,以确保在其被访问前,运行环境及超全局上下文已完全就绪。 // $_SERVER initialization must live inside the main entry function so
// the runtime environment and superglobal context are fully ready
// before it is accessed.
$cppCode .= $this->registerServerEnvironment($functionDef->sourceFile); $cppCode .= $this->registerServerEnvironment($functionDef->sourceFile);
} }
$callParams = $functionDef->argInfoList ? rtrim($callParams, ',') : ''; $callParams = $functionDef->argInfoList ? rtrim($callParams, ',') : '';
@ -3817,8 +3835,10 @@ CODE;
private function registerServerEnvironment(string $entryFile): string private function registerServerEnvironment(string $entryFile): string
{ {
/** /**
* 对于常驻内存型应用,执行完当前逻辑后,会立即进入长时间的事件循环等待。 * For long-running (resident) applications, control enters a long-lived
* 因此,这些变量仅作为临时用途,用完后应即刻销毁,无需长期持有。 * event loop immediately after the current logic finishes. These
* variables are therefore only temporary and should be destroyed as soon
* as they are used, rather than held for the long term.
*/ */
$indent = $this->getIndent(); $indent = $this->getIndent();
$cppCode = $indent . "const char *value = " . $this->genCharPtr($entryFile, true) . ';' . PHP_EOL; $cppCode = $indent . "const char *value = " . $this->genCharPtr($entryFile, true) . ';' . PHP_EOL;
@ -3870,7 +3890,7 @@ CODE;
{ {
$cppCode = ''; $cppCode = '';
// 接口没有方法实体 // Interfaces have no method bodies
if ($classDef instanceof ClassDef && $classDef->trait === null) { if ($classDef instanceof ClassDef && $classDef->trait === null) {
if ($classDef->nativeObject) { if ($classDef->nativeObject) {
return ''; return '';
@ -3927,14 +3947,15 @@ CODE;
} }
$this->functionDef = $this->getFunction($name); $this->functionDef = $this->getFunction($name);
// 类方法不要保存到 functions 中 // Class methods are not stored in `functions`
if ($this->methodDef) { if ($this->methodDef) {
$this->methodDef->functionDef = $this->functionDef; $this->methodDef->functionDef = $this->functionDef;
} else { } else {
$this->functionDefineInFile[$name] = $this->functionDef; $this->functionDefineInFile[$name] = $this->functionDef;
} }
// stub 函数,没有函数的具体实现,只有声明,实现在 C++ 或者 .so 中定义 // Stub functions have no concrete implementation, only a declaration;
// the implementation is defined in C++ or a .so file.
if ($this->functionDef->stub) { if ($this->functionDef->stub) {
$this->resetFunction(); $this->resetFunction();
return ''; return '';
@ -4062,7 +4083,7 @@ CODE;
$code .= $preamble . PHP_EOL; $code .= $preamble . PHP_EOL;
} }
$this->indentLevel--; $this->indentLevel--;
// 构建 PHP 级别的函数名用于 debug backtrace // Build the PHP-level function name for debug backtraces
if ($this->class) { if ($this->class) {
$debugName = $this->class . '::' . $this->function; $debugName = $this->class . '::' . $this->function;
} else { } else {
@ -4103,7 +4124,8 @@ CODE;
} }
/** /**
* 检查父类方法是否可以被重写,私有方法不能被重写,方法签名必须兼容 * Check whether a parent method can be overridden: private methods cannot be
* overridden, and the signature must be compatible.
*/ */
protected function checkParentMethodCanBeOverridden(Node\Stmt\ClassMethod $v, string $name): void protected function checkParentMethodCanBeOverridden(Node\Stmt\ClassMethod $v, string $name): void
{ {
@ -4118,7 +4140,7 @@ CODE;
if (!$extends) { if (!$extends) {
break; break;
} }
// 父类是内置类 // The parent class is a built-in class
if ($classDef->inheritedFromInternalClass) { if ($classDef->inheritedFromInternalClass) {
$modifiers = Reflection::getClassMethodModifiers($extends, $name); $modifiers = Reflection::getClassMethodModifiers($extends, $name);
if ($modifiers & \ReflectionMethod::IS_PRIVATE) { if ($modifiers & \ReflectionMethod::IS_PRIVATE) {
@ -5066,7 +5088,8 @@ CODE;
&& !$this->hasMatchingOverrideDeclaration($this->classDef, $name)) { && !$this->hasMatchingOverrideDeclaration($this->classDef, $name)) {
$this->fatalMissingOverride($v, $this->classDef->getNamespacedName(false), $name); $this->fatalMissingOverride($v, $this->classDef->getNamespacedName(false), $name);
} }
// 预处理阶段没有父类的信息,只能在实现阶段检查 // The preprocess phase has no parent-class info, so the check can
// only run in the implementation phase.
$this->checkParentMethodCanBeOverridden($v, $name); $this->checkParentMethodCanBeOverridden($v, $name);
$methodCodes[$name] = $this->parseFunction($v); $methodCodes[$name] = $this->parseFunction($v);
} }

@ -30,10 +30,14 @@ trait NativeTypeCompatibilityTrait
protected function isInheritedFrom(string $class, string $expected): bool protected function isInheritedFrom(string $class, string $expected): bool
{ {
// 继承关系判断的唯一入口。调用者不应直接使用 PHP 运行时反射函数判断普通项目类。 // The single entry point for inheritance checks. Callers must not use PHP
// 对 AOT 已扫描到的项目类/接口,必须走 classDef/interfaceDef 中的 extends/implements 图; // runtime reflection functions directly to judge ordinary project classes.
// 对 PHP 内置类/接口,可以使用 Zend 运行时反射,因为这部分属于目标 PHP 运行时的固定能力; // For project classes/interfaces already scanned by AOT, the extends/
// 对动态类返回 true 表示“静态阶段无法否定”,后续必须保留运行时检查兜底。 // implements graph in classDef/interfaceDef must be followed; for PHP
// built-in classes/interfaces, Zend runtime reflection may be used, since
// these are fixed capabilities of the target PHP runtime. Returning true
// for a dynamic class means "cannot be disproven at static time", so a
// runtime check must be retained as a fallback.
$class = ltrim($class, '\\'); $class = ltrim($class, '\\');
$expected = ltrim($expected, '\\'); $expected = ltrim($expected, '\\');
if (strcasecmp($class, $expected) === 0) { if (strcasecmp($class, $expected) === 0) {
@ -51,15 +55,18 @@ trait NativeTypeCompatibilityTrait
} }
if ($this->isInternalClass($class) or $this->isInternalInterface($class)) { if ($this->isInternalClass($class) or $this->isInternalInterface($class)) {
// 只允许内置类型之间使用 Zend 的继承关系。这里不是查询任意用户类, // Zend's inheritance relation is only used between built-in types.
// 因此不会把编译器进程加载过的外部库类混入项目静态类型系统。 // This is not a query on an arbitrary user class, so external library
// classes loaded by the compiler process are never mixed into the
// project's static type system.
if (!$internal) { if (!$internal) {
return false; return false;
} }
return is_subclass_of($class, $expected); return is_subclass_of($class, $expected);
} }
// 类不存在,说明这是一个动态类,跳过静态检查,需要运行时检查 // If the class does not exist, it is a dynamic class; skip the static
// check and defer to a runtime check
if (!$this->hasClass($class)) { if (!$this->hasClass($class)) {
return true; return true;
} }
@ -103,8 +110,9 @@ trait NativeTypeCompatibilityTrait
return true; return true;
} }
if (!$this->hasClass($class)) { if (!$this->hasClass($class)) {
// 原生类继承自一个内置类,例如: UserError extends Exception ,然后 $expected 预期是 Throwable // A native class extends a built-in class (e.g. UserError extends
// 这种情况,需要使用 ZendVM 获取继承关系 // Exception), and $expected is Throwable. In this case ZendVM must
// be used to obtain the inheritance relation.
if ($this->isInternalClass($class) and $internal) { if ($this->isInternalClass($class) and $internal) {
return $class === $expected or is_subclass_of($class, $expected); return $class === $expected or is_subclass_of($class, $expected);
} }
@ -116,8 +124,11 @@ trait NativeTypeCompatibilityTrait
} }
$class = $classDef->extends; $class = $classDef->extends;
if ($this->isInternalClass($class)) { if ($this->isInternalClass($class)) {
// 项目类可以继承内置类。进入内置父类链后,后续关系交给 Zend 判断; // Project classes may extend built-in classes. Once the built-in
// 但 expected 也必须是内置类/接口,否则不能跨到外部用户类命名空间做运行时反射。 // parent chain is entered, further relations are delegated to Zend;
// however, $expected must also be a built-in class/interface,
// otherwise runtime reflection cannot cross into the external user
// class namespace.
return $internal && is_subclass_of($class, $expected); return $internal && is_subclass_of($class, $expected);
} }
$classDef = $this->getClass($class); $classDef = $this->getClass($class);
@ -126,8 +137,10 @@ trait NativeTypeCompatibilityTrait
private function interfaceExtends(string $interface, string $expected): bool private function interfaceExtends(string $interface, string $expected): bool
{ {
// 接口继承需要单独处理,因为 interfaceDef 没有 classDef 的父类链。 // Interface inheritance is handled separately because interfaceDef has no
// 这里同样只遍历 AOT 已知接口图;遇到内置接口时,才允许使用 Zend 的 is_subclass_of()。 // parent chain like classDef. Only the AOT-known interface graph is
// traversed here; Zend's is_subclass_of() is allowed only when a built-in
// interface is encountered.
$stack = [$interface]; $stack = [$interface];
while ($stack) { while ($stack) {
$check = array_pop($stack); $check = array_pop($stack);
@ -228,7 +241,8 @@ trait NativeTypeCompatibilityTrait
} }
if ($this->isVarExpr($arg->value)) { if ($this->isVarExpr($arg->value)) {
$var = $this->parseVariable($arg->value); $var = $this->parseVariable($arg->value);
// 若参数是引用类型,可以传入未定义变量,将立即创建变量作为引用 // For a by-reference parameter, an undefined variable may be passed;
// it is created immediately as a reference
if (!$this->hasLocalVar($var)) { if (!$this->hasLocalVar($var)) {
$this->addLocalVar($var, Type::VAR); $this->addLocalVar($var, Type::VAR);
} }
@ -266,10 +280,13 @@ trait NativeTypeCompatibilityTrait
if ($declaredClass !== '') { if ($declaredClass !== '') {
$class = $this->detectDeclaredClassOfExpr($arg->value); $class = $this->detectDeclaredClassOfExpr($arg->value);
if ($class !== '') { if ($class !== '') {
// native call 是性能热点,若静态阶段已经证明实参 is-a 声明类型, // Native calls are a performance hot path. If the static phase
// 就不要再生成 php::toObject($expr, target_ce) 做重复运行时检查。 // has already proven the argument is-a the declared type, do not
// 如果无法证明,但右值是已知 concrete object,说明一定不兼容,直接编译期 fatal; // emit php::toObject($expr, target_ce) to repeat the runtime
// 其他动态/外部库/any 场景保留 php::toObject() 作为运行时兜底。 // check. If it cannot be proven but the right-hand side is a
// known concrete object, it is necessarily incompatible, so fail
// at compile time; other dynamic/external-library/any scenarios
// keep php::toObject() as a runtime fallback.
if ($this->isObjectClassStaticallyAssignableTo($class, $declaredClass)) { if ($this->isObjectClassStaticallyAssignableTo($class, $declaredClass)) {
return $type === Type::OBJECT ? $expr : $this->convertObjectExpr($expr); return $type === Type::OBJECT ? $expr : $this->convertObjectExpr($expr);
} }

@ -47,7 +47,7 @@ function main(int $argc, array $argv): void
return; return;
} }
// .prof 文件分析模式:./tpc app.prof // .prof file analysis mode: ./tpc app.prof
if ($argc >= 2 && str_ends_with($argv[1], '.prof')) { if ($argc >= 2 && str_ends_with($argv[1], '.prof')) {
profileAnalyze($argc, $argv); profileAnalyze($argc, $argv);
return; return;
@ -57,9 +57,9 @@ function main(int $argc, array $argv): void
$translator = new Translator(TYPEPHP_ROOT_PATH); $translator = new Translator(TYPEPHP_ROOT_PATH);
$translator->setIndent(' '); $translator->setIndent(' ');
// 扫描所有 PHP 文件,预处理 // Scan all PHP files and preprocess them.
$files = $translator->prepare($translator->parseArgv($argv)); $files = $translator->prepare($translator->parseArgv($argv));
// 生成 C++ 文件 // Generate the C++ source files.
$sourceFiles = $translator->convert($files); $sourceFiles = $translator->convert($files);
$wasmManifest = getenv('TYPEPHP_WASM_INTERFACE_MANIFEST'); $wasmManifest = getenv('TYPEPHP_WASM_INTERFACE_MANIFEST');
@ -87,7 +87,7 @@ function main(int $argc, array $argv): void
$sourceFiles[] = $wasmAdapter; $sourceFiles[] = $wasmAdapter;
} }
// --dry 模式:仅生成 C++ 代码,不执行编译 // --dry mode: only generate the C++ code, without compiling.
if ($translator->isDryRun()) { if ($translator->isDryRun()) {
$buildDir = $translator->getBuildDir(); $buildDir = $translator->getBuildDir();
$count = count($sourceFiles); $count = count($sourceFiles);
@ -105,11 +105,11 @@ function main(int $argc, array $argv): void
return; return;
} }
// 编译所有 C++ 文件 // Compile all C++ source files.
$objectFiles = $translator->compile($sourceFiles); $objectFiles = $translator->compile($sourceFiles);
// 连接所有目标文件,生成可执行文件 // Link all object files to produce the executable.
$binaryFile = $translator->build($objectFiles); $binaryFile = $translator->build($objectFiles);
// 如果指定了 --run / -r,编译完成后立即执行 // If --run / -r was specified, execute immediately after compilation.
if ($translator->isRunRequested()) { if ($translator->isRunRequested()) {
$translator->run($binaryFile); // never returns $translator->run($binaryFile); // never returns
} }
@ -310,7 +310,7 @@ function profileAnalyze(int $argc, array $argv): void
exit(1); exit(1);
} }
// 从 prof 文件名推导二进制文件名(app.prof → app) // Derive the binary name from the prof file name (app.prof → app).
$binary = basename($profFile, '.prof'); $binary = basename($profFile, '.prof');
if (!file_exists($binary) && file_exists('./' . $binary)) { if (!file_exists($binary) && file_exists('./' . $binary)) {
$binary = './' . $binary; $binary = './' . $binary;

@ -2494,7 +2494,8 @@ OUPUT_EXAMPLE
return null; return null;
} }
foreach ($generatedFuncInfos as $generatedFuncInfo) { foreach ($generatedFuncInfos as $generatedFuncInfo) {
// TODO 从数组遍历元素,调用方法,在编译期无法获得元素的类型,因此判断作用域,必须为 public 方法 // TODO When iterating elements from an array and calling a method, the element type cannot be
// determined at compile time, so check the scope and require the method to be public.
if ($generatedFuncInfo->equalsApartFromNameAndRefcount($this)) { if ($generatedFuncInfo->equalsApartFromNameAndRefcount($this)) {
return $generatedFuncInfo; return $generatedFuncInfo;
} }
@ -2695,7 +2696,8 @@ class EvaluatedValue
$constType = ($const->phpDocType ?? $const->type)->tryToSimpleType(); $constType = ($const->phpDocType ?? $const->type)->tryToSimpleType();
if ($constType) { if ($constType) {
// 这里返回的并不是真正的值,而是一个类型的占位符,最终的运算由编译器完成,此处仅用于 ArgInfo 处理 // What is returned here is not the real value but a type placeholder; the final computation
// is performed by the compiler. This is only used for ArgInfo processing.
if ($constType->isBool()) { if ($constType->isBool()) {
return true; return true;
} elseif ($constType->isInt()) { } elseif ($constType->isInt()) {
@ -3488,7 +3490,7 @@ class StringBuilder {
$versions = [ $versions = [
PHP_85_VERSION_ID => self::PHP_85_KNOWN, PHP_85_VERSION_ID => self::PHP_85_KNOWN,
PHP_84_VERSION_ID => self::PHP_84_KNOWN, PHP_84_VERSION_ID => self::PHP_84_KNOWN,
PHP_82_VERSION_ID => self::PHP_82_KNOWN, // 8.3 合并到 8.2 PHP_82_VERSION_ID => self::PHP_82_KNOWN, // 8.3 is merged into 8.2
PHP_81_VERSION_ID => self::PHP_81_KNOWN, PHP_81_VERSION_ID => self::PHP_81_KNOWN,
]; ];
@ -3619,7 +3621,7 @@ class PropertyInfo extends VariableLike
); );
if (!$useEmptyArrayDefault) { if (!$useEmptyArrayDefault) {
// New 操作作为属性的默认值,需编译器处理 gen_stub 作为 null 值 // A New expression as a property default requires compiler handling; gen_stub treats it as a null value.
if ($this->defaultValue === null || $this->defaultValue instanceof Expr\New_) { if ($this->defaultValue === null || $this->defaultValue instanceof Expr\New_) {
$defaultValue = EvaluatedValue::null(); $defaultValue = EvaluatedValue::null();
} else { } else {
@ -5041,7 +5043,7 @@ class FileInfo {
$this->getMinimumPhpVersionIdCompatibility(), $this->getMinimumPhpVersionIdCompatibility(),
$this->isUndocumentable $this->isUndocumentable
); );
// 清理当前类名,避免污染 // Clear the current class name to avoid leaking it into subsequent classes.
ClassInfo::$currentClass = ''; ClassInfo::$currentClass = '';
continue; continue;
} }
@ -5253,7 +5255,7 @@ class FramelessFunctionInfo {
} }
/** /**
* 获取魔术方法的默认返回值类型。只有用户未显式声明类型时才使用。 * Get the default return type of a magic method. Used only when the user has not explicitly declared a type.
*/ */
function getMagicMethodDefaultReturnType(FunctionOrMethodName $name): ?string function getMagicMethodDefaultReturnType(FunctionOrMethodName $name): ?string
{ {
@ -5277,7 +5279,7 @@ function getMagicMethodDefaultReturnType(FunctionOrMethodName $name): ?string
} }
/** /**
* 获取魔术方法的默认参数类型。只有用户未显式声明类型时才使用。 * Get the default parameter type of a magic method. Used only when the user has not explicitly declared a type.
*/ */
function getMagicMethodDefaultParamType(FunctionOrMethodName $name, int $index): ?string function getMagicMethodDefaultParamType(FunctionOrMethodName $name, int $index): ?string
{ {
@ -5934,7 +5936,7 @@ function generateFunctionEntries(?Name $className, array $funcInfos, ?string $co
$underscoreName = implode("_", $className->getParts()); $underscoreName = implode("_", $className->getParts());
$functionEntryName = "class_{$underscoreName}_methods"; $functionEntryName = "class_{$underscoreName}_methods";
} else { } else {
// 跳过生成 ext_functions // Skip generating ext_functions.
$functionEntryName = "ext_functions"; $functionEntryName = "ext_functions";
return ''; return '';
} }

Loading…
Cancel
Save