TypePHP 编译器 https://swoole.com/aot/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

124 lines
4.6 KiB

<?php
namespace TypePhp\Installer;
final class LibPhpxInstaller
{
public function __construct(private readonly InteractiveConsole $console = new InteractiveConsole())
{
}
public function ensure(string $phpxDir, string $phpDir): ?string
{
$phpxDir = rtrim($phpxDir, '/');
$phpDir = rtrim($phpDir, '/');
if (PHP_OS_FAMILY !== 'Linux' || $this->hasLibPhpx($phpxDir)) {
return $phpxDir;
}
if (!$this->console->isInteractive()) {
$this->console->write('libphpx.so is missing. Run tpc.php in an interactive terminal to build it automatically, or set PHPX_HOME.');
return null;
}
$this->validateSourceTree($phpxDir);
$this->validatePhpInstallation($phpDir);
$this->console->write("The PHPX installation does not provide libphpx.so: {$phpxDir}");
if (!$this->console->confirm('Build libphpx.so now?', true)) {
return null;
}
$this->installBuildDependencies($phpDir);
$buildDir = $phpxDir . '/build';
$this->mkdir($buildDir);
$this->run([
'cmake',
'-S', $phpxDir,
'-B', $buildDir,
'-DCMAKE_BUILD_TYPE=Release',
'-DBUILD_TESTS=OFF',
'-Dphp_dir=' . $phpDir,
], $phpDir);
$this->run([
'cmake',
'--build', $buildDir,
'--parallel', (string) min(8, max(1, $this->cpuCount())),
'--target', 'phpx',
], $phpDir);
if (!$this->hasLibPhpx($phpxDir)) {
throw new \RuntimeException("Build completed but {$phpxDir}/lib/libphpx.so was not created");
}
$this->console->write("libphpx.so built successfully in {$phpxDir}/lib");
return $phpxDir;
}
public function hasLibPhpx(string $phpxDir): bool
{
return is_file(rtrim($phpxDir, '/') . '/lib/libphpx.so');
}
private function validateSourceTree(string $phpxDir): void
{
if (!is_file($phpxDir . '/CMakeLists.txt') || !is_dir($phpxDir . '/src') || !is_dir($phpxDir . '/include')) {
throw new \RuntimeException("PHPX source tree is incomplete: {$phpxDir}");
}
}
private function validatePhpInstallation(string $phpDir): void
{
$php = $phpDir . '/bin/php';
$phpConfig = $phpDir . '/bin/php-config';
if (!is_executable($php) || !is_executable($phpConfig)) {
throw new \RuntimeException("PHP installation must provide bin/php and bin/php-config: {$phpDir}");
}
}
private function installBuildDependencies(string $phpDir): void
{
$manager = LinuxPackageManager::detect();
if ($manager === null) {
$this->console->write('No supported package manager (apt-get/dnf/yum) was found; continuing with existing build tools.');
return;
}
$packages = $manager->missingPackages($manager->packagesForBuildTools());
$this->console->write('Detected package manager: ' . $manager->command);
if ($packages === []) {
$this->console->write('All detected PHPX build dependencies are already installed.');
return;
}
if (!$this->console->confirm('Install missing PHPX build dependencies (' . implode(', ', $packages) . ')?', true)) {
return;
}
$useSudo = function_exists('posix_geteuid') && posix_geteuid() !== 0;
$refresh = $manager->refreshCommand($useSudo);
if ($refresh !== null) {
$this->run($refresh, $phpDir);
}
$this->run($manager->installCommand($packages, $useSudo), $phpDir);
}
private function run(array $command, string $phpDir): void
{
$this->console->write('$ ' . implode(' ', array_map('escapeshellarg', $command)));
$environment = getenv();
$environment['PHP_HOME'] = $phpDir;
$environment['PATH'] = $phpDir . '/bin:' . ($environment['PATH'] ?? '');
$process = proc_open($command, [STDIN, STDOUT, STDERR], $pipes, null, $environment);
if (!is_resource($process) || proc_close($process) !== 0) {
throw new \RuntimeException('Command failed: ' . implode(' ', $command));
}
}
private function mkdir(string $directory): void
{
if (!is_dir($directory) && !mkdir($directory, 0755, true) && !is_dir($directory)) {
throw new \RuntimeException("Unable to create directory {$directory}");
}
}
private function cpuCount(): int
{
$count = (int) trim((string) shell_exec('getconf _NPROCESSORS_ONLN 2>/dev/null'));
return $count > 0 ? $count : 1;
}
}