refactor(backend): 重构编译器后端实现以支持自定义编译器路径

- 修改 Clang 和 Gcc 类构造函数以接受自定义编译器命令参数
- 引入 CompilerFactory 来统一管理编译器创建和检测逻辑
- 更新 BackendTest 添加对自定义编译器路径的支持测试
- 实现 Windows 平台下 lld-link 检测和回退机制
- 重构 CompilerBase 类中的平台和编译器初始化逻辑
- 移除旧的平台特定检测方法并整合到 PlatformFactory
- 更新编译和链接命令构建以使用新的后端抽象层
- 优化 include paths、library paths 和 libraries 的处理流程
- 改进目标文件扩展名和链接选项的跨平台处理
pull/1/head
韩天峰 4 months ago
parent 16259f095d
commit 661ab07cd9
  1. 47
      phpunit/src/Backend/BackendTest.php
  2. 42
      phpunit/src/Platform/PlatformTest.php
  3. 141
      src/Php/Backend/Clang.php
  4. 89
      src/Php/Backend/CompilerFactory.php
  5. 82
      src/Php/Backend/Gcc.php
  6. 87
      src/Php/Backend/Msvc.php
  7. 526
      src/Php/CompilerBase.php
  8. 33
      src/Php/Platform/Linux.php
  9. 28
      src/Php/Platform/Macos.php
  10. 99
      src/Php/Platform/PlatformBase.php
  11. 79
      src/Php/Platform/Windows.php
  12. 7
      src/Php/Preprocessor.php
  13. 116
      src/Php/Translator.php

@ -9,6 +9,7 @@ use PhpAot\Php\Platform\Macos;
use PhpAot\Php\Backend\Msvc;
use PhpAot\Php\Backend\Gcc;
use PhpAot\Php\Backend\Clang;
use PhpAot\Php\Backend\CompilerFactory;
class BackendTest extends TestCase
{
@ -231,6 +232,43 @@ class BackendTest extends TestCase
$this->assertStringContainsString('-DZEND_WIN32', $cmd);
}
public function testGccBuildCompileCommandUsesCustomCompilerAndIncludes(): void
{
$platform = new Linux();
$compiler = new Gcc($platform, '/opt/toolchain/bin/g++');
$cmd = $compiler->buildCompileCommand('test.cpp', 'test.o', [
'include_paths' => ['/usr/include/php'],
'cpp_std' => 'c++20',
'cxxflags' => '-fno-rtti',
]);
$this->assertStringStartsWith('/opt/toolchain/bin/g++', $cmd);
$this->assertStringContainsString('-I' . escapeshellarg('/usr/include/php'), $cmd);
$this->assertStringContainsString('-std=c++20', $cmd);
$this->assertStringContainsString('-fno-rtti', $cmd);
}
public function testGccBuildLinkCommandIncludesPlatformPathsOptionsAndLibraries(): void
{
$platform = new Linux();
$compiler = new Gcc($platform, 'g++');
$cmd = $compiler->buildLinkCommand(['a.o', 'b.o'], 'app', [
'library_paths' => ['/usr/lib'],
'libraries' => ['/usr/lib/libphpx.so', 'php'],
'ldflags' => '-Wl,--as-needed',
'build_mode' => 'ext',
]);
$this->assertStringStartsWith('g++', $cmd);
$this->assertStringContainsString('-L' . escapeshellarg('/usr/lib'), $cmd);
$this->assertStringContainsString('-Wl,--as-needed', $cmd);
$this->assertStringContainsString('-shared', $cmd);
$this->assertStringContainsString('-lphpx', $cmd);
$this->assertStringContainsString('-lphp', $cmd);
}
/**
* 测试 GCC 完整编译选项
*/
@ -314,6 +352,15 @@ class BackendTest extends TestCase
$this->assertEquals('link', $compiler->getLinkerCommand());
}
public function testCompilerFactoryKeepsConfiguredCompilerCommand(): void
{
$compiler = CompilerFactory::createByName('/opt/llvm/bin/clang++', new Linux());
$this->assertInstanceOf(Clang::class, $compiler);
$this->assertSame('/opt/llvm/bin/clang++', $compiler->getCompilerCommand());
$this->assertSame('/opt/llvm/bin/clang++', $compiler->getLinkerCommand());
}
/**
* 测试 Clang 完整编译选项(Unix)
*/

@ -87,6 +87,29 @@ class PlatformTest extends TestCase
$this->assertEquals('src\Php\Backend', $path);
}
public function testTargetExtensions(): void
{
$windows = new Windows();
$linux = new Linux();
$macos = new Macos();
$this->assertSame('.exe', $windows->getTargetExtension('bin'));
$this->assertSame('.dll', $windows->getTargetExtension('ext'));
$this->assertSame('', $linux->getTargetExtension('bin'));
$this->assertSame('.so', $linux->getTargetExtension('ext'));
$this->assertSame('', $macos->getTargetExtension('bin'));
$this->assertSame('.so', $macos->getTargetExtension('ext'));
}
public function testPlatformPathPrefixRemoval(): void
{
$windows = new Windows();
$linux = new Linux();
$this->assertSame('src\app.php', $windows->removeCommonPrefix('C:\project', 'C:/project/src/app.php'));
$this->assertSame('src/app.php', $linux->removeCommonPrefix('/project', '/project/src/app.php'));
}
/**
* 测试 Windows 子系统选项
*/
@ -155,8 +178,8 @@ class PlatformTest extends TestCase
$paths = ['/usr/include/php', '/usr/local/include'];
$flags = $platform->getIncludeFlags($paths);
$this->assertStringContainsString('-I"/usr/include/php"', $flags);
$this->assertStringContainsString('-I"/usr/local/include"', $flags);
$this->assertStringContainsString('-I' . escapeshellarg('/usr/include/php'), $flags);
$this->assertStringContainsString('-I' . escapeshellarg('/usr/local/include'), $flags);
}
/**
@ -169,8 +192,8 @@ class PlatformTest extends TestCase
$paths = ['/usr/lib', '/usr/local/lib'];
$flags = $platform->getLibraryPathFlags($paths);
$this->assertStringContainsString('-L"/usr/lib"', $flags);
$this->assertStringContainsString('-L"/usr/local/lib"', $flags);
$this->assertStringContainsString('-L' . escapeshellarg('/usr/lib'), $flags);
$this->assertStringContainsString('-L' . escapeshellarg('/usr/local/lib'), $flags);
}
/**
@ -197,8 +220,8 @@ class PlatformTest extends TestCase
$paths = ['/usr/lib', '/usr/local/lib'];
$options = $platform->getRpathOptions($paths);
$this->assertStringContainsString('-Wl,-rpath,"/usr/lib"', $options);
$this->assertStringContainsString('-Wl,-rpath,"/usr/local/lib"', $options);
$this->assertStringContainsString('-Wl,-rpath,' . escapeshellarg('/usr/lib'), $options);
$this->assertStringContainsString('-Wl,-rpath,' . escapeshellarg('/usr/local/lib'), $options);
}
/**
@ -211,6 +234,13 @@ class PlatformTest extends TestCase
$this->assertEquals('-fPIC', $platform->getPicFlag());
}
public function testIntegerLiteralSuffixes(): void
{
$this->assertSame('L', (new Linux())->getIntegerLiteralSuffix());
$this->assertSame('LL', (new Macos())->getIntegerLiteralSuffix());
$this->assertSame('LL', (new Windows())->getIntegerLiteralSuffix());
}
/**
* 测试 Linux 共享库链接标志
*/

@ -9,6 +9,16 @@ use PhpAot\Php\Platform\PlatformBase;
*/
class Clang extends CompilerBackend
{
private string $compilerCommand;
private ?string $linkerCommand;
public function __construct(PlatformBase $platform, string $compilerCommand = 'clang++', ?string $linkerCommand = null)
{
parent::__construct($platform);
$this->compilerCommand = $compilerCommand;
$this->linkerCommand = $linkerCommand;
}
public function getName(): string
{
return 'Clang';
@ -16,16 +26,49 @@ class Clang extends CompilerBackend
public function getCompilerCommand(): string
{
return 'clang++';
return $this->compilerCommand;
}
public function getLinkerCommand(): string
{
if ($this->linkerCommand !== null) {
return $this->linkerCommand;
}
// Windows 下使用 MSVC 链接器,其他平台使用 clang++
if ($this->platform instanceof \PhpAot\Php\Platform\Windows) {
return 'link';
}
return 'clang++';
return $this->compilerCommand;
}
/**
* Windows 下优先使用 lld-link,找不到时回退到 link.exe
*/
public static function detectWindowsLinker(): string
{
$output = [];
$returnCode = 0;
exec('lld-link --version 2>&1', $output, $returnCode);
if ($returnCode === 0) {
return 'lld-link';
}
$llvmHome = getenv('LLVM_HOME');
if ($llvmHome && is_dir($llvmHome)) {
$lldLinkPath = rtrim($llvmHome, '\/') . '\x64\bin\lld-link.exe';
if (file_exists($lldLinkPath)) {
exec('"' . $lldLinkPath . '" --version 2>&1', $output, $returnCode);
if ($returnCode === 0) {
$lldDir = dirname($lldLinkPath);
putenv("PATH={$lldDir};" . getenv('PATH'));
return 'lld-link';
}
}
}
return 'link';
}
public function compileFile(
@ -118,34 +161,13 @@ class Clang extends CompilerBackend
$cmd .= ' -c';
$cmd .= ' ' . escapeshellarg($sourceFile);
$cmd .= ' -o ' . escapeshellarg($outputFile);
// 优化级别
$optimizeLevel = $options['optimize'] ?? 2;
// 调试模式
if (!empty($options['debug'])) {
$cmd .= ' -O0 -g';
} else {
$cmd .= ' -O' . $optimizeLevel;
}
// 警告级别
$cmd .= ' -Wall';
// C++ 标准
$cppStd = $options['cpp_std'] ?? 'c++17';
$cmd .= ' -std=' . $cppStd;
// Sanitizer 支持
if (!empty($options['sanitize'])) {
$cmd .= ' -fsanitize=' . $options['sanitize'];
}
// PIC(位置无关代码)
if (!empty($options['pic'])) {
$cmd .= ' -fPIC';
if (!empty($options['include_paths'])) {
$cmd .= ' ' . $this->formatIncludePaths($options['include_paths']);
}
$cmd .= $this->buildCompileOptions($options);
return $cmd;
}
@ -167,6 +189,10 @@ class Clang extends CompilerBackend
$cmd .= ' -c';
$cmd .= ' ' . escapeshellarg($sourceFile);
$cmd .= ' -o ' . escapeshellarg($outputFile);
if (!empty($options['include_paths'])) {
$cmd .= ' ' . $this->formatIncludePaths($options['include_paths']);
}
// 优化级别(C 文件通常使用较低的优化)
$optimizeLevel = $options['optimize'] ?? 0;
@ -195,41 +221,22 @@ class Clang extends CompilerBackend
if ($this->platform instanceof \PhpAot\Php\Platform\Windows) {
$cmd .= ' /OUT:' . escapeshellarg($outputFile);
// 调试信息
if (!empty($options['debug'])) {
$cmd .= ' /DEBUG';
}
// Windows 子系统
if (!empty($options['no_console'])) {
$cmd .= ' ' . $this->platform->getSubsystemOptions(true);
}
// CRT 配置
$cmd .= ' ' . $this->platform->getCrtConfig();
} else {
// Unix/Linux/macOS 使用 GCC 风格语法
$cmd .= ' -o ' . escapeshellarg($outputFile);
// 共享库
if (!empty($options['shared'])) {
$cmd .= ' ' . $this->platform->getSharedLinkFlag();
// macOS 需要 install_name
if ($this->platform instanceof \PhpAot\Php\Platform\Macos && !empty($options['install_name'])) {
$cmd .= ' ' . $this->platform->getCurrentInstallNameOption($options['install_name']);
}
}
// RPATH(运行时库搜索路径)
if (!empty($options['rpath'])) {
$cmd .= ' ' . $this->platform->getRpathOptions($options['rpath']);
}
// Sanitizer 链接选项
if (!empty($options['sanitize'])) {
$cmd .= ' -fsanitize=' . $options['sanitize'];
}
}
if (!empty($options['library_paths'])) {
$cmd .= ' ' . $this->formatLibraryPaths($options['library_paths']);
}
if (!empty($options['ldflags'])) {
$cmd .= ' ' . $options['ldflags'];
}
$cmd .= $this->buildLinkOptions($options);
if (!empty($options['libraries'])) {
$cmd .= ' ' . $this->formatLibraries($options['libraries']);
}
return $cmd;
@ -358,7 +365,7 @@ class Clang extends CompilerBackend
}
// PIC (Position Independent Code)
if (!empty($config['build_mode']) && $config['build_mode'] === 'ext') {
if ((!empty($config['build_mode']) && $config['build_mode'] === 'ext') || !empty($config['pic'])) {
if ($this->platform instanceof \PhpAot\Php\Platform\Windows) {
// Windows Clang 不需要特殊处理
} else {
@ -408,8 +415,12 @@ class Clang extends CompilerBackend
} else {
// Unix/Linux/macOS
// 扩展模块选项
if (!empty($config['build_mode']) && $config['build_mode'] === 'ext') {
$cmd .= ' -shared';
if ((!empty($config['build_mode']) && $config['build_mode'] === 'ext') || !empty($config['shared'])) {
$cmd .= ' ' . $this->platform->getSharedLinkFlag();
if ($this->platform instanceof \PhpAot\Php\Platform\Macos && !empty($config['install_name'])) {
$cmd .= ' ' . $this->platform->getCurrentInstallNameOption($config['install_name']);
}
}
// RPATH

@ -20,49 +20,106 @@ class CompilerFactory
{
if ($platform instanceof Windows) {
// Windows 默认使用 MSVC
return new Msvc($platform);
return new Msvc($platform, $platform->getDefaultCompiler());
} elseif ($platform instanceof Linux) {
// Linux 默认使用 GCC
return new Gcc($platform);
return new Gcc($platform, $platform->getDefaultCompiler());
} elseif ($platform instanceof Macos) {
// macOS 默认使用 Clang
return new Clang($platform);
return new Clang($platform, $platform->getDefaultCompiler());
} else {
throw new \RuntimeException("Unsupported platform: " . $platform->getName());
}
}
/**
* 根据配置、环境变量和平台默认值解析编译器命令
*/
public static function detectCompilerName(PlatformBase $platform, string $configuredCompiler = ''): string
{
if ($configuredCompiler !== '') {
return $configuredCompiler;
}
$compilerEnv = getenv('PHPX_CC');
if ($compilerEnv) {
return $compilerEnv;
}
if (!$platform instanceof Windows) {
$cxxEnv = getenv('CXX');
if ($cxxEnv) {
return $cxxEnv;
}
}
return $platform->getDefaultCompiler();
}
/**
* 创建指定类型的编译器后端
*/
public static function createByName(string $compilerName, PlatformBase $platform): CompilerBackend
{
return match (strtolower($compilerName)) {
'msvc', 'cl' => new Msvc($platform),
'gcc', 'g++' => new Gcc($platform),
'clang', 'clang++' => new Clang($platform),
default => throw new \RuntimeException("Unsupported compiler: {$compilerName}"),
};
$normalized = self::normalizeCompilerName($compilerName);
$lowerCommand = strtolower($compilerName);
if (str_contains($normalized, 'clang') || str_contains($lowerCommand, 'clang')) {
$linker = $platform instanceof Windows ? Clang::detectWindowsLinker() : null;
return new Clang($platform, $compilerName, $linker);
}
if (
$normalized === 'gcc' ||
$normalized === 'g++' ||
$normalized === 'c++' ||
str_ends_with($normalized, '-gcc') ||
str_ends_with($normalized, '-g++') ||
str_contains($lowerCommand, 'g++') ||
str_contains($lowerCommand, 'c++') ||
str_contains($lowerCommand, 'gcc')
) {
return new Gcc($platform, $compilerName);
}
if ($normalized === 'msvc' || $normalized === 'cl') {
if (!$platform instanceof Windows) {
throw new \RuntimeException("MSVC compiler is only supported on Windows");
}
return new Msvc($platform, $compilerName);
}
throw new \RuntimeException("Unsupported compiler: {$compilerName}");
}
/**
* 自动检测并创建编译器和平台
*/
public static function autoDetect(string $compilerName = ''): array
public static function autoDetect(string $compilerName = '', ?PlatformBase $platform = null): array
{
// 创建平台
$platform = \PhpAot\Php\Platform\PlatformFactory::create();
$platform ??= \PhpAot\Php\Platform\PlatformFactory::create();
// 创建编译器
if (!empty($compilerName)) {
$compiler = self::createByName($compilerName, $platform);
} else {
$compiler = self::create($platform);
}
$compilerName = self::detectCompilerName($platform, $compilerName);
$compiler = self::createByName($compilerName, $platform);
return [
'platform' => $platform,
'compiler' => $compiler,
];
}
private static function normalizeCompilerName(string $compilerName): string
{
$firstToken = strtok(trim($compilerName), ' ');
if ($firstToken === false || $firstToken === '') {
return '';
}
$name = basename(str_replace('\\', '/', $firstToken));
$name = strtolower($name);
return preg_replace('/\.exe$/', '', $name);
}
}

@ -9,6 +9,16 @@ use PhpAot\Php\Platform\PlatformBase;
*/
class Gcc extends CompilerBackend
{
private string $compilerCommand;
private string $linkerCommand;
public function __construct(PlatformBase $platform, string $compilerCommand = 'g++', ?string $linkerCommand = null)
{
parent::__construct($platform);
$this->compilerCommand = $compilerCommand;
$this->linkerCommand = $linkerCommand ?? $compilerCommand;
}
public function getName(): string
{
return 'GCC';
@ -16,12 +26,12 @@ class Gcc extends CompilerBackend
public function getCompilerCommand(): string
{
return 'g++';
return $this->compilerCommand;
}
public function getLinkerCommand(): string
{
return 'g++';
return $this->linkerCommand;
}
public function compileFile(
@ -92,28 +102,13 @@ class Gcc extends CompilerBackend
$cmd .= ' -c';
$cmd .= ' ' . escapeshellarg($sourceFile);
$cmd .= ' -o ' . escapeshellarg($outputFile);
// 优化级别
$optimizeLevel = $options['optimize'] ?? 2;
$cmd .= ' -O' . $optimizeLevel;
// 调试信息
if (!empty($options['debug'])) {
$cmd .= ' -g';
}
// 警告级别
$cmd .= ' -Wall';
// C++ 标准
$cppStd = $options['cpp_std'] ?? 'c++17';
$cmd .= ' -std=' . $cppStd;
// PIC(位置无关代码)
if (!empty($options['pic'])) {
$cmd .= ' -fPIC';
if (!empty($options['include_paths'])) {
$cmd .= ' ' . $this->formatIncludePaths($options['include_paths']);
}
$cmd .= $this->buildCompileOptions($options);
return $cmd;
}
@ -126,6 +121,10 @@ class Gcc extends CompilerBackend
$cmd .= ' -c';
$cmd .= ' ' . escapeshellarg($sourceFile);
$cmd .= ' -o ' . escapeshellarg($outputFile);
if (!empty($options['include_paths'])) {
$cmd .= ' ' . $this->formatIncludePaths($options['include_paths']);
}
// 优化级别(C 文件通常使用较低的优化)
$optimizeLevel = $options['optimize'] ?? 0;
@ -149,22 +148,21 @@ class Gcc extends CompilerBackend
$cmd = $this->getLinkerCommand();
$cmd .= ' ' . implode(' ', array_map('escapeshellarg', $objectFiles));
$cmd .= ' -o ' . escapeshellarg($outputFile);
// 共享库
if (!empty($options['shared'])) {
$cmd .= ' ' . $this->platform->getSharedLinkFlag();
// macOS 需要 install_name
if ($this->platform instanceof \PhpAot\Php\Platform\Macos && !empty($options['install_name'])) {
$cmd .= ' ' . $this->platform->getCurrentInstallNameOption($options['install_name']);
}
if (!empty($options['library_paths'])) {
$cmd .= ' ' . $this->formatLibraryPaths($options['library_paths']);
}
// RPATH(运行时库搜索路径)
if (!empty($options['rpath'])) {
$cmd .= ' ' . $this->platform->getRpathOptions($options['rpath']);
if (!empty($options['ldflags'])) {
$cmd .= ' ' . $options['ldflags'];
}
$cmd .= $this->buildLinkOptions($options);
if (!empty($options['libraries'])) {
$cmd .= ' ' . $this->formatLibraries($options['libraries']);
}
return $cmd;
}
@ -262,7 +260,7 @@ class Gcc extends CompilerBackend
}
// PIC (Position Independent Code)
if (!empty($config['build_mode']) && $config['build_mode'] === 'ext') {
if ((!empty($config['build_mode']) && $config['build_mode'] === 'ext') || !empty($config['pic'])) {
$cmd .= ' -fPIC';
}
@ -287,8 +285,12 @@ class Gcc extends CompilerBackend
$cmd = '';
// 扩展模块选项
if (!empty($config['build_mode']) && $config['build_mode'] === 'ext') {
$cmd .= ' -shared';
if ((!empty($config['build_mode']) && $config['build_mode'] === 'ext') || !empty($config['shared'])) {
$cmd .= ' ' . $this->platform->getSharedLinkFlag();
if ($this->platform instanceof \PhpAot\Php\Platform\Macos && !empty($config['install_name'])) {
$cmd .= ' ' . $this->platform->getCurrentInstallNameOption($config['install_name']);
}
}
// RPATH

@ -9,9 +9,14 @@ use PhpAot\Php\Platform\Windows;
*/
class Msvc extends CompilerBackend
{
public function __construct(Windows $platform)
private string $compilerCommand;
private string $linkerCommand;
public function __construct(Windows $platform, string $compilerCommand = 'cl', string $linkerCommand = 'link')
{
parent::__construct($platform);
$this->compilerCommand = $compilerCommand;
$this->linkerCommand = $linkerCommand;
}
public function getName(): string
@ -21,12 +26,12 @@ class Msvc extends CompilerBackend
public function getCompilerCommand(): string
{
return 'cl';
return $this->compilerCommand;
}
public function getLinkerCommand(): string
{
return 'link';
return $this->linkerCommand;
}
public function compileFile(
@ -97,42 +102,14 @@ class Msvc extends CompilerBackend
$cmd .= ' /c';
$cmd .= ' ' . escapeshellarg($sourceFile);
$cmd .= ' /Fo' . escapeshellarg($outputFile);
// 平台宏定义
$cmd .= ' /DZEND_WIN32 /DPHP_WIN32 /DZEND_DEBUG=0';
// ZTS 支持
if ($this->platform instanceof Windows && $this->platform->isZts()) {
$cmd .= ' /DZTS';
}
// 优化级别
$optimizeLevel = $options['optimize'] ?? 2;
$cmd .= ' /O' . ($optimizeLevel >= 2 ? '2' : ($optimizeLevel === 0 ? 'd' : '1'));
// 警告级别
$cmd .= ' /W3';
// 禁用常见警告
if (!empty($options['suppressed_warnings'])) {
foreach ($options['suppressed_warnings'] as $code => $description) {
$cmd .= " /wd{$code}";
}
if (!empty($options['include_paths'])) {
$cmd .= ' ' . $this->formatIncludePaths($options['include_paths']);
}
// C++ 标准
$cppStd = $options['cpp_std'] ?? 'c++17';
$cmd .= ' /std:' . $cppStd;
// 异常处理
$cmd .= ' /EHsc';
// CRT
$cmd .= ' /MD';
// nologo
$cmd .= ' /nologo';
$options['is_zts'] ??= $this->platform instanceof Windows && $this->platform->isZts();
$cmd .= $this->buildCompileOptions($options);
return $cmd;
}
@ -145,6 +122,10 @@ class Msvc extends CompilerBackend
$cmd .= ' /c';
$cmd .= ' ' . escapeshellarg($sourceFile);
$cmd .= ' /Fo' . escapeshellarg($outputFile);
if (!empty($options['include_paths'])) {
$cmd .= ' ' . $this->formatIncludePaths($options['include_paths']);
}
// 平台宏定义
$cmd .= ' /DZEND_WIN32 /DPHP_WIN32 /DZEND_DEBUG=0';
@ -180,23 +161,21 @@ class Msvc extends CompilerBackend
$cmd = $this->getLinkerCommand();
$cmd .= ' ' . implode(' ', array_map('escapeshellarg', $objectFiles));
$cmd .= ' /OUT:' . escapeshellarg($outputFile);
// 调试信息
if (!empty($options['debug'])) {
$cmd .= ' /DEBUG';
if (!empty($options['library_paths'])) {
$cmd .= ' ' . $this->formatLibraryPaths($options['library_paths']);
}
// Windows 子系统
if (!empty($options['no_console'])) {
$cmd .= ' ' . $this->platform->getSubsystemOptions(true);
if (!empty($options['ldflags'])) {
$cmd .= ' ' . $options['ldflags'];
}
// CRT 配置
$cmd .= ' ' . $this->platform->getCrtConfig();
// nologo
$cmd .= ' /nologo';
$cmd .= $this->buildLinkOptions($options);
if (!empty($options['libraries'])) {
$cmd .= ' ' . $this->formatLibraries($options['libraries']);
}
return $cmd;
}
@ -267,6 +246,7 @@ class Msvc extends CompilerBackend
// 禁用常见警告(只使用键,即警告代码)
if (!empty($options['suppressed_warnings'])) {
foreach ($options['suppressed_warnings'] as $code => $description) {
$code = is_int($code) && $code < 100 ? $description : $code;
$cmd .= " /wd{$code}";
}
}
@ -354,6 +334,7 @@ class Msvc extends CompilerBackend
// 禁用常见警告(只使用键,即警告代码)
if (!empty($config['suppressed_warnings'])) {
foreach ($config['suppressed_warnings'] as $code => $description) {
$code = is_int($code) && $code < 100 ? $description : $code;
$cmd .= " /wd{$code}";
}
}

@ -9,6 +9,8 @@
namespace PhpAot\Php;
use League\CLImate\CLImate;
use PhpAot\Php\Backend\CompilerBackend;
use PhpAot\Php\Backend\CompilerFactory;
use PhpAot\Php\Context\FunctionContext;
use PhpAot\Php\Entity\ClassDef;
use PhpAot\Php\Entity\ConstantDef;
@ -26,6 +28,11 @@ use PhpAot\Php\Generator\PlaceHolderGenerator;
use PhpAot\Php\Generator\PropertyPromotion;
use PhpAot\Php\Generator\Utils;
use PhpAot\Php\Parser\StdArrayParser;
use PhpAot\Php\Platform\Linux;
use PhpAot\Php\Platform\Macos;
use PhpAot\Php\Platform\PlatformBase;
use PhpAot\Php\Platform\PlatformFactory;
use PhpAot\Php\Platform\Windows;
use PhpParser\Modifiers;
use PhpParser\Node;
use PhpParser\Node\Expr;
@ -164,7 +171,6 @@ class CompilerBase extends \PhpAot\Core\Translator
protected string $cxxflags = '';
protected string $cxxStd = 'c++17';
protected string $ldflags = '';
protected string $linker = 'link'; // Windows linker: link.exe or lld-link
protected int $floatPrecision = 17;
protected bool $debug = false;
protected bool $formatCode = false;
@ -268,8 +274,8 @@ class CompilerBase extends \PhpAot\Core\Translator
protected string $windowsPhpCoreLib = ''; // php8ts.lib 或 php8.lib 路径
// 新的平台和编译器抽象层(可选使用)
protected ?\PhpAot\Php\Platform\PlatformBase $platform = null;
protected ?\PhpAot\Php\Backend\CompilerBackend $compilerBackend = null;
protected ?PlatformBase $platform = null;
protected ?CompilerBackend $compilerBackend = null;
/**
* 在预处理阶段获取所有类的方法名称,检测子类和父类中存在的同名方法,解决动态绑定方法调用的问题
@ -307,53 +313,30 @@ class CompilerBase extends \PhpAot\Core\Translator
*/
protected function detectPlatform(): void
{
// 检测是否为 Windows 系统
$this->isWindows = strtoupper(substr(PHP_OS, 0, 3)) === 'WIN';
if ($this->isWindows) {
// Windows 下检测使用哪个编译器
// 优先级:环境变量 PHPX_CC > cl (MSVC, 默认) > clang
$compilerEnv = getenv('PHPX_CC');
if ($compilerEnv) {
// 用户通过环境变量指定编译器
$this->cppCompiler = $compilerEnv;
$this->climate->info("Using compiler from PHPX_CC: {$this->cppCompiler}");
} else {
// 默认使用 MSVC(更稳定,与链接器兼容)
$this->cppCompiler = 'cl';
$this->climate->info('Using MSVC compiler (cl)');
try {
$this->platform = PlatformFactory::create();
$this->isWindows = $this->platform instanceof Windows;
$this->cppCompiler = CompilerFactory::detectCompilerName($this->platform);
if ($this->platform instanceof Windows) {
$libInfo = $this->platform->detectPhpLibs($this->getPhpDir());
$this->windowsPhpEmbedLib = $libInfo['embed'];
$this->windowsPhpCoreLib = $libInfo['core'];
$this->isPhpZts = $libInfo['is_zts'];
$this->platform = new Windows(
phpLibs: [$this->windowsPhpCoreLib, $this->windowsPhpEmbedLib],
isZts: $this->isPhpZts,
phpSdkPath: $this->getPhpDir() . '\\SDK'
);
}
// Windows 平台:检测 PHP lib 文件并决定 ZTS/NTS 模式
$this->detectWindowsPhpLibs();
// 初始化新的 Platform 和 Backend 抽象层
$this->initializeNewArchitecture();
} else {
// Unix/Linux/macOS 下检测编译器
// 优先级:环境变量 PHPX_CC > CXX > 平台默认
$compilerEnv = getenv('PHPX_CC') ?: getenv('CXX');
if ($compilerEnv) {
// 用户通过环境变量指定编译器
$this->cppCompiler = $compilerEnv;
$this->climate->info("Using compiler from environment: {$this->cppCompiler}");
} else {
// 根据平台选择默认编译器
if ($this->isMacos()) {
// macOS 默认使用 Clang(系统自带)
$this->cppCompiler = 'clang++';
$this->climate->info('Using Clang compiler (clang++)');
} else {
// Linux 默认使用 GCC
$this->cppCompiler = 'g++';
$this->climate->info('Using GCC compiler (g++)');
}
}
// 初始化新的 Platform 和 Backend 抽象层
$this->initializeNewArchitecture();
$this->compilerBackend = CompilerFactory::createByName($this->cppCompiler, $this->platform);
$this->climate->info(
"Initialized platform/backend: {$this->platform->getName()} + {$this->compilerBackend->getName()} ({$this->compilerBackend->getCompilerCommand()})"
);
} catch (\Throwable $e) {
$this->error($e->getMessage());
}
}
@ -376,28 +359,14 @@ class CompilerBase extends \PhpAot\Core\Translator
protected function initializeNewArchitecture(): void
{
try {
$platform = $this->platform ?? PlatformFactory::create();
$this->platform = $platform;
// 自动检测平台和编译器
$result = \PhpAot\Php\Backend\CompilerFactory::autoDetect($this->cppCompiler);
$result = CompilerFactory::autoDetect($this->cppCompiler, $platform);
$this->platform = $result['platform'];
$this->compilerBackend = $result['compiler'];
// Windows 平台:需要传递 ZTS 状态给 Platform 对象
if ($this->platform instanceof \PhpAot\Php\Platform\Windows && $this->isWindows) {
// 重新创建 Windows Platform,传递正确的 ZTS 状态
$phpSdkPath = $this->getPhpDir() . '\\SDK';
$this->platform = new \PhpAot\Php\Platform\Windows(
phpLibs: [],
isZts: $this->isPhpZts, // ✅ 传递检测到的 ZTS 状态
phpSdkPath: $phpSdkPath
);
// 重新创建 Backend,使用更新后的 Platform
$this->compilerBackend = \PhpAot\Php\Backend\CompilerFactory::createByName(
$this->cppCompiler,
$this->platform
);
}
$this->climate->info(
"Initialized new architecture: {$this->platform->getName()} + {$this->compilerBackend->getName()}"
);
@ -411,232 +380,86 @@ class CompilerBase extends \PhpAot\Core\Translator
}
}
/**
* Windows 平台:检测 PHP lib 文件并决定 ZTS/NTS 模式
* 根据找到的 lib 文件来决定是 ZTS 还是 NTS:
* - php8ts.lib 存在 => ZTS 模式
* - php8.lib 存在 => NTS 模式
*/
protected function detectWindowsPhpLibs(): void
protected function getPhpxDir(): string
{
$phpDirs = [
$this->getPhpDir() . '\SDK\lib', // 优先从 SDK/lib 查找
$this->getPhpDir() . '\lib', // 备选从 lib 查找
];
$embedLibPath = '';
$tsLibPath = '';
$ntsLibPath = '';
foreach ($phpDirs as $phpDir) {
if (!is_dir($phpDir)) {
continue;
}
// 检查 php8embed.lib
if (empty($embedLibPath) && file_exists($phpDir . '\php8embed.lib')) {
$embedLibPath = $phpDir . '\php8embed.lib';
}
// 检查 php8ts.lib (ZTS)
if (empty($tsLibPath) && file_exists($phpDir . '\php8ts.lib')) {
$tsLibPath = $phpDir . '\php8ts.lib';
}
// 检查 php8.lib (NTS)
if (empty($ntsLibPath) && file_exists($phpDir . '\php8.lib')) {
$ntsLibPath = $phpDir . '\php8.lib';
}
// 如果已经找到所有需要的库,提前退出
if ($embedLibPath && ($tsLibPath || $ntsLibPath)) {
break;
}
}
// 验证必需的库文件是否存在
if (!$embedLibPath) {
$this->error('php8embed.lib not found. Please ensure it exists in SDK/lib or lib directory of your PHP installation');
}
if (!$tsLibPath && !$ntsLibPath) {
$this->error('Neither php8ts.lib nor php8.lib found. Please ensure at least one exists in SDK/lib or lib directory of your PHP installation');
// 优先使用环境变量 PHPX_HOME
$phpxDir = getenv('PHPX_HOME');
if ($phpxDir && is_dir($phpxDir)) {
return rtrim($phpxDir, '\/');
}
// 保存库文件路径
$this->windowsPhpEmbedLib = $embedLibPath;
// 根据找到的库文件决定 ZTS/NTS 模式
// 优先级:php8ts.lib > php8.lib
if ($tsLibPath) {
$this->isPhpZts = true;
$this->windowsPhpCoreLib = $tsLibPath;
$this->climate->info('Detected ZTS mode (php8ts.lib found)');
} else {
$this->isPhpZts = false;
$this->windowsPhpCoreLib = $ntsLibPath;
$this->climate->info('Detected NTS mode (php8.lib found)');
// 尝试使用 Composer 安装的 phpx
$composerPhpxDir = $this->rootPath . '/vendor/swoole/phpx';
if (is_dir($composerPhpxDir)) {
return $composerPhpxDir;
}
}
/**
* 检测 Clang 是否可用
*/
protected function isClangAvailable(): bool
{
if (!$this->isWindows) {
return false;
$composerPhpxVendorDir = $this->rootPath . '/vendor/swoole/phpx-vendor';
if (is_dir($composerPhpxVendorDir)) {
return $composerPhpxVendorDir;
}
// 首先尝试 PATH 中的 clang++
$output = [];
$returnCode = 0;
exec('clang++ --version 2>&1', $output, $returnCode);
if ($returnCode === 0) {
// 检查是否有 lld-link
$this->checkLldLinker();
return true;
}
if (defined('ROOT_PATH')) {
$rootPhpxDir = ROOT_PATH . '/vendor/swoole/phpx';
if (is_dir($rootPhpxDir)) {
return $rootPhpxDir;
}
// 如果 PATH 中没有,尝试从 LLVM_HOME 环境变量获取
$llvmHome = getenv('LLVM_HOME');
if ($llvmHome && is_dir($llvmHome)) {
$clangPath = rtrim($llvmHome, '\/') . '\x64\bin\clang++.exe';
if (file_exists($clangPath)) {
// 验证是否可以执行
exec('"' . $clangPath . '" --version 2>&1', $output, $returnCode);
if ($returnCode === 0) {
// 将 Clang 路径添加到环境变量
$clangDir = dirname($clangPath);
putenv("PATH={$clangDir};" . getenv('PATH'));
// 检查是否有 lld-link
$this->checkLldLinker();
return true;
}
$rootPhpxVendorDir = ROOT_PATH . '/vendor/swoole/phpx-vendor';
if (is_dir($rootPhpxVendorDir)) {
return $rootPhpxVendorDir;
}
}
return false;
// 两个路径都不存在,报错
$this->error(
'phpx directory not found. Please either:\n' .
'1. Set PHPX_HOME environment variable to your phpx installation path\n' .
'2. Install phpx via Composer: composer require swoole/phpx'
);
}
/**
* 检查 lld-link 是否可用(用于更快的链接)
*/
protected function checkLldLinker(): void
protected function getPlatform(): PlatformBase
{
// 优先使用 lld-link(更快),否则使用 link.exe
$output = [];
$returnCode = 0;
exec('lld-link --version 2>&1', $output, $returnCode);
if ($returnCode === 0) {
$this->linker = 'lld-link';
$this->climate->info('Using lld-link linker (faster than link.exe)');
return;
}
// 如果 PATH 中没有,尝试从 LLVM_HOME 获取
$llvmHome = getenv('LLVM_HOME');
if ($llvmHome && is_dir($llvmHome)) {
$lldLinkPath = rtrim($llvmHome, '\/') . '\x64\bin\lld-link.exe';
if (file_exists($lldLinkPath)) {
exec('"' . $lldLinkPath . '" --version 2>&1', $output, $returnCode);
if ($returnCode === 0) {
// 将 lld-link 路径添加到环境变量
$lldDir = dirname($lldLinkPath);
putenv("PATH={$lldDir};" . getenv('PATH'));
$this->linker = 'lld-link';
$this->climate->info('Using lld-link linker from LLVM_HOME (faster than link.exe)');
return;
}
}
if ($this->platform === null) {
$this->platform = PlatformFactory::create();
$this->isWindows = $this->platform instanceof Windows;
}
// Fallback 到 MSVC link.exe
$this->linker = 'link';
$this->climate->info('Using MSVC link.exe linker');
return $this->platform;
}
protected function getPhpxDir(): string
protected function getCompilerBackend(): CompilerBackend
{
// 优先使用环境变量 PHPX_HOME
$phpxDir = getenv('PHPX_HOME');
if ($phpxDir && is_dir($phpxDir)) {
return rtrim($phpxDir, '\/');
}
// 尝试使用 Composer 安装的 phpx
$composerPhpxDir = $this->rootPath . '/vendor/swoole/phpx';
if (is_dir($composerPhpxDir)) {
return $composerPhpxDir;
if ($this->compilerBackend === null) {
$this->cppCompiler = CompilerFactory::detectCompilerName($this->getPlatform(), $this->cppCompiler);
$this->compilerBackend = CompilerFactory::createByName($this->cppCompiler, $this->getPlatform());
}
// 两个路径都不存在,报错
$this->error(
'phpx directory not found. Please either:\n' .
'1. Set PHPX_HOME environment variable to your phpx installation path\n' .
'2. Install phpx via Composer: composer require swoole/phpx'
);
return $this->compilerBackend;
}
public function isWindows(): bool
{
return $this->isWindows;
return $this->getPlatform() instanceof Windows;
}
public function isLinux(): bool
{
return $this->getPlatform() instanceof Linux;
}
public function isMacos(): bool
{
return strtoupper(substr(PHP_OS, 0, 6)) === 'DARWIN';
return $this->getPlatform() instanceof Macos;
}
public function getPhpDir(): string
{
if ($this->isWindows()) {
// Windows 下尝试从环境变量获取 PHP 路径
$phpDir = getenv('PHP_HOME');
if ($phpDir && is_dir($phpDir)) {
return rtrim($phpDir, '\/');
}
// 尝试从 php.exe 路径推断(使用 where 命令)
$phpExe = exec('where php 2>nul');
if ($phpExe) {
$phpDir = dirname($phpExe);
if (is_dir($phpDir)) {
return rtrim($phpDir, '\/');
}
}
// 默认路径
return 'C:\php';
} else {
// Unix/Linux/macOS 下获取 PHP 路径
// 优先级:环境变量 PHP_HOME > php-config > which php
// 1. 尝试环境变量 PHP_HOME
$phpDir = getenv('PHP_HOME');
if ($phpDir && is_dir($phpDir)) {
return rtrim($phpDir, '\/');
}
// 2. 使用 php-config 获取 PHP 路径(优先从 PATH 中查找)
$phpDir = shell_exec('php-config --prefix 2>/dev/null');
if (!empty($phpDir)) {
return trim($phpDir);
}
// 3. 如果 php-config 不可用,尝试从 which php 推断
$phpExe = trim(shell_exec('which php 2>/dev/null'));
if ($phpExe && file_exists($phpExe)) {
$phpDir = dirname(dirname($phpExe));
if (is_dir($phpDir)) {
return $phpDir;
}
}
$this->error('The `php-config` is not found. Please install PHP development package or set PHP_HOME environment variable');
try {
return $this->getPlatform()->getPhpDir();
} catch (\RuntimeException $e) {
$this->error($e->getMessage());
}
}
@ -931,33 +754,7 @@ class CompilerBase extends \PhpAot\Core\Translator
protected function removeCommonPrefix(string $short, string $long): string
{
// Windows 下统一使用反斜杠
if ($this->isWindows()) {
$short = str_replace('/', '\\', $short);
$long = str_replace('/', '\\', $long);
}
$len = min(strlen($short), strlen($long));
$prefixLen = 0;
for ($i = 0; $i < $len; $i++) {
if ($short[$i] === $long[$i]) {
$prefixLen++;
} else {
break;
}
}
$result = substr($long, $prefixLen);
// 移除开头的路径分隔符
if ($this->isWindows()) {
$result = ltrim($result, '\\');
} else {
$result = ltrim($result, '/');
}
return $result;
return $this->getPlatform()->removeCommonPrefix($short, $long);
}
protected function getVarType(string $name): string
@ -1319,7 +1116,7 @@ class CompilerBase extends \PhpAot\Core\Translator
$type = $expr->getType();
switch ($type) {
case 'Scalar_Int':
return $expr->value . ($this->isLinux() ? 'L' : 'LL');
return $expr->value . $this->getPlatform()->getIntegerLiteralSuffix();
case 'Scalar_Float':
return $this->parseScalarFloat($expr);
case 'Scalar_String':
@ -2427,10 +2224,11 @@ class CompilerBase extends \PhpAot\Core\Translator
}
/**
* 解析包含路径
* 获取包含路径
*/
protected function parseIncludes(): string
protected function getIncludePaths(): array
{
$platform = $this->getPlatform();
$includePaths = [
$this->getPhpxDir() . '/include',
$this->getBuildDir() . '/include',
@ -2438,39 +2236,49 @@ class CompilerBase extends \PhpAot\Core\Translator
];
// 根据平台添加 PHP 包含路径
if ($this->platform instanceof \PhpAot\Php\Platform\Windows) {
/** @var \PhpAot\Php\Platform\Windows $platform */
$platform = $this->platform;
if ($platform instanceof Windows) {
$phpSdkPaths = $platform->buildPhpSdkIncludePaths($this->getPhpDir());
$includePaths = array_merge($includePaths, $phpSdkPaths);
} else {
// Linux/macOS
$phpPaths = $this->platform->buildPhpIncludePaths($this->getPhpDir());
$phpPaths = $platform->buildPhpIncludePaths($this->getPhpDir());
$includePaths = array_merge($includePaths, $phpPaths);
}
return $this->platform->getIncludeFlags($includePaths);
return $includePaths;
}
protected function parseLdflags(): string
/**
* 解析包含路径
*/
protected function parseIncludes(): string
{
return $this->getPlatform()->getIncludeFlags($this->getIncludePaths());
}
protected function getLibraryPaths(): array
{
$platform = $this->getPlatform();
$libraryPaths = [
$this->getPhpxDir() . '/lib',
];
// 根据平台添加 PHP 库路径
if ($this->platform instanceof \PhpAot\Php\Platform\Windows) {
/** @var \PhpAot\Php\Platform\Windows $platform */
$platform = $this->platform;
if ($platform instanceof Windows) {
$phpLibPaths = $platform->buildPhpSdkLibPaths($this->getPhpDir());
$libraryPaths = array_merge($libraryPaths, $phpLibPaths);
} else {
// Linux/macOS
$phpLibPaths = $this->platform->buildPhpLibPaths($this->getPhpDir());
$phpLibPaths = $platform->buildPhpLibPaths($this->getPhpDir());
$libraryPaths = array_merge($libraryPaths, $phpLibPaths);
}
$flags = $this->platform->getLibraryPathFlags($libraryPaths);
return $libraryPaths;
}
protected function parseLdflags(): string
{
$flags = $this->getPlatform()->getLibraryPathFlags($this->getLibraryPaths());
// 添加用户自定义的 ldflags
if (!empty($this->ldflags)) {
@ -2481,14 +2289,15 @@ class CompilerBase extends \PhpAot\Core\Translator
}
/**
* 解析库文件
* 获取库文件
*/
protected function parseLibs(): string
protected function getLibraries(): array
{
$platform = $this->getPlatform();
$libraries = [];
// phpx 库(根据平台使用不同的文件名格式)
if ($this->platform instanceof \PhpAot\Php\Platform\Windows) {
if ($platform instanceof Windows) {
// Windows: phpx.lib (无 lib 前缀)
$phpxLibPath = $this->getPhpxDir() . '\\lib\\phpx.lib';
if (file_exists($phpxLibPath)) {
@ -2498,7 +2307,7 @@ class CompilerBase extends \PhpAot\Core\Translator
}
} else {
// Linux/macOS: libphpx.so 或 libphpx.a
$sharedLibExt = $this->platform->getSharedLibraryExtension();
$sharedLibExt = $platform->getSharedLibraryExtension();
// getSharedLibraryExtension() 返回的值可能带点或不带点,需要统一处理
$extWithoutDot = ltrim($sharedLibExt, '.');
$phpxLibPath = $this->getPhpxDir() . '/lib/libphpx.' . $extWithoutDot;
@ -2516,7 +2325,7 @@ class CompilerBase extends \PhpAot\Core\Translator
}
// extension 和 bin 模式都需要链接 PHP 库
if ($this->platform instanceof \PhpAot\Php\Platform\Windows) {
if ($platform instanceof Windows) {
// Windows: 根据构建模式选择不同的库
if ($this->buildMode === 'bin') {
// bin 模式:需要同时链接 php8ts.lib 和 php8embed.lib
@ -2545,7 +2354,15 @@ class CompilerBase extends \PhpAot\Core\Translator
$libraries[] = 'php';
}
return $this->platform->getLibraryFlags($libraries);
return $libraries;
}
/**
* 解析库文件
*/
protected function parseLibs(): string
{
return $this->getPlatform()->getLibraryFlags($this->getLibraries());
}
@ -2573,7 +2390,7 @@ class CompilerBase extends \PhpAot\Core\Translator
'cxxflags' => $this->cxxflags,
];
$cmd .= $this->compilerBackend->buildCompileOptions($config);
$cmd .= $this->getCompilerBackend()->buildCompileOptions($config);
} else {
// 链接时选项
@ -2589,23 +2406,92 @@ class CompilerBase extends \PhpAot\Core\Translator
];
// 添加 RPATH(通过 Platform 层获取,仅 macOS 需要)
if ($this->platform !== null) {
$rpaths = $this->platform->getDefaultRpaths(
$this->getPhpxDir(),
$this->getPhpDir()
);
if (!empty($rpaths)) {
$config['rpath'] = $rpaths;
}
$rpaths = $this->getPlatform()->getDefaultRpaths(
$this->getPhpxDir(),
$this->getPhpDir()
);
if (!empty($rpaths)) {
$config['rpath'] = $rpaths;
}
$cmd .= $this->compilerBackend->buildLinkOptions($config);
$cmd .= $this->getCompilerBackend()->buildLinkOptions($config);
// 最后添加库文件(平台相关,必须在链接选项之后)
$cmd .= ' ' . $this->parseLibs();
}
}
protected function getCompileCommandOptions(): array
{
return [
'include_paths' => $this->getIncludePaths(),
'optimize' => $this->optimizeLevel,
'debug' => $this->debug,
'sanitize' => $this->sanitize,
'cpp_std' => $this->cxxStd,
'is_zts' => $this->isPhpZts,
'build_mode' => $this->buildMode,
'enable_profiler' => $this->enableProfiler,
'suppressed_warnings' => Constants::MSVC_SUPPRESSED_WARNINGS ?? [],
'cxxflags' => $this->cxxflags,
];
}
protected function getCCompileCommandOptions(): array
{
return [
'include_paths' => $this->getIncludePaths(),
'optimize' => 0,
'debug' => $this->debug,
'is_zts' => $this->isPhpZts,
'suppressed_warnings' => ['4244', '4146'],
];
}
protected function getLinkCommandOptions(): array
{
$options = [
'library_paths' => $this->getLibraryPaths(),
'libraries' => $this->getLibraries(),
'ldflags' => $this->ldflags,
'debug' => $this->debug,
'no_console' => $this->noConsole,
'build_mode' => $this->buildMode,
'sanitize' => $this->sanitize,
];
$rpaths = $this->getPlatform()->getDefaultRpaths(
$this->getPhpxDir(),
$this->getPhpDir()
);
if (!empty($rpaths)) {
$options['rpath'] = $rpaths;
}
return $options;
}
protected function getTargetFileName(): string
{
$targetFile = $this->targetName;
$extension = $this->getPlatform()->getTargetExtension($this->buildMode);
if ($extension !== '' && !str_ends_with($targetFile, $extension)) {
$targetFile .= $extension;
}
return $targetFile;
}
protected function buildLinkCommand(array $objectFiles, string $targetFile): string
{
return $this->getCompilerBackend()->buildLinkCommand(
$objectFiles,
$targetFile,
$this->getLinkCommandOptions()
);
}
protected function parseBinaryOpConcat(Expr\BinaryOp\Concat $expr): string
{
@ -4183,7 +4069,7 @@ class CompilerBase extends \PhpAot\Core\Translator
}
// Windows 下可能没有 clang-format,跳过格式化
if ($this->isWindows()) {
if ($this->getPlatform() instanceof Windows) {
return;
}
@ -4883,12 +4769,14 @@ class CompilerBase extends \PhpAot\Core\Translator
if ($scope) {
break;
}
$this->fatalError($expr, "Cannot access protected property `{$property}` of class `{$class}`");
$displayClass = ltrim($class, '\\');
$this->fatalError($expr, "Cannot access protected property `{$property}` of class `{$displayClass}`");
} else {
if ($scope === $findClass) {
break;
}
$this->fatalError($expr, "Cannot access private property `{$property}` of class `{$class}`");
$displayClass = ltrim($class, '\\');
$this->fatalError($expr, "Cannot access private property `{$property}` of class `{$displayClass}`");
}
} elseif ($classDef->extends) {
$findClass = $classDef->extends;

@ -86,6 +86,39 @@ class Linux extends PlatformBase
return '/';
}
public function getDefaultCompiler(): string
{
return 'g++';
}
public function getPhpDir(): string
{
$phpDir = getenv('PHP_HOME');
if ($phpDir && is_dir($phpDir)) {
return rtrim($phpDir, '\/');
}
$phpDir = shell_exec('php-config --prefix 2>/dev/null');
if (!empty($phpDir)) {
return trim($phpDir);
}
$phpExe = trim(shell_exec('which php 2>/dev/null'));
if ($phpExe && file_exists($phpExe)) {
$phpDir = dirname(dirname($phpExe));
if (is_dir($phpDir)) {
return $phpDir;
}
}
throw new \RuntimeException('The `php-config` is not found. Please install PHP development package or set PHP_HOME environment variable');
}
public function getIntegerLiteralSuffix(): string
{
return 'L';
}
/**
* 获取 RPATH 选项
*/

@ -86,6 +86,34 @@ class Macos extends PlatformBase
return '/';
}
public function getDefaultCompiler(): string
{
return 'clang++';
}
public function getPhpDir(): string
{
$phpDir = getenv('PHP_HOME');
if ($phpDir && is_dir($phpDir)) {
return rtrim($phpDir, '\/');
}
$phpDir = shell_exec('php-config --prefix 2>/dev/null');
if (!empty($phpDir)) {
return trim($phpDir);
}
$phpExe = trim(shell_exec('which php 2>/dev/null'));
if ($phpExe && file_exists($phpExe)) {
$phpDir = dirname(dirname($phpExe));
if (is_dir($phpDir)) {
return $phpDir;
}
}
throw new \RuntimeException('The `php-config` is not found. Please install PHP development package or set PHP_HOME environment variable');
}
/**
* 获取 RPATH 选项(macOS 需要绝对路径)
*/

@ -53,6 +53,83 @@ abstract class PlatformBase
*/
abstract public function getPathSeparator(): string;
/**
* 获取该平台默认使用的 C++ 编译器命令
*/
abstract public function getDefaultCompiler(): string;
/**
* 获取 PHP 安装目录
*/
abstract public function getPhpDir(): string;
/**
* 构建 PHP 包含路径
*/
abstract public function buildPhpIncludePaths(string $phpDir): array;
/**
* 构建 PHP 库路径
*/
abstract public function buildPhpLibPaths(string $phpDir): array;
/**
* 检测 PHP 库文件
*/
abstract public function detectPhpLibs(string $phpDir): array;
/**
* 获取指定构建模式的目标文件扩展名
*/
public function getTargetExtension(string $buildMode): string
{
return $buildMode === 'ext'
? '.so'
: $this->getExecutableExtension();
}
/**
* 获取构建前的运行库检查告警
*/
public function getBuildLibraryWarnings(string $phpDir, string $phpxDir, string $buildMode): array
{
if ($buildMode !== 'bin') {
return [];
}
$ext = ltrim($this->getSharedLibraryExtension(), '.');
$warnings = [];
if (!is_file($phpDir . '/lib/libphp.' . $ext)) {
$warnings[] = [
'warning' => "The `libphp.{$ext}` is not found",
'info' => 'Note: If you are building an extension (-m ext), this is OK. For binary mode, please run `make` to build it',
];
}
if (!is_file($phpxDir . '/lib/libphpx.' . $ext)) {
$warnings[] = [
'warning' => "The `libphpx.{$ext}` is not found",
'info' => 'Note: If you are building an extension (-m ext), this is OK. For binary mode, please run `make` to build it',
];
}
return $warnings;
}
/**
* 当前平台是否适合使用 pcntl_fork 并行编译
*/
public function supportsPcntlParallelCompile(): bool
{
return true;
}
public function getIntegerLiteralSuffix(): string
{
return 'LL';
}
/**
* 规范化路径
*/
@ -69,6 +146,28 @@ abstract class PlatformBase
return implode($this->getPathSeparator(), $parts);
}
public function removeCommonPrefix(string $short, string $long): string
{
$separator = $this->getPathSeparator();
if ($separator === '\\') {
$short = str_replace('/', '\\', $short);
$long = str_replace('/', '\\', $long);
}
$len = min(strlen($short), strlen($long));
$prefixLen = 0;
for ($i = 0; $i < $len; $i++) {
if ($short[$i] === $long[$i]) {
$prefixLen++;
} else {
break;
}
}
return ltrim(substr($long, $prefixLen), $separator);
}
/**
* 获取默认的 RPATH 路径列表(仅 macOS 需要)
*

@ -103,6 +103,34 @@ class Windows extends PlatformBase
return '\\';
}
public function getTargetExtension(string $buildMode): string
{
return $buildMode === 'ext' ? '.dll' : '.exe';
}
public function getDefaultCompiler(): string
{
return 'cl';
}
public function getPhpDir(): string
{
$phpDir = getenv('PHP_HOME');
if ($phpDir && is_dir($phpDir)) {
return rtrim($phpDir, '\/');
}
$phpExe = exec('where php 2>nul');
if ($phpExe) {
$phpDir = dirname($phpExe);
if (is_dir($phpDir)) {
return rtrim($phpDir, '\/');
}
}
return 'C:\php';
}
/**
* 获取 PHP 库文件列表
*/
@ -147,6 +175,47 @@ class Windows extends PlatformBase
return '/NODEFAULTLIB:LIBCMT';
}
public function getBuildLibraryWarnings(string $phpDir, string $phpxDir, string $buildMode): array
{
if ($buildMode !== 'bin') {
return [];
}
$warnings = [];
$phpDirs = [
$phpDir . '\SDK\lib',
$phpDir . '\lib',
];
$foundLib = false;
foreach ($phpDirs as $dir) {
if (is_dir($dir) && (is_file($dir . '\php8.lib') || is_file($dir . '\php8ts.lib'))) {
$foundLib = true;
break;
}
}
if (!$foundLib && !is_file($phpDir . '\php8.dll') && !is_file($phpDir . '\php8ts.dll')) {
$warnings[] = [
'warning' => 'The `php8.lib` or `php8.dll` is not found in PHP directory, please check your PHP installation',
];
}
if (!is_file($phpxDir . '\lib\phpx.lib') && !is_file($phpxDir . '\lib\phpx.dll')) {
$warnings[] = [
'warning' => 'The `phpx.lib` or `phpx.dll` is not found in PHX directory',
'info' => 'Note: If you are building an extension (-m ext), this is OK. For binary mode, please run `make` to build it',
];
}
return $warnings;
}
public function supportsPcntlParallelCompile(): bool
{
return false;
}
/**
* 获取调试选项
*/
@ -159,6 +228,16 @@ class Windows extends PlatformBase
return '/DEBUG';
}
public function buildPhpIncludePaths(string $phpDir): array
{
return $this->buildPhpSdkIncludePaths($phpDir);
}
public function buildPhpLibPaths(string $phpDir): array
{
return $this->buildPhpSdkLibPaths($phpDir);
}
/**
* 构建 PHP SDK 包含路径
*/

@ -72,8 +72,7 @@ class Preprocessor extends CompilerBase
{
$info = pathinfo($file);
// Windows 下使用反斜杠,其他平台使用正斜杠
$separator = $this->isWindows() ? '\\' : '/';
$separator = $this->getPlatform()->getPathSeparator();
$relativePath = $this->removeCommonPrefix($this->buildDir, $info['dirname']);
return $this->buildDir . $separator . $relativePath . $separator . $info['filename'] . '.cc';
@ -82,10 +81,10 @@ class Preprocessor extends CompilerBase
public function getObjectFile(string $cppFile): string
{
$info = pathinfo($cppFile);
$ext = $this->isWindows() ? '.obj' : '.o';
$ext = $this->getPlatform()->getObjectExtension();
// 保持与 cppFile 相同的路径分隔符
return $info['dirname'] . ($this->isWindows() ? '\\' : '/') . $info['filename'] . $ext;
return $info['dirname'] . $this->getPlatform()->getPathSeparator() . $info['filename'] . $ext;
}
public function hasCppFileCache(string $file): bool

@ -283,52 +283,10 @@ class Translator extends Preprocessor
{
// 根据平台检查库文件(仅在构建二进制文件时需要)
if ($this->buildMode === 'bin') {
if ($this->isWindows()) {
// Windows 平台检查 dll 和 lib 文件
// 按照新的路径规则检查:SDK/lib, lib, 根目录
$phpDirs = [
$this->getPhpDir() . '\SDK\lib',
$this->getPhpDir() . '\lib',
];
$foundLib = false;
foreach ($phpDirs as $phpDir) {
if (is_dir($phpDir)) {
if (is_file($phpDir . '\php8.lib') || is_file($phpDir . '\php8ts.lib')) {
$foundLib = true;
break;
}
}
}
// 如果没找到 lib,检查根目录的 DLL
if (!$foundLib) {
$phpDll = $this->getPhpDir() . '\php8.dll';
$phpTsDll = $this->getPhpDir() . '\php8ts.dll';
if (!is_file($phpDll) && !is_file($phpTsDll)) {
$this->climate->warning('The `php8.lib` or `php8.dll` is not found in PHP directory, please check your PHP installation');
}
}
$phpxLib = $this->getPhpxDir() . '\lib\phpx.lib';
$phpxDll = $this->getPhpxDir() . '\lib\phpx.dll';
if (!is_file($phpxLib) && !is_file($phpxDll)) {
$this->climate->warning('The `phpx.lib` or `phpx.dll` is not found in PHX directory');
$this->climate->info('Note: If you are building an extension (-m ext), this is OK. For binary mode, please run `make` to build it');
}
} else {
// Unix/Linux/macOS 平台检查 .so 或 .dylib 文件
$ext = $this->isMacos() ? 'dylib' : 'so';
$phpLib = $this->getPhpDir() . '/lib/libphp.' . $ext;
$phpxLib = $this->getPhpxDir() . '/lib/libphpx.' . $ext;
if (!is_file($phpLib)) {
$this->climate->warning("The `libphp.{$ext}` is not found");
$this->climate->info('Note: If you are building an extension (-m ext), this is OK. For binary mode, please run `make` to build it');
}
if (!is_file($phpxLib)) {
$this->climate->warning("The `libphpx.{$ext}` is not found");
$this->climate->info('Note: If you are building an extension (-m ext), this is OK. For binary mode, please run `make` to build it');
foreach ($this->getPlatform()->getBuildLibraryWarnings($this->getPhpDir(), $this->getPhpxDir(), $this->buildMode) as $message) {
$this->climate->warning($message['warning']);
if (!empty($message['info'])) {
$this->climate->info($message['info']);
}
}
}
@ -743,38 +701,19 @@ CODE;
// 使用 Backend 层构建编译命令
if ($isCppFile) {
// C++ 文件:使用标准的编译命令构建
$cmd = $this->compilerBackend->buildCompileCommand(
$cmd = $this->getCompilerBackend()->buildCompileCommand(
$cppFile,
$objectFile,
[
'optimize' => $this->optimizeLevel,
'debug' => $this->debug,
'sanitize' => $this->sanitize,
'cpp_std' => $this->cxxStd,
'is_zts' => $this->isPhpZts,
'build_mode' => $this->buildMode,
'enable_profiler' => $this->enableProfiler,
'suppressed_warnings' => Constants::MSVC_SUPPRESSED_WARNINGS ?? [],
'cxxflags' => $this->cxxflags,
]
$this->getCompileCommandOptions()
);
// 添加包含路径(通过 Platform 层)
$cmd .= ' ' . $this->parseIncludes();
} else {
// C 文件:不能使用 C++ 特定选项(/EHsc, /std:c++17 等)
// 使用 Backend 的 buildCCompileCommand() 方法
$cmd = $this->compilerBackend->buildCCompileCommand(
$cmd = $this->getCompilerBackend()->buildCCompileCommand(
$cppFile,
$objectFile,
[
'optimize' => 0,
'suppressed_warnings' => ['4244', '4146'],
]
$this->getCCompileCommandOptions()
);
// 手动添加包含路径
$cmd .= ' ' . $this->parseIncludes();
}
if (!$parallel) {
@ -814,8 +753,7 @@ CODE;
$sourceFiles[] = $this->getPhpxDir() . '/src/misc/ps_title.c';
}
// Windows 不支持 pcntl_fork,使用串行编译或 proc_open
if ($this->isWindows() or $job <= 1) {
if (!$this->getPlatform()->supportsPcntlParallelCompile() or $job <= 1) {
return $this->compileSourceFile($sourceFiles);
}
@ -956,40 +894,8 @@ CODE;
public function build(array $objectFiles): void
{
$targetFile = $this->targetName;
// 根据平台设置目标文件扩展名
if ($this->isWindows()) {
if ($this->buildMode == 'ext') {
if (!str_ends_with($targetFile, '.dll')) {
$targetFile .= '.dll';
}
} else {
if (!str_ends_with($targetFile, '.exe')) {
$targetFile .= '.exe';
}
}
} else {
if ($this->buildMode == 'ext' and !str_ends_with($targetFile, '.so')) {
$targetFile .= '.so';
}
}
// 根据平台构建链接命令
if ($this->isWindows()) {
// Windows 使用 link.exe 或 lld-link 进行链接
$objectList = implode(' ', $objectFiles);
// 使用检测到的链接器(lld-link 或 link)
$linkerCmd = $this->linker;
$linkCmd = "{$linkerCmd} /nologo {$objectList} /OUT:{$targetFile}";
} else {
// Unix/Linux/macOS GCC 链接命令
$objectList = implode(' ', $objectFiles);
$linkCmd = $this->cppCompiler . ' ' . $objectList . ' -o ' . $targetFile;
}
$this->addCompilationOption($linkCmd, true);
$targetFile = $this->getTargetFileName();
$linkCmd = $this->buildLinkCommand($objectFiles, $targetFile);
$this->climate->comment($linkCmd);
// 执行链接并捕获输出

Loading…
Cancel
Save