Translate all Chinese notes into English

master^2
韩天峰 1 hour ago
parent 45f6b8a163
commit ee2afb08f7
  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 文件的 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 src/compiler.php
*/
@ -231,7 +231,7 @@ function dumpNode(NodeAbstract $node, int $depth = 0): void
$end = $node->getEndLine();
$subInfo = '';
// 为常见节点类型提取关键信息
// Extract key information for common node types.
switch (true) {
case $node instanceof Node\Expr\Variable:
$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') {
extractorMain($argv);
}

@ -7,7 +7,7 @@ use TypePhp\Platform\Windows;
use TypePhp\Platform\Macos;
/**
* Clang 编译器后端实现
* Clang compiler backend implementation.
*/
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
{
@ -62,7 +62,7 @@ class Clang extends GccLikeBackend
return 'link';
}
// ──── 钩子方法覆盖 ────
// ──── Hook method overrides ────
protected function getCompilerPrefixFlags(): string
{

@ -5,18 +5,18 @@ namespace TypePhp\Backend;
use TypePhp\Platform\PlatformBase;
/**
* 编译器后端抽象基类
* 定义所有编译器必须实现的接口
* Abstract base class for compiler backends.
* Defines the interface that all compilers must implement.
*/
abstract class CompilerBackend
{
/**
* 平台实例
* The platform instance.
*/
protected PlatformBase $platform;
/**
* 最近创建的 Response File 路径,用于构建完成后清理
* Path of the most recently created Response File, used for cleanup after the build completes.
*/
protected string $lastResponseFile = '';
@ -26,17 +26,17 @@ abstract class CompilerBackend
}
/**
* 获取编译器名称
* Get the compiler name.
*/
abstract public function getName(): string;
/**
* 获取编译器命令
* Get the compiler command.
*/
abstract public function getCompilerCommand(): string;
/**
* 获取链接器命令
* Get the linker command.
*/
abstract public function getLinkerCommand(): string;
@ -51,7 +51,7 @@ abstract class CompilerBackend
}
/**
* 构建完整的编译命令
* Build the complete compile command.
*/
abstract public function buildCompileCommand(
string $sourceFile,
@ -60,7 +60,7 @@ abstract class CompilerBackend
): string;
/**
* 构建 C 文件的编译命令(不包含 C++ 特定选项)
* Build the compile command for C files (excludes C++-specific options).
*/
abstract public function buildCCompileCommand(
string $sourceFile,
@ -69,9 +69,9 @@ abstract class CompilerBackend
): 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(
string $sourceFile,
@ -81,7 +81,7 @@ abstract class CompilerBackend
): string;
/**
* 构建完整的链接命令
* Build the complete link command.
*/
abstract public function buildLinkCommand(
array $objectFiles,
@ -90,33 +90,35 @@ abstract class CompilerBackend
): string;
/**
* 构建编译选项(不含文件路径)
* @param array $config 编译配置
* - optimize: 优化级别 (0-3)
* - debug_info: 是否生成调试信息
* - sanitize: sanitizer 类型 (address, undefined, etc.)
* - cpp_std: C++ 标准版本
* - is_zts: 是否为 ZTS 模式
* - build_mode: 构建模式 ('bin' or 'ext')
* - enable_profiler: 是否启用性能分析
* - suppressed_warnings: 需要屏蔽的警告代码数组
* - cxxflags: 用户自定义编译标志
* - compiler_pdb: MSVC 编译器 PDB 输出路径
* Build compile options (excludes file paths).
*
* @param array $config Compile configuration
* - optimize: optimization level (0-3)
* - debug_info: whether to generate debug information
* - sanitize: sanitizer type (address, undefined, etc.)
* - cpp_std: C++ standard version
* - is_zts: whether ZTS mode is enabled
* - build_mode: build mode ('bin' or 'ext')
* - enable_profiler: whether to enable profiling
* - suppressed_warnings: array of warning codes to suppress
* - cxxflags: user-defined compile flags
* - compiler_pdb: MSVC compiler PDB output path
*/
abstract public function buildCompileOptions(array $config = []): string;
/**
* 构建链接选项(不含文件路径)
* @param array $config 链接配置
* - debug_info: 是否生成调试信息
* - no_console: 是否隐藏控制台窗口
* - build_mode: 构建模式 ('bin' or 'ext')
* - sanitize: sanitizer 类型
* Build link options (excludes file paths).
*
* @param array $config Link configuration
* - debug_info: whether to generate debug information
* - no_console: whether to hide the console window
* - build_mode: build mode ('bin' or 'ext')
* - sanitize: sanitizer type
*/
abstract public function buildLinkOptions(array $config = []): string;
/**
* 获取平台实例
* Get the platform instance.
*/
public function getPlatform(): PlatformBase
{
@ -124,7 +126,7 @@ abstract class CompilerBackend
}
/**
* 格式化包含路径
* Format include paths.
*/
protected function formatIncludePaths(array $includePaths): string
{
@ -132,7 +134,7 @@ abstract class CompilerBackend
}
/**
* 格式化库路径
* Format library paths.
*/
protected function formatLibraryPaths(array $libraryPaths): string
{
@ -140,7 +142,7 @@ abstract class CompilerBackend
}
/**
* 格式化库文件
* Format library files.
*/
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 string $targetFile 最终输出文件路径(Response File 写入同目录)
* @return string 链接器参数,如 @build/project.rsp
* @param array $objectFiles List of object file paths.
* @param string $targetFile Final output file path (the Response File is written to the same directory).
* @return string Linker argument, e.g. @build/project.rsp
*/
protected function createResponseFile(array $objectFiles, string $targetFile): string
{
@ -169,7 +171,7 @@ abstract class CompilerBackend
$this->lastResponseFile = $rspFile;
$lines = [];
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, ' ')) {
$file = '"' . $file . '"';
}
@ -180,7 +182,7 @@ abstract class CompilerBackend
}
/**
* 删除最近创建的 Response File 临时文件
* Delete the most recently created Response File temporary file.
*/
public function cleanupResponseFile(): void
{

@ -8,24 +8,24 @@ use TypePhp\Platform\Linux;
use TypePhp\Platform\Macos;
/**
* 编译器工厂类
* 根据平台自动创建合适的编译器后端
* Compiler factory.
* Automatically creates the appropriate compiler backend based on the platform.
*/
class CompilerFactory
{
/**
* 创建默认编译器后端
* Create the default compiler backend.
*/
public static function create(PlatformBase $platform): CompilerBackend
{
if ($platform instanceof Windows) {
// Windows 默认使用 MSVC
// Windows uses MSVC by default.
return new Msvc($platform, $platform->getDefaultCompiler());
} elseif ($platform instanceof Linux) {
// Linux 默认使用 GCC
// Linux uses GCC by default.
return new Gcc($platform, $platform->getDefaultCompiler());
} elseif ($platform instanceof Macos) {
// macOS 默认使用 Clang
// macOS uses Clang by default.
return new Clang($platform, $platform->getDefaultCompiler());
} else {
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
{
@ -57,7 +57,7 @@ class CompilerFactory
}
/**
* 创建指定类型的编译器后端
* Create a compiler backend of the specified type.
*/
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
{
// 创建平台
// Create the platform.
$platform ??= \TypePhp\Platform\PlatformFactory::create();
// 创建编译器
// Create the compiler.
$compilerName = self::detectCompilerName($platform, $compilerName);
$compiler = self::createByName($compilerName, $platform);
return [
'platform' => $platform,
'compiler' => $compiler,

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

@ -6,9 +6,9 @@ use TypePhp\Platform\PlatformBase;
use TypePhp\Platform\Windows;
/**
* GCC/Clang 共享后端基类
* 包含 Unix-like 编译器(GCC、Clang)的通用命令行构建逻辑。
* 子类只需覆盖平台差异的钩子方法。
* Shared backend base class for 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
{
@ -37,21 +37,21 @@ abstract class GccLikeBackend extends CompilerBackend
return $headerFile . '.gch';
}
// ──── 钩子方法(子类覆盖点) ────
// ──── Hook methods (subclass override points) ────
/** 编译器特定的前缀标志(如 MSVC 兼容模式) */
/** Compiler-specific prefix flags (e.g. MSVC compatibility mode). */
protected function getCompilerPrefixFlags(): string
{
return '';
}
/** 链接器输出标志(-o vs /OUT:) */
/** Linker output flag (-o vs /OUT:). */
protected function getLinkerOutputFlag(): string
{
return '-o';
}
/** 格式化 sanitizer 标志 */
/** Format the sanitizer flag. */
protected function formatSanitizerFlag(string $sanitizer): string
{
return match ($sanitizer) {
@ -61,7 +61,7 @@ abstract class GccLikeBackend extends CompilerBackend
};
}
/** 获取 PIC 标志 */
/** Get the PIC flag. */
protected function getPICFlag(array $config): string
{
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 '';
}
/** 构建 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
{
$cmd = '';
@ -146,7 +146,7 @@ abstract class GccLikeBackend extends CompilerBackend
return ' -include ' . escapeshellarg($precompiledHeader['header']);
}
/** 获取平台特定的链接选项 */
/** Get platform-specific link options. */
protected function getPlatformLinkFlags(array $config): string
{
$flags = '';
@ -181,7 +181,7 @@ abstract class GccLikeBackend extends CompilerBackend
return $flags;
}
// ──── 抽象方法实现 ────
// ──── Abstract method implementations ────
public function buildCompileCommand(string $sourceFile, string $outputFile, array $options = []): string
{

@ -5,7 +5,7 @@ namespace TypePhp\Backend;
use TypePhp\Platform\Windows;
/**
* MSVC 编译器后端实现
* MSVC compiler backend implementation.
*/
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
{
@ -145,20 +145,20 @@ class Msvc extends CompilerBackend
$cmd .= ' ' . $this->formatIncludePaths($options['include_paths']);
}
// 平台宏定义
// Platform macro definitions.
$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;
}
/**
* 构建原生源文件的编译命令
* 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
{
@ -214,42 +214,42 @@ class Msvc extends CompilerBackend
}
/**
* 构建编译选项(实现抽象方法)
* Build compile options (implements the abstract method).
*/
public function buildCompileOptions(array $config = []): string
{
return $this->buildCommonCompileFlags($config, true);
}
/**
* 构建链接选项(实现抽象方法)
* Build link options (implements the abstract method).
*/
public function buildLinkOptions(array $config = []): string
{
$cmd = '';
// 调试
// Debug.
if (!empty($config['debug'])) {
$cmd .= ' /DEBUG';
}
// Windows 子系统
// Windows subsystem.
if (!empty($config['no_console'])) {
$cmd .= ' ' . $this->platform->getSubsystemOptions(true);
}
// CRT 配置
// CRT configuration.
$cmd .= ' ' . $this->platform->getCrtConfig();
// 扩展模块选项
// Extension module options.
if (!empty($config['build_mode']) && ($config['build_mode'] === 'ext' || $config['build_mode'] === 'lib')) {
$cmd .= ' /DLL';
}
// nologo
// nologo.
$cmd .= ' /nologo';
// LTO(链接时代码生成)
// LTO (Link Time Code Generation).
if (!empty($config['lto'])) {
$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 文件
* .res 文件可以直接传给 link.exe 作为输入
* Uses rc.exe (the MSVC resource compiler) to compile a .rc file into a .res file.
* The .res file can be passed directly to link.exe as input.
*
* @param string $rcFile 资源文件路径 (.rc)
* @param string $resFile 输出资源文件路径 (.res)
* @return string 编译命令
* @param string $rcFile Resource file path (.rc).
* @param string $resFile Output resource file path (.res).
* @return string The compile command.
*/
public function compileResourceFile(string $rcFile, string $resFile): string
{
// rc.exe 是 MSVC 自带的资源编译器
// /nologo: 不显示版权信息
// /fo: 指定输出文件
// rc.exe is the resource compiler bundled with MSVC.
// /nologo: suppress the copyright banner.
// /fo: specify the output file.
$cmd = 'rc.exe /nologo';
$cmd .= ' /fo ' . escapeshellarg($resFile);
$cmd .= ' ' . escapeshellarg($rcFile);

@ -20,7 +20,7 @@ trait NativeBuildConfigurationTrait
$this->getPhpxDir() . '/src/misc',
];
// 根据平台添加 PHP 包含路径
// Add the platform-specific PHP include paths
if ($platform instanceof Windows) {
$phpSdkPaths = $platform->buildPhpSdkIncludePaths($this->getPhpDir());
$includePaths = array_merge($includePaths, $phpSdkPaths);
@ -28,7 +28,7 @@ trait NativeBuildConfigurationTrait
// Linux/macOS
$phpPaths = $platform->buildPhpIncludePaths($this->getPhpDir());
$includePaths = array_merge($includePaths, $phpPaths);
// 内置 mpdecimal 头文件目录
// Bundled mpdecimal header directories
$includePaths[] = $this->getPhpxDir() . '/thirdparty/mpdecimal/libmpdec';
$includePaths[] = $this->getPhpxDir() . '/thirdparty/mpdecimal/libmpdec++';
}
@ -43,7 +43,7 @@ trait NativeBuildConfigurationTrait
$this->getPhpxDir() . '/lib',
];
// 根据平台添加 PHP 库路径
// Add the platform-specific PHP library paths
if ($platform instanceof Windows) {
$phpLibPaths = $platform->buildPhpSdkLibPaths($this->getPhpDir());
$libraryPaths = array_merge($libraryPaths, $phpLibPaths);
@ -57,45 +57,45 @@ trait NativeBuildConfigurationTrait
}
/**
* 获取库文件
* Get the library files to link against
*/
protected function getLibraries(): array
{
$platform = $this->getPlatform();
$libraries = [];
// phpx 库(根据平台使用不同的文件名格式)
// phpx library (file name format differs by platform)
$phpxLibPath = $this->findPhpxLibrary();
if ($phpxLibPath === null) {
$this->error($this->getPhpxLibraryErrorMessage());
}
$libraries[] = $phpxLibPath;
// extension 和 bin 模式都需要链接 PHP 库
// Both extension and bin modes need to link the PHP library
if ($platform instanceof Windows) {
// Windows: 根据构建模式选择不同的库
// Windows: pick different libraries based on the build mode
if ($this->isBuildModeEmbed()) {
// bin 模式:需要同时链接 php8ts.lib 和 php8embed.lib
// 注意:php8ts.lib 必须在 php8embed.lib 之前,因为 embed 依赖 core
// php8ts.lib 提供 PHP 核心全局符号(executor_globals, compiler_globals, sapi_globals)
// bin mode: link both php8ts.lib and php8embed.lib
// Note: php8ts.lib must come before php8embed.lib because embed depends on core
// php8ts.lib provides the PHP core global symbols (executor_globals, compiler_globals, sapi_globals)
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)) {
$libraries[] = $this->windowsPhpEmbedLib; // 不添加引号
$libraries[] = $this->windowsPhpEmbedLib; // do not quote
}
} else {
// ext 模式:只使用 php8ts.lib 或 php8.lib(PHP 扩展)
// ext mode: use only php8ts.lib or php8.lib (PHP extension)
if (!empty($this->windowsPhpCoreLib)) {
$libraries[] = $this->windowsPhpCoreLib; // 不添加引号
$libraries[] = $this->windowsPhpCoreLib; // do not quote
}
}
// 添加 Windows API 库(Win32 GUI 程序需要)
$libraries[] = 'user32.lib'; // Windows UI 函数(CreateWindow, MessageBox 等)
$libraries[] = 'gdi32.lib'; // GDI 图形函数
$libraries[] = 'kernel32.lib'; // 核心 Windows API
// Add the Windows API libraries (required by Win32 GUI programs)
$libraries[] = 'user32.lib'; // Windows UI functions (CreateWindow, MessageBox, etc.)
$libraries[] = 'gdi32.lib'; // GDI graphics functions
$libraries[] = 'kernel32.lib'; // Core Windows API
$libraries[] = 'gmp.lib';
$libraries[] = 'gmpxx.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 前缀);其他平台优先使用共享库
* (libphpx.so / libphpx.dylib),找不到时回退到静态库 libphpx.a。
* Windows uses phpx.lib (no lib prefix); other platforms prefer the shared
* library (libphpx.so / libphpx.dylib) and fall back to the static library
* libphpx.a when it is not found.
*/
protected function findPhpxLibrary(): ?string
{
@ -131,8 +133,8 @@ trait NativeBuildConfigurationTrait
return is_file($phpxLibPath) ? $phpxLibPath : null;
}
// Linux/macOS:共享库优先,静态库兜底
// getSharedLibraryExtension() 返回的值可能带点或不带点,需要统一处理
// Linux/macOS: prefer the shared library, fall back to the static library
// getSharedLibraryExtension() may or may not include a leading dot, so normalize it
$sharedLibExt = ltrim($platform->getSharedLibraryExtension(), '.');
$phpxLibPath = $this->getPhpxDir() . '/lib/libphpx.' . $sharedLibExt;
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
{
@ -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
{

@ -34,7 +34,7 @@ trait SourcePipelineTrait
$path = $realpath;
if (is_dir($path)) {
// 目录模式:不解析 YAML
// Directory mode: no YAML parsing
$list = $this->getFilesFromDir($path);
$targetName = basename($path);
$this->setTargetName($targetName);
@ -42,10 +42,10 @@ trait SourcePipelineTrait
} else {
$ext = pathinfo($path, PATHINFO_EXTENSION);
if ($ext === 'yml' || $ext === 'yaml') {
// YAML 配置模式:先解析 YAML
// YAML config mode: parse the YAML first
$list = $this->parseProjectYaml($path);
} elseif ($ext === 'php') {
// 单文件模式:不解析 YAML
// Single-file mode: no YAML parsing
$list = [$path];
$targetName = FileScanner::getFileName($path);
$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();
// The generated public import stub is an output artifact, not an input
@ -100,19 +101,22 @@ trait SourcePipelineTrait
}
}
// 仅在 PHP 脚本入口(bin/tpc.php)前置检测 phpx 库:缺少库立即 fatal,
// 避免继续向下执行到文件处理/编译阶段才报错。已编译的 tpc 可执行文件
// 在进入 main() 前就由动态链接器加载 libphpx,无需(也无法)在此检测。
// Pre-check the phpx library only at the PHP script entry (bin/tpc.php):
// a missing library fails immediately rather than surfacing later during
// 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)) {
$this->validatePhpxLibrary();
}
$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) {
foreach ($this->getPlatform()->getBuildLibraryWarnings(
$this->getPhpDir(),
@ -136,7 +140,7 @@ trait SourcePipelineTrait
$files = $this->filterIgnoredFiles($files);
$this->discoverNativeClassDeclarations($files);
// 分析 PHP 文件,预处理
// Analyze and preprocess the PHP files
foreach ($files as $k => $file) {
if (FileScanner::isPhpFile($file)) {
try {
@ -258,7 +262,7 @@ trait SourcePipelineTrait
$sourceFiles = [];
$validSourceCount = 0;
// 生成 C++ 文件
// Generate the C++ files
foreach ($files as $k => $file) {
try {
if (FileScanner::isPhpFile($file)) {
@ -293,10 +297,11 @@ trait SourcePipelineTrait
$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->genDataDeclarations($this->getIncludeDir() . "/php_{$this->targetName}_data_decl.h");
// 生成扩展模块源文件
// Generate the extension module source file
$sourceFiles[] = $this->genExtension();
return $sourceFiles;

@ -288,12 +288,14 @@ class CompilerBase implements PropertyAccessContext
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>
*/
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>
*/
protected array $persistentClassMap = [];
@ -305,21 +307,26 @@ class CompilerBase implements PropertyAccessContext
protected int $funcIndex = 0;
/**
* 用户定义(请求生命周期)函数/方法 → ID,运行期为 THREAD_LOCAL 缓存,RSHUTDOWN 清理
* key 为函数名或 `Class::method`
* User-defined (request-lifetime) function/method → ID. Backed by a
* THREAD_LOCAL cache at runtime and cleared on RSHUTDOWN.
* Key is a function name or `Class::method`.
* @var array<string, int>
*/
protected array $funcMap = [];
/**
* 内置/编译产物(模块生命周期)函数/方法 → ID,PHP 启动完成后惰性填充,RSHUTDOWN 不清理
* key 为函数名或 `Class::method`
* Built-in / compiled-output (module-lifetime) function/method → ID. Lazily
* populated after PHP startup and NOT cleared on RSHUTDOWN.
* Key is a function name or `Class::method`.
* @var array<string, int>
*/
protected array $persistentFuncMap = [];
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 int $persistentPropIndex = 0;
@ -345,10 +352,11 @@ class CompilerBase implements PropertyAccessContext
'mixed' => Type::VAR,
'null' => Type::VAR,
'any' => Type::VAR,
// callable 类型,可以是字符串、数组、对象
// 1) 'foo' 函数名称字符串, 2) [ $obj, 'bar' ] 对象方法数组, 3) Closure 对象, 4) [ 'class', 'staticMethod'] 类名+静态方法数组
// The callable type can be a string, array, or object:
// 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,
// iterable 类型,可以是数组或者对象
// The iterable type can be an array or an object.
'iterable' => Type::VAR,
'stream' => Type::STREAM,
'bigint' => Type::BIGINT,
@ -361,13 +369,15 @@ class CompilerBase implements PropertyAccessContext
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>
*/
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>>
*/
protected array $symbolCallInFile = [];
@ -400,7 +410,7 @@ class CompilerBase implements PropertyAccessContext
protected string $dir;
/**
* 原始值,可能包含 `\\` 多层空间.
* The raw namespace value, which may contain `\\` multi-level separators.
*/
protected string $namespace = '';
protected string $method = '';
@ -413,7 +423,7 @@ class CompilerBase implements PropertyAccessContext
protected array $useImportAliases = [];
/**
* 原始类名,不包含命名空间.
* The raw class name, without the namespace.
*/
protected string $class = '';
protected string $parentClass = '';
@ -469,7 +479,7 @@ class CompilerBase implements PropertyAccessContext
protected bool $bigintTypes = false;
protected string $rootPath;
protected string $buildDir;
protected string $outputDir = ''; // -o 参数指定的输出目录
protected string $outputDir = ''; // Output directory specified by the -o option
protected int $debugLine = 0;
protected CLImate $climate;
protected bool $stubFile = false;
@ -483,26 +493,28 @@ class CompilerBase implements PropertyAccessContext
protected Parser $parser;
protected string $phpVersion = self::DEFAULT_PHP_VERSION;
protected PrettyPrinter $printer;
protected bool $isPhpZts = false; // PHP 是否为线程安全版本
protected bool $isPhpZts = false; // Whether the PHP build is thread-safe (ZTS)
// Windows 平台:保存检测到的 PHP lib 文件路径
protected string $windowsPhpEmbedLib = ''; // php8embed.lib 路径
protected string $windowsPhpCoreLib = ''; // php8ts.lib 或 php8.lib 路径
// Windows platform: store the detected PHP lib file paths.
protected string $windowsPhpEmbedLib = ''; // Path to php8embed.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 ?CompilerBackend $compilerBackend = null;
/**
* 在预处理阶段获取所有类的方法名称,检测子类和父类中存在的同名方法,解决动态绑定方法调用的问题
* `static::methodCall()`
* `$this->methodCall()` 子类和父类中存在同名方法
* Records all class method names collected during the preprocessing phase.
* Used to detect methods with the same name declared in both a child class
* 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>
*/
protected array $classMethodOverride = [];
/**
* 存储所有类继承关系,类名必须全部为小写
* Stores all class inheritance relationships. Class names must be all lowercase.
* @var array<string, string>
*/
protected SymbolRepository $symbols;
@ -1183,8 +1195,10 @@ class CompilerBase implements PropertyAccessContext
}
/**
* 判断类的符号指针是否在 PHP 模块生命周期内稳定(MINIT 注册,跨请求缓存安全)。
* 编译产物(本单元编译的类/接口)与 PHP 内置类/接口均满足条件。
* Determine whether a class's symbol pointer is stable across the 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
{
@ -1196,8 +1210,9 @@ class CompilerBase implements PropertyAccessContext
}
/**
* 判断函数/方法符号指针是否在 PHP 模块生命周期内稳定。
* `Class::method` 形式的 key 以其所属类的稳定性为准。
* Determine whether a function/method symbol pointer is stable across the
* PHP module lifetime. For a `Class::method` key, stability is determined
* by the class it belongs to.
*/
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)。
* 属性 offset 缓存的前提是编译期能解析出声明属性(PropertyAccessResolver
* 只接受编译类的 ClassDef 或内置类的反射声明属性),用户类在编译期不可见,
* 其属性访问一律走 `.attr(name)` 字符串路径,因此所有条目必然进程级稳定,
* 全部进入 persistentPropMap。
* Note: there is no dynamic propMap for user-defined classes (unlike
* classMap/funcMap). The property-offset cache assumes declared properties
* can be resolved at compile time (PropertyAccessResolver only accepts a
* compiled ClassDef or the reflected declared properties of a built-in
* 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
{
@ -1555,10 +1572,12 @@ class CompilerBase implements PropertyAccessContext
}
return $this->withoutLocalClassEntryHoisting(function () use ($default): string {
/*
* 函数参数默认值只能为字面量,无法使用表达式获取值。
* 但 PHP 自 5.6 起支持在默认参数值中使用常量表达式,包括
* 类常量(self::FOO、ClassName::BAR、\Full\Class::BAZ),
* 编译器需要在编译期将其折叠为对应的字面量。
* Function parameter default values may only be literals; they
* cannot be obtained through an expression. Since PHP 5.6, however,
* 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.
* 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
{
@ -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
{
@ -2002,7 +2022,7 @@ class CompilerBase implements PropertyAccessContext
if ($this->classDef?->nativeObject) {
$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 '';
} else {
return $this->getNamespacedClassName($class);
@ -2104,10 +2124,10 @@ class CompilerBase implements PropertyAccessContext
protected function detectDeclaredClassOfExpr(NodeAbstract $expr): string
{
// 对象表达式有两类类型信息:
// 1. detectClassOfExpr() 返回“实际可推断的类”,例如 new Foo()、typed object 变量;
// 2. getDeclaredObjectType() 返回变量声明/首次赋值记录的 declared type,可能是接口或抽象类。
// 参数和属性赋值检查需要先使用实际类;实际类不可知时才退回 declared type。
// Object expressions carry two kinds of type information:
// 1. detectClassOfExpr() returns the "actually inferable class", e.g. new Foo() or a typed object variable;
// 2. getDeclaredObjectType() returns the declared type recorded at declaration/first assignment, which may be an interface or abstract class.
// 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);
if ($class !== '') {
return $class;
@ -2120,13 +2140,22 @@ class CompilerBase implements PropertyAccessContext
protected function isObjectClassStaticallyAssignableTo(string $class, string $expected): bool
{
// 这个函数只回答“编译器在静态阶段能否证明 $class is-a $expected”。
// 这里禁止使用 class_exists()/interface_exists()/is_a() 去查询当前运行编译器的 PHP 进程:
// - 编译器进程已加载的 Composer/工具类,不等价于被编译项目运行时可用的类;
// - 自举编译时还会把编译器自身依赖的外部库误判为项目静态类;
// - AOT 的静态判断必须只依赖 hasClass()/hasInterface() 记录的项目类图,或明确的内置类/接口。
// 如果类不属于这些集合,说明它是动态类/外部库类,不能在这里静态判定,应返回 false,
// 由调用处决定是延迟到运行时 php::toObject()/TypeCheck,还是因为确定 concrete mismatch 而 fatal。
// This function only answers "can the compiler prove at the static
// stage that $class is-a $expected". It must not use
// class_exists()/interface_exists()/is_a() to query the PHP process
// currently running the compiler:
// - Composer/tool classes already loaded in the compiler process are not
// equivalent to classes available at runtime for the compiled project;
// - 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, '\\');
$expected = ltrim($expected, '\\');
if (strcasecmp($class, $expected) === 0) {
@ -2146,10 +2175,13 @@ class CompilerBase implements PropertyAccessContext
protected function isKnownConcreteObjectExpr(NodeAbstract $expr, string $class): bool
{
// “已知 concrete object” 的要求比“表达式写着 new SomeClass”更严格:
// 只有 AOT 项目类图中的类或内置类,编译器才能在静态阶段确认其继承关系。
// 外部库类即使出现在 new 表达式中,也不能用当前编译器进程的反射信息判定,
// 否则会把编译器/Composer 运行环境泄漏进被编译项目的类型系统。
// "Known concrete object" is stricter than "the expression literally
// says new SomeClass": only classes in the AOT project class graph or
// built-in classes allow the compiler to confirm inheritance at the
// 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)) {
return false;
}
@ -2390,7 +2422,7 @@ class CompilerBase implements PropertyAccessContext
$lines[] = 'return ' . $tuple . ';';
return implode(PHP_EOL . $this->getIndent(), $lines);
}
// 实际函数的返回值
// The return value of the actual function.
$type = $this->detectTypeOfExpr($v->expr);
// In ordinary PHP mode, int +/−/* int is only conditionally an int:
// runtime overflow promotes the result to float. Keep the Variant
@ -2488,7 +2520,7 @@ class CompilerBase implements PropertyAccessContext
$expr = $this->parseExprAsValue($v->expr);
$returnType = $this->getReturnType();
// 匿名函数的返回值一定是 var
// The return value of an anonymous function is always var.
if (!$this->context->inClosure) {
if ($returnType === Type::VOID) {
$this->fatalError($v, 'The return type is void, cannot return any value');
@ -2511,7 +2543,7 @@ class CompilerBase implements PropertyAccessContext
}
$returnObjectCheckClass = '';
// 返回值的表达式是一个类的对象
// The return-value expression is an instance of a class.
$objectClass = $this->detectDeclaredClassOfExpr($v->expr);
$returnClass = $this->context->inClosure ? '' : $this->getReturnClass();
if ($returnClass) {
@ -2537,13 +2569,17 @@ class CompilerBase implements PropertyAccessContext
[$code, $tmpVar] = $this->genUnionCheckedReturnAssignment($exprCode);
$this->context->afterStmtLines[] = $this->getIndent() . 'return ' . $tmpVar . ';';
} elseif (!$this->isVarExpr($v->expr) and !$this->isScalar($v->expr)) {
// return 如果使用了 Indirect 语句,可能会导致变量提前析构,出现悬空指针
// 将 Indirect 赋值给临时变量后,使用 Ctor::Copy 解除了 Indirect,保证内存安全
// If return uses an Indirect statement, the variable may be
// destructed early, producing a dangling pointer. Assign the
// Indirect to a temporary variable; Ctor::Copy releases the
// Indirect, guaranteeing memory safety.
$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);
$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 . ';';
} else {
$code = 'return ' . $exprCode . ';';
@ -2715,7 +2751,8 @@ class CompilerBase implements PropertyAccessContext
$classDef = $this->getClass($class);
$methodDef = null;
// 递归查找,若子类中未定义方法,则尝试查找父类是否存在此方法
// Search recursively: if the method is not defined in the child class,
// try to find it in the parent class.
while (true) {
if (!$classDef->hasMethod($method)) {
if (!$classDef->extends) {
@ -2743,7 +2780,7 @@ class CompilerBase implements PropertyAccessContext
if (!$this->checkAccessible($classDef, $methodDef->flags)) {
$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])) {
return false;
}
@ -2767,7 +2804,8 @@ class CompilerBase implements PropertyAccessContext
$classDef = $this->getClass($class);
$originClassDef = $classDef;
$constDef = null;
// 递归查找,若子类中未定义方法,则尝试查找父类是否存在此方法
// Search recursively: if the constant is not defined in the child class,
// try to find it in the parent class.
while (true) {
if (!$classDef->hasConstant($const)) {
if (!$classDef->extends) {
@ -3276,12 +3314,15 @@ class CompilerBase implements PropertyAccessContext
}
/**
* $GLOBALS['var'] 等价于 global $var; $var ,将字符串常量转为变量名称即可
* 仅限于字面量字符串可以转为变量名称,其他则使用 php::global() 函数获取
* Resolve a PHP function name to its native (compiled) name by trying
* 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
{
// 绝对命名空间的函数
// Absolutely-qualified function name.
if ($funcName[0] == '\\') {
$funcName = ltrim($funcName, '\\');
$possibleFunctionNames = [$this->escapeName($funcName)];
@ -3759,13 +3800,14 @@ class CompilerBase implements PropertyAccessContext
$this->assertNotNativeObjectDynamicClassTarget($expr->class, $expr);
}
$ctorClassName = '';
// 匿名类
// Anonymous class.
if ($expr->class instanceof Node\Stmt\Class_) {
if ($expr->class->name === null) {
$classDef = $expr->class;
$className = $this->genAnonClassName();
$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) {
$parentClass = $this->getNamespacedClassName($this->parseIdentifier($classDef->extends));
$classDef->extends = new Node\Name\FullyQualified($parentClass);
@ -3777,7 +3819,9 @@ class CompilerBase implements PropertyAccessContext
}
}
$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->context->beforeStmtLines[] = 'static THREAD_LOCAL bool ' . $className . '_defined = false;';
$classCode = $this->genEmbeddedCode($classDef);
@ -4144,7 +4188,7 @@ class CompilerBase implements PropertyAccessContext
protected function parseEval(Expr\Eval_ $expr): string
{
$this->assertExprCanBeUsedAsValue($expr->expr, 'eval operand');
// 对 eval() 指令的 PHP 代码段禁止字面量优化
// Disable literal-string optimization for the PHP code passed to eval().
$expr->expr->setAttribute('noLiteralString', true);
$source = $this->isNativeObjectClass($this->detectClassOfExpr($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
{
@ -4282,7 +4327,8 @@ class CompilerBase implements PropertyAccessContext
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));
$fn = $this->getChainedFunc($op);
$expr = $node;
@ -4301,7 +4347,7 @@ class CompilerBase implements PropertyAccessContext
// $getValue is true: fall through to use the chain+result mechanism,
// 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)) {
$prop = $this->parsePropertyFetch($expr);
if ($this->isNativePropertyAccess($expr)) {
@ -4371,7 +4417,8 @@ class CompilerBase implements PropertyAccessContext
$node->setAttribute('chainOpResult', $result);
return $fn . '(' . $var . ', {' . implode(', ', $list) . '}, ' . $result . ')';
} 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)) {
return $var . '.toReference()';
}
@ -4854,19 +4901,20 @@ class CompilerBase implements PropertyAccessContext
if ($toType === Type::VAR or $fromType === Type::VAR) {
return true;
}
// 引用当前没有类型信息,按照 var 处理
// References currently carry no type information, so treat them as var.
if ($toType === Type::REF or $fromType === Type::REF) {
return true;
}
// 类型一致,可以互相赋值
// Types are identical, so they can be assigned to each other.
if ($toType === $fromType) {
return true;
}
// 原生类型可以互相转换,由 C++ 底层完成
// Native types can be converted between each other, handled by the C++ layer.
if ($this->isNativeType($toType) and $this->isNativeType($fromType)) {
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];
if (in_array($toType, $bigTypes, true) or in_array($fromType, $bigTypes, true)) {
return true;
@ -4911,12 +4959,12 @@ class CompilerBase implements PropertyAccessContext
$scopeClassDef = $this->getClass($this->functionDef->attributeFactoryScope);
}
}
// 私有方法,只能当前的类使用
// Private methods can only be used by the current class.
if ($flags & Modifiers::PRIVATE) {
return $scopeClassDef !== null
&& $this->isSameClassName($declaringClass, $scopeClassDef->getNamespacedName(false));
}
// 保护方法,只能当前类和子类使用
// Protected methods can only be used by the current class and its subclasses.
if ($flags & Modifiers::PROTECTED) {
if (!$scopeClassDef) {
return false;
@ -4926,12 +4974,14 @@ class CompilerBase implements PropertyAccessContext
$declaringClass
);
}
// 类外部调用,只允许调用 public 方法
// Calls from outside the class are only allowed for public methods.
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
*/

@ -45,7 +45,7 @@ trait CompilationStateTrait
$this->fatalError($var, 'Duplicate variable `$' . $var->name . '`');
}
$this->context->staticVars[$name] = $type;
// 静态变量实际上是一个全局变量的引用
// A static variable is actually a reference to a global variable.
$globalVar = $this->escapeStaticVar($name);
$this->addGlobalVar($globalVar, $type);
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
{
@ -222,8 +222,8 @@ trait CompilationStateTrait
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])
and $this->symbolDeclInFile[$name] === $this->file
and !$this->hasFunction($name)) {

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

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

@ -35,7 +35,7 @@ trait CallArgumentGenerator
$hasNamedArg = false;
$argNameIndex = $this->getFunctionArgNameIndex($functionDef);
$variadicArgIndex = $this->getVariadicArgIndex($functionDef);
// 对命名参数进行重排
// Reorder the named arguments into their declared positions
foreach ($callArgs as $i => $arg) {
if ($this->isPlaceholderExpr($arg)) {
throw new PlaceHolder();
@ -76,7 +76,7 @@ trait CallArgumentGenerator
if ($deferTrailingDefaults && $variadicArgCount > 0) {
$lastProvidedIndex = $variadicArgIndex;
}
// 命名参数中间存在空洞,需要使用默认参数填充
// Holes left between named arguments must be filled with default arguments
foreach ($functionDef->argInfoList as $k => $argInfo) {
if ($k < $parameterOffset) {
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
and count($functionDef->argInfoList) === $parameterOffset + 1
and $functionDef->argInfoList[$parameterOffset]->variadic) {
@ -203,7 +204,8 @@ trait CallArgumentGenerator
}
if ($className) {
// 动态调用类方法,无法判断参数是否为引用
// For dynamically called class methods, whether the parameter is
// passed by reference cannot be determined
if ($className === self::DYNAMIC_CALLED_CLASS) {
return false;
}
@ -216,7 +218,8 @@ trait CallArgumentGenerator
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);
return $variadicParam !== null && $variadicParam->isPassedByReference();
}
@ -492,7 +495,7 @@ trait CallArgumentGenerator
$array = $this->parseIdentifier($arg->value->var);
if ($array === 'GLOBALS') {
$globalVar = $this->parseGlobalsArrayDimFetch($arg->value);
// 全局变量作为引用参数
// Global variable passed as a by-reference argument
if ($byRef) {
$ref = $this->addTmpVar(Type::REF);
$this->context->beforeStmtLines[] = $ref . ' = ' . $globalVar . '.toReference();';
@ -749,8 +752,9 @@ trait CallArgumentGenerator
}
/**
* 展开 refval() 调用中的数组元素或对象属性,返回对应的 C++ 引用表达式。
* 若为普通变量则返回 null,由调用方自行处理。
* Expand an array element or object property inside a refval() call into its
* 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
{
@ -777,27 +781,31 @@ trait CallArgumentGenerator
}
/**
* 仅用于动态调用的参数解析
* Argument parsing used only for dynamic calls
*/
protected function parseArgRefVar(Node\Arg $arg, string $name): string
{
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);
} elseif ($this->getVarType($name) === Type::REF) {
return '&' . $name;
} else {
// 本地变量,且是原生类型,则转为普通变量
// A local variable of native type is converted to a plain variable
if ($this->hasLocalVar($name) and $this->isNativeType($this->getVarType($name))) {
$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();
$this->addLocalVar($tmpVar, Type::REF);
$this->context->beforeStmtLines[] = $tmpVar . ' = ' . $this->parseExpr($arg->value) . '.toReference();';
$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;
}

@ -285,7 +285,8 @@ trait ClosureGenerator
$this->fatalError($useItem->var, 'Incorrect Closure use syntax, only variable names are allowed');
}
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])
&& !isset($oriContext->staticVars[$var])) {
$oriContext->localVars[$var] = Type::REF;

@ -3,11 +3,12 @@
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:
* icon: path/to/icon.ico
@ -28,18 +29,18 @@ namespace TypePhp\Generator;
* product-name: "My Product"
* 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
*/
class ResourceFileGenerator
{
/**
* 资源配置
* Resource configuration
*/
private array $config;
/**
* 项目目录(用于解析相对路径)
* Project directory (used to resolve relative paths)
*/
private string $projectDir;
@ -50,7 +51,7 @@ class ResourceFileGenerator
}
/**
* 检查是否有任何资源配置
* Check whether any resource is configured
*/
public function hasResource(): bool
{
@ -60,7 +61,7 @@ class ResourceFileGenerator
}
/**
* 获取图标文件的绝对路径
* Get the absolute path of the icon file
*/
public function getIconPath(): ?string
{
@ -73,7 +74,7 @@ class ResourceFileGenerator
}
/**
* 获取 manifest 文件的绝对路径
* Get the absolute path of the manifest file
*/
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
{
// 如果是绝对路径,直接使用
// If it is already an absolute path, use it as-is
if (preg_match('/^[A-Za-z]:\\\\|^\//', $path)) {
return $path;
}
// 相对路径,基于项目目录解析
// Otherwise resolve the relative path against the project directory
return $this->projectDir . DIRECTORY_SEPARATOR . $path;
}
/**
* 生成 .rc 资源文件内容
* Generate the contents of the .rc resource file
*/
public function generate(): string
{
@ -109,15 +110,15 @@ class ResourceFileGenerator
$content .= '// DO NOT EDIT - This file is auto-generated' . 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 .= PHP_EOL;
// 包含 Windows 版本信息头文件
// Include the Windows header for version information
$content .= '#include <windows.h>' . PHP_EOL;
$content .= PHP_EOL;
// Manifest 资源(Windows 清单文件,如 UAC、DPI 感知等)
// Manifest resource (Windows application manifest, e.g. UAC, DPI awareness, etc.)
$manifestPath = $this->getManifestPath();
if ($manifestPath) {
$manifestPathRc = str_replace('\\', '/', $manifestPath);
@ -126,17 +127,17 @@ class ResourceFileGenerator
$content .= PHP_EOL;
}
// 图标资源
// Icon resource
$iconPath = $this->getIconPath();
if ($iconPath) {
// 使用正斜杠,Windows RC 编译器更兼容
// Use forward slashes for better compatibility with the Windows RC compiler
$iconPathRc = str_replace('\\', '/', $iconPath);
$content .= '// Icon Resource' . PHP_EOL;
$content .= 'MAINICON ICON "' . addslashes($iconPathRc) . '"' . PHP_EOL;
$content .= PHP_EOL;
}
// 版本信息
// Version information
$versionInfo = $this->config['version-info'] ?? [];
if (!empty($versionInfo)) {
$content .= $this->generateVersionInfo($versionInfo);
@ -146,7 +147,7 @@ class ResourceFileGenerator
}
/**
* 生成版本信息块
* Generate the version information block
*/
private function generateVersionInfo(array $info): string
{
@ -165,16 +166,17 @@ class ResourceFileGenerator
$content .= 'FILESUBTYPE ' . ($info['file-subtype'] ?? 'VFT2_UNKNOWN') . PHP_EOL;
$content .= 'BEGIN' . PHP_EOL;
// StringFileInfo
// StringFileInfo block
$content .= ' BLOCK "StringFileInfo"' . 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';
$content .= ' BLOCK "' . $langCodepage . '"' . PHP_EOL;
$content .= ' BEGIN' . PHP_EOL;
// 字符串值
// String values
$stringFields = [
'company-name' => 'CompanyName',
'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) {
// 已在上面通过 file-version 键处理
// Already handled above via the file-version key
}
$content .= ' END' . PHP_EOL;
$content .= ' END' . PHP_EOL;
// VarFileInfo
// VarFileInfo block
$content .= ' BLOCK "VarFileInfo"' . PHP_EOL;
$content .= ' BEGIN' . PHP_EOL;
// 0x0409 = English(US),1200 = Unicode(UTF-16)
// 配合 StringFileInfo 中的 040904b0 代码页,确保中文在 UTF-8 源文件中正确编码
// 0x0409 = English(US), 1200 = Unicode(UTF-16)
// 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 .= ' 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"
* - "v1052" → "1052,0,0,0" (去掉 v 前缀)
* - "v1052" → "1052,0,0,0" (strips the leading "v")
* - "1.0" → "1,0,0,0"
*/
private function formatVersionDots(string $version): string
{
// 去掉 v/V 前缀(如 v1052 → 1052)
// Strip a leading v/V prefix (e.g. v1052 → 1052)
$version = ltrim($version, 'vV');
// 如果已经是逗号分隔格式,直接返回
// Return as-is if it is already comma-separated
if (str_contains($version, ',')) {
return $version;
}
// 用点号分隔
// Split on dots
$parts = explode('.', $version);
// 确保每个部分都是数字(过滤掉非数字字符)
// Keep only digits in each part (filter out non-numeric characters)
foreach ($parts as $i => $part) {
$parts[$i] = preg_replace('/[^0-9]/', '', $part) ?: '0';
}
// 确保恰好有4个部分
// Ensure exactly four parts
while (count($parts) < 4) {
$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
{

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

@ -188,7 +188,7 @@ class Constants
'description' => 'Run the compiled binary after build',
'noValue' => true,
],
// 内部开发选项,用于定位特定行的翻译问题,请勿写入用户文档
// Internal development option used to locate translation issues on a specific line. Do not write it into user documentation.
'debug-line' => [
'longPrefix' => 'debug-line',
'description' => 'Enable debug line',
@ -305,10 +305,10 @@ class Constants
];
/**
* MSVC 编译器警告屏蔽列表
* 这些警告来自 Windows SDK 和 PHP SDK 头文件,都是编译器噪音,不影响功能
* MSVC compiler warning suppression list.
* 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 = [
'4244' => '类型转换可能丢失数据 (int -> smaller type)',

@ -233,8 +233,9 @@ trait FuncCallOptimizer
}
}
// 检测参数中使用的变量是否已定义,若变量不存在则回退到动态调用路径
// 动态路径中的 parseCallArgs() 会给出明确的错误信息
// Check whether the variables used in the arguments are defined; if a variable
// does not exist, fall back to the dynamic call path, where parseCallArgs()
// produces a clear error message.
foreach ($expr->args as $arg) {
if (!$arg instanceof Node\Arg) {
continue;
@ -269,7 +270,7 @@ trait FuncCallOptimizer
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) {
if ($arg->name !== null || $arg->unpack) {
return false;

@ -18,7 +18,7 @@ trait ArrayExpressionTrait
protected function parseArray(Expr\Array_ $node): string
{
$items = $node->items;
// 优化代码风格,空数组直接返回{},否则会产生一些空洞内容
// Optimize code style: return {} directly for an empty array, otherwise it would produce empty entries
if (count($items) === 0) {
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)) {
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
@ -270,7 +271,7 @@ trait ArrayExpressionTrait
{
$tmpVar = $this->genTmpVarName();
$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();';
$items = $node->items;

@ -153,7 +153,7 @@ trait AssignOpTrait
$this->addLocalVar($tmpVar, Type::VAR);
}
// 翻转赋值链
// Reverse the assignment chain
$chain = array_reverse($chain);
$list = [];
@ -525,7 +525,7 @@ trait AssignOpTrait
return $copyAssign;
}
}
// 类型推断,获取对象的类名,如果不是对象则返回空字符串
// Infer the type and obtain the object's class name; return an empty string for non-objects
$rightClass = $this->detectClassOfExpr($right);
$markNativeObjectNonNull = $this->context->scopeLevel <= 1
&& !$this->hasScopeGlobalVar($var)
@ -548,7 +548,7 @@ trait AssignOpTrait
$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 (!$this->hasVar($var)) {
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)) {
$finalVarType = $this->getNormalAssignType($type);
$finalVarType = $this->isNativeType($finalVarType) ? $this->getNativeType($finalVarType) : $finalVarType;
@ -944,9 +944,9 @@ trait AssignOpTrait
}
/**
* $count[$r] -= 1;
* 需要转为下面语句:
* must be lowered to the following statements:
* $tmp_var = $count[$r] - 1;
* $count[$r] = $tmp_var;.
* $count[$r] = $tmp_var;
*/
$isGlobals = $this->isVarExpr($node->var->var) && $node->var->var->name === 'GLOBALS';
$type = $isGlobals ? Type::VAR : $this->detectVarType($node->var);

@ -24,7 +24,7 @@ trait BinaryOpTrait
$this->assertExprCanBeUsedAsValue($left, 'binary operand');
$this->assertExprCanBeUsedAsValue($right, 'binary operand');
// 运算逻辑,优先转为数字
// Arithmetic logic: convert to a numeric type first when possible
$leftExpr = $this->parseOrderedBinaryOperand($left);
$rightExpr = $this->parseOrderedBinaryOperand($right);
@ -927,7 +927,7 @@ trait BinaryOpTrait
protected function parseCompareExpr(NodeAbstract $expr): string
{
$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)) {
return $this->getBoolValue($expr);
}

@ -220,8 +220,8 @@ trait ForeachTrait
}
/**
* 为了兼容已有代码,默认不使用原生类型,而是将整数和浮点数作为 php 变量处理
* 原生 int/float/bool 类型,是不支持自动转换的,例如如果 int 计算超过最大值后,会自动转为 float,除法若不能除尽,则会转为 float
* 某些情况下高性能计算,可能需要使用原生类型,使用 $a = std::int(0) 来显式地使用原生类型
* For backward compatibility, native types are not used by default; integers and floats are treated as php variables.
* 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.
* 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');
}
// 函数调用占位符,不是真实的函数调用
// Function call placeholder, not a real function call
if (count($expr->args) === 1 and $this->isPlaceholderExpr($expr->args[0])) {
return $this->genPlaceHolder($this->identifierToStr($expr->name));
}
@ -195,7 +195,7 @@ trait FunctionCallTrait
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);
$this->checkInternalFunctionArgCount($name, $expr);
$code = $this->parseFuncCallWithOptimizer($name, $expr);

@ -252,7 +252,7 @@ trait MethodCallTrait
}
$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 ($this->hasClass($class) and $this->getNativeMethod($expr, $class, '__call', false)) {
throw new DynamicCall();
@ -261,7 +261,7 @@ trait MethodCallTrait
$fullMethodName = $this->getOverrideMethodName($class, $method);
// 存在子类同名方法,尝试去虚化
// A subclass declares a method with the same name, so try to devirtualize
if ($this->isOverrideMethod($fullMethodName)) {
if (!$this->canDevirtualize($object, $class, $method)) {
return false;
@ -414,7 +414,7 @@ trait MethodCallTrait
if (empty($expr->args)) {
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) . ')';
}
@ -465,10 +465,10 @@ trait MethodCallTrait
if ($this->isTypedObject($object)) {
$class = $this->getObjectType($object);
} 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;
} 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);
}
}
@ -591,7 +591,7 @@ trait MethodCallTrait
. $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)) {
$type = $this->getVarType($object);
if ($class !== '' && $this->isNativeObjectClass($class)) {
@ -647,9 +647,9 @@ trait MethodCallTrait
return self::PREFIX . $nativeFunc . '(' . $receiver . ', '
. $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) {
// 非对象类型可使用内置方法
// Non-object types can use built-in methods
$fn = $this->findUniversalMethodAnyType($type, $methodName);
if ($fn) {
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)) {
$type = $this->detectTypeOfExpr($expr->var);
if ($type === Type::VOID) {
@ -929,7 +929,7 @@ trait MethodCallTrait
);
}
$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;
$rtClass = $this->getFullClassName();
} else {
@ -974,7 +974,7 @@ trait MethodCallTrait
} catch (PlaceHolder) {
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) {
$object = 'this_';
} 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)) {
// 属性 declared class 若是接口、抽象类或动态类,当前属性布局优化无法静态确认最终对象类型。
// 不在这里 fatal;后续 wrapObjectPropertyAssignTypeCheck() 会在需要时插入运行时检查。
// 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.
// Do not report a fatal error here; wrapObjectPropertyAssignTypeCheck() inserts a runtime check later when needed.
return;
}
$rightClass = $this->detectClassOfExpr($right);
// TODO 静态编译阶段无法获得准确的类型,需要在运行时检查
// TODO: the exact type cannot be determined at the static compilation stage; a runtime check is required
if ($rightClass === '') {
return;
}

@ -38,7 +38,7 @@ trait SwitchTrait
$var_def .= $type . ' ' . $tmp_var . ' = ' . $condExpr . ';' . PHP_EOL;
$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;
$code = $this->parseBeforeStmtLines() . PHP_EOL;

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

@ -3,8 +3,8 @@
namespace TypePhp\Platform;
/**
* Unix-like 平台基类(Linux, macOS)
* 包含 GCC/Clang 通用标志语法的共享实现
* Base class for Unix-like platforms (Linux, macOS).
* Contains the shared implementation of common GCC/Clang flag syntax.
*/
abstract class UnixPlatform extends PlatformBase
{
@ -107,9 +107,10 @@ abstract class UnixPlatform extends PlatformBase
return $phpDir;
}
// Ubuntu/PPA 多版本环境下 php8.4 与 php-config8.4 并存,而
// php-config 可能被 update-alternatives 指向其它版本。优先依据
// PHP_BINARY 的版本后缀定位版本化 php-config,避免 ABI 错配。
// On Ubuntu/PPA multi-version installations, php8.4 and php-config8.4 coexist,
// while the unversioned php-config may be pointed at another version by
// 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));
if ($versionedConfig !== null) {
$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
{
@ -161,7 +162,7 @@ abstract class UnixPlatform extends PlatformBase
}
/**
* 获取 PIC 选项
* Get the PIC option.
*/
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
{
@ -209,7 +210,7 @@ abstract class UnixPlatform extends PlatformBase
}
/**
* 查找 php-config 可执行文件
* Locate the php-config executable.
*/
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) {
if ($this->phpConfigMatchesCurrentPhp($config)) {
return $config;
}
}
// 存在候选但版本均不匹配时给出明确错误
// Report a clear error when candidates exist but none matches the version.
if ($candidates !== []) {
$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
{
@ -346,7 +347,7 @@ abstract class UnixPlatform extends PlatformBase
}
/**
* 构建 PHP 库路径
* Build the PHP library paths.
*/
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
{

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

@ -180,7 +180,7 @@ class Preprocessor extends CompilerBase
$sorter = new StringSort();
$fileDeps = [];
// 构建依赖关系图
// Build the dependency graph
foreach ($this->symbolCallInFile as $file => $symbols) {
$deps = [];
foreach ($symbols as $symbol) {
@ -198,7 +198,7 @@ class Preprocessor extends CompilerBase
$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) {
if (!$this->isStubFile($file) and !in_array($file, $sortedFiles)) {
$sortedFiles[] = $file;
@ -237,7 +237,7 @@ class Preprocessor extends CompilerBase
$info = pathinfo($cppFile);
$ext = $this->getPlatform()->getObjectExtension();
// 保持与 cppFile 相同的路径分隔符
// Keep the same path separator as cppFile
$normalizedFile = str_replace('\\', '/', $cppFile);
$normalizedMiscDir = str_replace('\\', '/', $this->getPhpxDir() . '/src/misc/');
if (str_starts_with($normalizedFile, $normalizedMiscDir)) {
@ -719,7 +719,7 @@ class Preprocessor extends CompilerBase
foreach ($functionCalls as $call) {
if ($call->name instanceof Node\Name) {
// 内置函数不参与依赖管理
// Internal functions do not participate in dependency management
$funcName = strtolower($call->name->toString());
if (!$this->isInternalFunction($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]);
}
@ -846,7 +846,7 @@ class Preprocessor extends CompilerBase
if ($this->stubFile && $this->stubImportLibrary === '' && !$param->type) {
throw new \RuntimeException('No type for ' . $phpName);
}
// 构造方法属性定义语法(Constructor Property Promotion)
// Constructor property promotion syntax
if ($param->isPromoted()) {
if (!$this->classDef or !$this->methodDef or $this->methodDef->name !== '__construct') {
$this->fatalError($param, 'Promoted properties are not supported');
@ -910,7 +910,7 @@ class Preprocessor extends CompilerBase
$this->lowerArgumentDefault($param, $argInfo);
}
} elseif ($param->variadic) {
// 变长参数可以视为空数组默认值
// A variadic parameter can be treated as an empty-array default value
$argInfo->default = '{}';
$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.
// Generated external stubs may preserve an untyped PHP declaration as php::Var.
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) {
$name = $this->class ? $this->class . '::' . $v->name : $v->name;
$this->fatalError($v, 'The return type of the function `' . $name . '` must be specified');
@ -997,7 +997,7 @@ class Preprocessor extends CompilerBase
if ($nullableNativeReturn !== null) {
[$returnType, $class] = $nullableNativeReturn;
}
// 构造、析构、克隆方法不能有返回值
// Constructor, destructor, and clone methods cannot have a return value
if ($this->method and in_array($this->method, ['__construct', '__destruct', '__clone'])) {
$returnType = Type::VOID;
}
@ -1077,7 +1077,7 @@ class Preprocessor extends CompilerBase
$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 (count($v->params) > 0) {
if (count($v->params) != 2) {
@ -1158,7 +1158,7 @@ class Preprocessor extends CompilerBase
}
$this->fatalError($v, "Duplicate function `{$name}`");
}
// 禁止重定义内置函数
// Forbid redefining built-in functions
if (!$this->methodDef and $this->isInternalFunction($name)) {
$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->classDef->extends = $this->parentClass;
// 是否继承了内置类
// Whether it inherits from an internal class
$this->classDef->inheritedFromInternalClass = $this->isInternalClass($parentClassLower);
}
@ -2146,11 +2146,11 @@ class Preprocessor extends CompilerBase
$fullMethodNameLower = strtolower($fullMethodName);
$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);
$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)) !== '') {
$parentMethodLower = strtolower($parentClass . '::' . $this->method);
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
{
@ -2400,7 +2400,7 @@ class Preprocessor extends CompilerBase
// use THello1, THello2 {
// 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;
} else {
$traits[] = $adaptation->trait;
@ -2409,9 +2409,9 @@ class Preprocessor extends CompilerBase
$traitName = $this->getNamespacedClassName($this->parseIdentifier($trait));
$methodName = $adaptation->method->toString();
/*
* 例如:
* For example:
* use TraitA { TraitA::method as newMethod}
* 这表示 TraitA::method() 会被重命名为 TraitA::newMethod()
* This means TraitA::method() is renamed to TraitA::newMethod()
*/
$aliases[$this->getFullMethodName($traitName, $methodName)][] = [
'newName' => $adaptation->newName ? $adaptation->newName->toString() : $methodName,
@ -2425,9 +2425,9 @@ class Preprocessor extends CompilerBase
}
$methodName = $adaptation->method->toString();
/*
* 例如:
* For example:
* 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) {
$traitName = $this->getNamespacedClassName($this->parseIdentifier($trait2));

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

@ -15,7 +15,7 @@ final class PythonToTypePhpConverter
/** @var array<string, true> */
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 = [];
/** @var array<string, true> */
@ -53,12 +53,12 @@ final class PythonToTypePhpConverter
foreach ($tree['body'] ?? [] as $node) {
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;
if (!$annotationOnly) {
$targets = ($node['_type'] ?? '') === 'Assign' ? ($node['targets'] ?? []) : [$node['target'] ?? []];
foreach ($targets as $target) {
// 解构赋值展开为其中的名称元素
// A destructuring assignment expands into its individual name elements.
$elements = in_array($target['_type'] ?? '', ['Tuple', 'List'], true)
? ($target['elts'] ?? [])
: [$target];
@ -77,7 +77,7 @@ final class PythonToTypePhpConverter
$name = (string) $node['name'];
$this->definedFunctions[$name] = true;
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->moduleGlobals[$name] = true;
}
@ -108,7 +108,7 @@ final class PythonToTypePhpConverter
if ($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 ($this->decoratorRebindings($function) as $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
{
@ -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>
*/
@ -233,7 +233,7 @@ final class PythonToTypePhpConverter
/** @param array<string, mixed> $node */
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') {
return $this->call($node);
}
@ -312,7 +312,7 @@ final class PythonToTypePhpConverter
$parts[] = match ($element['_type'] ?? '') {
'Name' => $this->variable((string) $element['id']),
'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'),
default => $this->unsupported($owner, 'nested destructuring is not supported'),
};
@ -337,7 +337,7 @@ final class PythonToTypePhpConverter
{
$target = $this->target($node['target']);
$operator = $node['op']['_type'] ?? '';
// PHP 没有 //= 与 @=,展开为对应的运算符函数调用
// PHP has no //= or @= operators, so these expand into the corresponding operator function calls.
if ($operator === 'FloorDiv' || $operator === 'MatMult') {
$function = $operator === 'FloorDiv' ? 'python\\operator\\floordiv' : 'python\\operator\\matmul';
return [$this->line($target . ' = ' . $function . '(' . $target . ', ' . $this->expression($node['value']) . ');')];
@ -530,7 +530,7 @@ final class PythonToTypePhpConverter
{
$targets = [];
$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)) {
foreach ($target['elts'] ?? [] as $element) {
$walk($element);
@ -585,7 +585,7 @@ final class PythonToTypePhpConverter
if (($function['_type'] ?? '') === 'Name') {
$name = (string) $function['id'];
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);
} elseif (isset($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 = [];
foreach ($fnDef->argInfoList as $argInfo) {
if ($argInfo->variadic) {

@ -69,12 +69,12 @@ trait NameResolutionTrait
}
/**
* 将 trait 方法参数中的类名 Name 节点升级为 Name\FullyQualified。
* 对于已由 parseTypeDecl() 解析的限定名(含 \),直接升级节点类型;
* 对于尚未解析的非限定名(如 NullableType 内层,parseTypeDecl 返回 TYPE_VAR 跳过了解析),
* 先通过 useAliases/useNamespaces 解析再升级。
* gen_stub.php 的 SimpleType::fromNode() 依赖 isFullyQualified() 判断是否需要再次解析,
* 若不升级为 FullyQualified,在上下文丢失后会被错误地追加当前 namespace 前缀。
* Upgrade the class-name Name node in a trait method parameter to Name\FullyQualified.
* For qualified names (containing \) already resolved by parseTypeDecl(), upgrade the node type directly;
* for unresolved unqualified names (such as the inner type of a NullableType, which parseTypeDecl skips by returning TYPE_VAR),
* resolve them via useAliases/useNamespaces first and then upgrade.
* gen_stub.php's SimpleType::fromNode() relies on isFullyQualified() to decide whether to re-resolve;
* 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
{
@ -130,7 +130,7 @@ trait NameResolutionTrait
}
/**
* 函数名称处理,补齐 namespace
* Process the function name and prepend the namespace when required.
*/
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
{
@ -155,17 +155,17 @@ trait NameResolutionTrait
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) {
return Type::VAR;
}
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;
} else {
$typeName = $this->parseIdentifier($type);
$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')) {
$this->fatalError($type, 'The type `void`/`never` is allowed only for return type');
} elseif (isset($this->zendTypeMap[$typeNameLower])) {
@ -179,12 +179,12 @@ trait NameResolutionTrait
}
$class = $this->classDef->extends;
} elseif ($typeName === 'static') {
// static 类无法在编译期获取
// The static class cannot be determined at compile time
$class = '';
} else {
$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) {
$type->name = $class;
}

@ -70,7 +70,7 @@ final class PropertyAccessResolver
while (true) {
$classDef = $this->compiler->getClassDef($findClass);
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);
}
@ -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)反射不可见,
* 返回 null 回退到按名字符串查找路径。内置类在 MINIT 注册、进程级存活,
* 其声明属性的 offset 终身不变,缓存安全。
* Only declared properties visible to reflection are handled: dynamic properties and magic properties (__get/__set) are not visible to reflection,
* 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,
* so the offset of their declared properties never changes and caching is safe.
*/
private function resolveInternalClassProperty(
NodeAbstract $expr,
@ -182,7 +182,7 @@ final class PropertyAccessResolver
return null;
}
$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()) {
return null;
}
@ -209,8 +209,8 @@ final class PropertyAccessResolver
$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();
if ($propType !== null && !$propType instanceof \ReflectionNamedType) {
return null;

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

@ -9,12 +9,13 @@ use TypePhp\Entity\InterfaceDef;
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>
*/
private array $functions = [];
/**
* key 类名,包含命名空间
* Keyed by class name, including the namespace.
* @var array<string, ClassDef>
*/
private array $classes = [];

@ -80,7 +80,7 @@ class Translator extends Preprocessor
protected array $argInfoHeaderFiles = [];
protected array $registerSymbols = [];
// Windows 资源文件配置(图标、版本信息等)
// Windows resource file configuration (icon, version info, etc.)
protected array $resourceConfig = [];
protected array $globalHeaders = [
'cstring',
@ -112,8 +112,9 @@ class Translator extends Preprocessor
$this->preprocessArgvAdvanced();
$this->climate->arguments->parse();
// 只读取命令行参数,不立即应用(等待 YAML 解析后再应用)
// 这样可以确保优先级:命令行 > YAML > 默认值
// Only read the command-line arguments here; do not apply them yet
// (they are applied after YAML parsing). This preserves the priority:
// command line > YAML > defaults.
$this->internalFunctions = [];
foreach (get_defined_functions()['internal'] as $functionName) {
$function = Reflection::getFunction($functionName);
@ -133,13 +134,13 @@ class Translator extends Preprocessor
exit(0);
}
// 提前处理 --no-color,确保后续所有输出均为无颜色模式
// Handle --no-color early so all subsequent output is colorless.
if ($this->climate->arguments->defined('no-color')) {
$this->climate->forceAnsiOff();
}
// 检测操作系统、编译器以及 Windows 平台的 PHP lib 文件
// Detect the OS, the compiler, and (on Windows) the PHP lib files.
$this->detectPlatform();
}
@ -152,7 +153,8 @@ class Translator extends Preprocessor
$constants = [];
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
|| Reflection::isTypePhpExtension($groupName)
|| !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
{
@ -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
{
$this->applyPhpVersionCommandLineArgument();
// 优化级别
// Optimization level
if ($this->climate->arguments->defined('optimize')) {
$this->optimizeLevel = $this->climate->arguments->get('optimize');
}
// 构建模式
// Build mode
if ($this->climate->arguments->defined('mode')) {
$this->setBuildMode($this->climate->arguments->get('mode'));
}
// 调试行号
// Debug line number
if ($this->climate->arguments->defined('debug-line')) {
$this->debugLine = intval($this->climate->arguments->get('debug-line'));
}
// 最大并行任务数
// Maximum number of parallel jobs
if ($this->climate->arguments->defined('job')) {
$this->maxJob = intval($this->climate->arguments->get('job'));
}
// 调试模式
// Debug mode
if ($this->climate->arguments->defined('debug')) {
$this->debug = true;
}
// 禁用字面量字符串优化
// Disable literal string optimization
if ($this->climate->arguments->defined('no-literal-strings')) {
$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->isLinux()) {
$this->climate->error('--profile is only supported on Linux (requires gperftools)');
@ -340,12 +344,12 @@ class Translator extends Preprocessor
$this->enableProfiler = true;
}
// 禁用进度条
// Disable the progress bar
if ($this->climate->arguments->defined('no-progress')) {
$this->noProgress = true;
}
// 隐藏控制台窗口
// Hide the console window
if ($this->climate->arguments->defined('no-console')) {
$this->noConsole = true;
}
@ -355,27 +359,27 @@ class Translator extends Preprocessor
$this->sanitize = $this->climate->arguments->get('sanitize');
}
// C++ 标准版本
// C++ standard version
if ($this->climate->arguments->defined('cxx-std')) {
$this->cxxStd = $this->climate->arguments->get('cxx-std');
}
// 目标 CPU 指令集
// Target CPU instruction set
if ($this->climate->arguments->defined('march')) {
$this->march = $this->climate->arguments->get('march');
}
// 交叉编译目标平台
// Cross-compilation target platform
if ($this->climate->arguments->defined('target-platform')) {
$this->targetPlatform = $this->climate->arguments->get('target-platform');
}
// 输出文件名/路径
// Output file name/path
if ($this->climate->arguments->defined('output')) {
$this->setOutputPath($this->climate->arguments->get('output'));
}
// 构建目录
// Build directory
if ($this->climate->arguments->defined('build-dir')) {
$buildDir = $this->climate->arguments->get('build-dir');
if (!empty($buildDir)) {
@ -383,35 +387,39 @@ class Translator extends Preprocessor
}
}
// 干运行模式
// Dry-run mode
if ($this->climate->arguments->defined('dry')) {
$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'])) {
$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'])) {
$this->userDefines = $this->parseRepeatableArgv(['-D', '--define']);
}
// 链接时优化
// Link-time optimization
if ($this->climate->arguments->defined('lto')) {
$this->enableLto = true;
}
// clang-format 代码格式化(默认关闭,需显式 --format 开启)
// clang-format code formatting (disabled by default; requires explicit --format)
if ($this->climate->arguments->defined('format')) {
$this->enableCodeFormattingIfAvailable('--format');
}
// 用户自定义链接库(直接从 argv 解析以支持多值)
// User-defined link libraries (parsed directly from argv to support
// multiple values)
if ($this->hasRepeatableArgvFlag(['-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'])) {
$this->linkPaths = $this->parseRepeatableArgv(['-L', '--link-path']);
}
@ -426,25 +434,26 @@ class Translator extends Preprocessor
}
/**
* 从原始 $argv 中解析可重复参数,支持 -X val 和 --long val 两种形式。
* CLImate 的 multiple 选项只能保留最后一个值,因此需要手动解析。
* Parse repeatable arguments from the raw $argv, supporting both the
* "-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']
* @return string[] 收集到的所有值
* @param string[] $flags Flags to match, e.g. ['-I', '--include-path']
* @return string[] All collected values
*/
protected function parseRepeatableArgv(array $flags): array
{
global $argv;
$values = [];
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] !== '-') {
$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)) {
// 检查短标志合并:-I/path
// Check short-flag combined form: -I/path
foreach ($flags as $flag) {
if (strlen($flag) === 2 && $flag[0] === '-') {
$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
{
@ -551,7 +560,8 @@ class Translator extends Preprocessor
} else {
$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);
return $cppCode === '' ? null : $cppFile;
} 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
{
@ -592,7 +602,7 @@ class Translator extends Preprocessor
$platform = $this->platform ?? PlatformFactory::create();
$this->platform = $platform;
// 自动检测平台和编译器
// Auto-detect the platform and compiler
$result = CompilerFactory::autoDetect($this->cppCompiler, $platform);
$this->platform = $result['platform'];
$this->compilerBackend = $result['compiler'];
@ -601,7 +611,7 @@ class Translator extends Preprocessor
"Initialized new architecture: {$this->platform->getName()} + {$this->compilerBackend->getName()}"
);
} catch (\Exception $e) {
// 如果初始化失败,回退到旧逻辑
// Fall back to the legacy logic if initialization fails
$this->climate->warning(
"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
{
$this->cppCompiler = $compiler;
$this->climate->info("Using compiler from config: {$this->cppCompiler}");
// 重新初始化 Backend
// Re-initialize the Backend
$this->initializeNewArchitecture();
}
@ -641,7 +651,8 @@ class Translator extends Preprocessor
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, '\\')) {
$this->outputDir = dirname($name);
$name = basename($name);
@ -874,7 +885,7 @@ class Translator extends Preprocessor
}
$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;
// Internal/compiled symbols have module lifetime. They are initialized
// lazily after PHP startup, so disable_functions/disable_classes have
@ -888,7 +899,8 @@ class Translator extends Preprocessor
$code .= $this->genPythonModuleStorage();
$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 .= "// functions \n";
@ -1331,8 +1343,10 @@ CODE;
}
/**
* 检查 phpx/src/misc/ 下的源文件是否已有有效缓存,始终生效(除非指定 --force)。
* 缓存必须匹配编译命令和 PHP ABI,且 .o 文件必须不早于源文件和 phpx 头文件。
* Check whether source files under phpx/src/misc/ have a valid cache, always
* 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
{
@ -1427,7 +1441,7 @@ CODE;
}
/**
* 判断文件是否为 C++ 源文件
* Determine whether a file is a C++ source file.
*/
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++),
* 或 null 表示使用默认检测(C++ 文件)
* @return string|null Language identifier (c, assembler, objective-c, objective-c++),
* or null to use the default detection (C++ files).
*/
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
{
@ -1521,7 +1535,7 @@ CODE;
{
$job = $this->maxJob;
// embed 需要 main 函数,以及 cli 的内置函数定义
// The embed build needs the main function and the CLI's built-in function definitions.
if ($this->isBuildModeEmbed()) {
$runtimeSource = $this->getPhpxDir() . '/src/misc/typephp_runtime.cc';
// PHPX 2.6.3 keeps the common runtime in typephp_main.cc. Newer
@ -1540,14 +1554,14 @@ CODE;
$this->preparePhpXPrecompiledHeader();
// Windows 平台:编译资源文件(图标、版本信息等)
// Windows: compile the resource file (icon, version info, etc.)
$this->compileResourceFile();
if (!$this->getPlatform()->supportsPcntlParallelCompile() or $job <= 1) {
return $this->compileSourceFile($sourceFiles);
}
// Unix/Linux/macOS 使用 pcntl 并行编译
// Unix/Linux/macOS compile in parallel using pcntl
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
{
@ -1755,7 +1769,7 @@ CODE;
{
$targetFile = $this->getTargetFileName();
// Windows 平台:将 .res 资源文件加入链接
// Windows: add the .res resource file to the link
if ($this->isWindows() && $this->hasResourceFile()) {
$resFile = $this->getResourceResFile();
if (file_exists($resFile)) {
@ -2558,7 +2572,7 @@ CODE;
$this->sanitize = (string) $sanitize;
}
// 读取 cxx-flags
// Read cxx-flags
$cxxFlags = $cfg['cxx-flags'] ?? null;
if (!empty($cxxFlags)) {
if (is_array($cxxFlags)) {
@ -2568,25 +2582,25 @@ CODE;
}
}
// 读取 cxx-std
// Read cxx-std
$cxxStd = $cfg['cxx-std'] ?? null;
if (!empty($cxxStd)) {
$this->cxxStd = $cxxStd;
}
// 读取 march(目标 CPU 指令集)
// Read march (target CPU instruction set)
$march = $cfg['march'] ?? null;
if (!empty($march)) {
$this->march = $march;
}
// 读取 target-platform
// Read target-platform
$targetPlatform = $cfg['target-platform'] ?? null;
if (!empty($targetPlatform)) {
$this->targetPlatform = (string) $targetPlatform;
}
// 读取 build-dir
// Read build-dir
$buildDir = $cfg['build-dir'] ?? null;
if (!empty($buildDir)) {
$this->setBuildDir($this->resolvePath((string) $buildDir, $projectDir, 'Build path'));
@ -2596,7 +2610,7 @@ CODE;
$this->dryRun = true;
}
// 读取 ld-flags
// Read ld-flags
$ldflags = $cfg['ld-flags'] ?? null;
if (!empty($ldflags)) {
if (is_array($ldflags)) {
@ -2606,7 +2620,7 @@ CODE;
}
}
// 读取 include-paths
// Read include-paths
$includePaths = $cfg['include-paths'] ?? null;
if (!empty($includePaths) && is_array($includePaths)) {
foreach ($includePaths as $includePath) {
@ -2614,7 +2628,7 @@ CODE;
}
}
// 读取 defines
// Read defines
$defines = $cfg['defines'] ?? null;
if (!empty($defines) && is_array($defines)) {
foreach ($defines as $define) {
@ -2622,17 +2636,17 @@ CODE;
}
}
// 读取 lto
// Read lto
if (!empty($cfg['lto'])) {
$this->enableLto = true;
}
// 读取 format
// Read format
if (!empty($cfg['format'])) {
$this->enableCodeFormattingIfAvailable('YAML format');
}
// 读取 link-libs
// Read link-libs
$linkLibs = $cfg['link-libs'] ?? null;
if (!empty($linkLibs) && is_array($linkLibs)) {
foreach ($linkLibs as $lib) {
@ -2640,7 +2654,7 @@ CODE;
}
}
// 读取 link-paths
// Read link-paths
$linkPaths = $cfg['link-paths'] ?? null;
if (!empty($linkPaths) && is_array($linkPaths)) {
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;
if (!empty($output)) {
$this->setOutputPath($this->resolvePath((string) $output, $projectDir, 'Output path'));
@ -2683,19 +2698,19 @@ CODE;
$this->setTargetName((string) $cfg['name']);
}
// 读取 cpp-compiler
// Read cpp-compiler
$cppCompiler = $cfg['cpp-compiler'] ?? null;
if (!empty($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;
if (!empty($buildMode)) {
$this->setBuildMode((string) $buildMode);
}
// 读取 ignore(支持中横线和下划线)
// Read ignore (supports both hyphen and underscore)
$ignore = $cfg['ignore'] ?? null;
if (!empty($ignore)) {
if (!is_array($ignore)) {
@ -2713,13 +2728,13 @@ CODE;
}
}
// 读取 resource(Windows 资源配置:图标、版本信息)
// Read resource (Windows resource config: icon, version info)
$resource = $cfg['resource'] ?? null;
if (!empty($resource)) {
if (!is_array($resource)) {
$this->error('`resource` must be array');
}
// 验证图标文件是否存在
// Verify that the icon file exists
if (!empty($resource['icon'])) {
$iconPath = $resource['icon'];
if (!preg_match('/^[A-Za-z]:\\|^\//', $iconPath)) {
@ -2733,7 +2748,7 @@ CODE;
$this->resourceConfig['_projectDir'] = $projectDir;
}
// 读取 manifest(Windows 清单文件,与 resource 同级,缺省不携带)
// Read manifest (Windows manifest file, same level as resource, omitted by default)
$manifest = $cfg['manifest'] ?? null;
if (!empty($manifest)) {
if (!is_string($manifest)) {
@ -2893,7 +2908,7 @@ CODE;
foreach ($interfaceDef->extendsList ?: ($interfaceDef->extends ? [$interfaceDef->extends] : []) as $parent) {
$tmpCe = self::PREFIX . 'class_entry_' . $this->escapeCeName($parent);
// 不存在的接口,说明可能是内置接口
// A non-existent interface is likely a built-in interface
if (!$this->hasInterface($parent)) {
$sorter->add($tmpCe);
}
@ -2917,7 +2932,7 @@ CODE;
$deps = [];
$parent = $classDef->extends;
if ($parent) {
// 不存在的父类,说明可能是内置类
// A non-existent parent is likely a built-in class
$tmpCe = $this->getParentClassCe($classDef);
if (!$this->hasClass($parent)) {
$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) {
$parentClass = $this->getNamespacedClassName($this->parseIdentifier($class->extends));
if ($this->hasClass($parentClass)) {
@ -3579,7 +3595,7 @@ CODE;
'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) {
$this->fatalError($class, "Class `{$this->class}` cannot extend final class `{$parentClass}`");
}
@ -3787,7 +3803,9 @@ CODE;
$isEntryFunction = $this->hasFunction(self::ENTRY_FUNCTION)
&& $functionDef === $this->getFunction(self::ENTRY_FUNCTION);
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);
}
$callParams = $functionDef->argInfoList ? rtrim($callParams, ',') : '';
@ -3817,8 +3835,10 @@ CODE;
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();
$cppCode = $indent . "const char *value = " . $this->genCharPtr($entryFile, true) . ';' . PHP_EOL;
@ -3870,7 +3890,7 @@ CODE;
{
$cppCode = '';
// 接口没有方法实体
// Interfaces have no method bodies
if ($classDef instanceof ClassDef && $classDef->trait === null) {
if ($classDef->nativeObject) {
return '';
@ -3927,14 +3947,15 @@ CODE;
}
$this->functionDef = $this->getFunction($name);
// 类方法不要保存到 functions 中
// Class methods are not stored in `functions`
if ($this->methodDef) {
$this->methodDef->functionDef = $this->functionDef;
} else {
$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) {
$this->resetFunction();
return '';
@ -4062,7 +4083,7 @@ CODE;
$code .= $preamble . PHP_EOL;
}
$this->indentLevel--;
// 构建 PHP 级别的函数名用于 debug backtrace
// Build the PHP-level function name for debug backtraces
if ($this->class) {
$debugName = $this->class . '::' . $this->function;
} 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
{
@ -4118,7 +4140,7 @@ CODE;
if (!$extends) {
break;
}
// 父类是内置类
// The parent class is a built-in class
if ($classDef->inheritedFromInternalClass) {
$modifiers = Reflection::getClassMethodModifiers($extends, $name);
if ($modifiers & \ReflectionMethod::IS_PRIVATE) {
@ -5066,7 +5088,8 @@ CODE;
&& !$this->hasMatchingOverrideDeclaration($this->classDef, $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);
$methodCodes[$name] = $this->parseFunction($v);
}

@ -30,10 +30,14 @@ trait NativeTypeCompatibilityTrait
protected function isInheritedFrom(string $class, string $expected): bool
{
// 继承关系判断的唯一入口。调用者不应直接使用 PHP 运行时反射函数判断普通项目类。
// 对 AOT 已扫描到的项目类/接口,必须走 classDef/interfaceDef 中的 extends/implements 图;
// 对 PHP 内置类/接口,可以使用 Zend 运行时反射,因为这部分属于目标 PHP 运行时的固定能力;
// 对动态类返回 true 表示“静态阶段无法否定”,后续必须保留运行时检查兜底。
// The single entry point for inheritance checks. Callers must not use PHP
// runtime reflection functions directly to judge ordinary project classes.
// For project classes/interfaces already scanned by AOT, the extends/
// 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, '\\');
$expected = ltrim($expected, '\\');
if (strcasecmp($class, $expected) === 0) {
@ -51,15 +55,18 @@ trait NativeTypeCompatibilityTrait
}
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) {
return false;
}
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)) {
return true;
}
@ -103,8 +110,9 @@ trait NativeTypeCompatibilityTrait
return true;
}
if (!$this->hasClass($class)) {
// 原生类继承自一个内置类,例如: UserError extends Exception ,然后 $expected 预期是 Throwable
// 这种情况,需要使用 ZendVM 获取继承关系
// A native class extends a built-in class (e.g. UserError extends
// Exception), and $expected is Throwable. In this case ZendVM must
// be used to obtain the inheritance relation.
if ($this->isInternalClass($class) and $internal) {
return $class === $expected or is_subclass_of($class, $expected);
}
@ -116,8 +124,11 @@ trait NativeTypeCompatibilityTrait
}
$class = $classDef->extends;
if ($this->isInternalClass($class)) {
// 项目类可以继承内置类。进入内置父类链后,后续关系交给 Zend 判断;
// 但 expected 也必须是内置类/接口,否则不能跨到外部用户类命名空间做运行时反射。
// Project classes may extend built-in classes. Once the built-in
// 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);
}
$classDef = $this->getClass($class);
@ -126,8 +137,10 @@ trait NativeTypeCompatibilityTrait
private function interfaceExtends(string $interface, string $expected): bool
{
// 接口继承需要单独处理,因为 interfaceDef 没有 classDef 的父类链。
// 这里同样只遍历 AOT 已知接口图;遇到内置接口时,才允许使用 Zend 的 is_subclass_of()。
// Interface inheritance is handled separately because interfaceDef has no
// 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];
while ($stack) {
$check = array_pop($stack);
@ -228,7 +241,8 @@ trait NativeTypeCompatibilityTrait
}
if ($this->isVarExpr($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)) {
$this->addLocalVar($var, Type::VAR);
}
@ -266,10 +280,13 @@ trait NativeTypeCompatibilityTrait
if ($declaredClass !== '') {
$class = $this->detectDeclaredClassOfExpr($arg->value);
if ($class !== '') {
// native call 是性能热点,若静态阶段已经证明实参 is-a 声明类型,
// 就不要再生成 php::toObject($expr, target_ce) 做重复运行时检查。
// 如果无法证明,但右值是已知 concrete object,说明一定不兼容,直接编译期 fatal;
// 其他动态/外部库/any 场景保留 php::toObject() 作为运行时兜底。
// Native calls are a performance hot path. If the static phase
// has already proven the argument is-a the declared type, do not
// emit php::toObject($expr, target_ce) to repeat the runtime
// 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)) {
return $type === Type::OBJECT ? $expr : $this->convertObjectExpr($expr);
}

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

@ -2494,7 +2494,8 @@ OUPUT_EXAMPLE
return null;
}
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)) {
return $generatedFuncInfo;
}
@ -2695,7 +2696,8 @@ class EvaluatedValue
$constType = ($const->phpDocType ?? $const->type)->tryToSimpleType();
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()) {
return true;
} elseif ($constType->isInt()) {
@ -3488,7 +3490,7 @@ class StringBuilder {
$versions = [
PHP_85_VERSION_ID => self::PHP_85_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,
];
@ -3619,7 +3621,7 @@ class PropertyInfo extends VariableLike
);
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_) {
$defaultValue = EvaluatedValue::null();
} else {
@ -5041,7 +5043,7 @@ class FileInfo {
$this->getMinimumPhpVersionIdCompatibility(),
$this->isUndocumentable
);
// 清理当前类名,避免污染
// Clear the current class name to avoid leaking it into subsequent classes.
ClassInfo::$currentClass = '';
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
{
@ -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
{
@ -5934,7 +5936,7 @@ function generateFunctionEntries(?Name $className, array $funcInfos, ?string $co
$underscoreName = implode("_", $className->getParts());
$functionEntryName = "class_{$underscoreName}_methods";
} else {
// 跳过生成 ext_functions
// Skip generating ext_functions.
$functionEntryName = "ext_functions";
return '';
}

Loading…
Cancel
Save