From 7c8b40a49a9fc9787f60e3c28da173b06ddffa09 Mon Sep 17 00:00:00 2001 From: crazywhalecc Date: Thu, 11 Dec 2025 13:49:32 +0800 Subject: [PATCH] Add windows php cli builds, support micro patches --- src/Package/Target/php.php | 441 +---------------------- src/StaticPHP/Package/PackageBuilder.php | 13 +- src/StaticPHP/Runtime/Shell/Shell.php | 3 - src/StaticPHP/Util/SourcePatcher.php | 66 ++++ 4 files changed, 80 insertions(+), 443 deletions(-) diff --git a/src/Package/Target/php.php b/src/Package/Target/php.php index 88e01858..8cd6e940 100644 --- a/src/Package/Target/php.php +++ b/src/Package/Target/php.php @@ -4,19 +4,16 @@ declare(strict_types=1); namespace Package\Target; +use Package\Target\php\unix; +use Package\Target\php\windows; use StaticPHP\Attribute\Package\BeforeStage; -use StaticPHP\Attribute\Package\BuildFor; use StaticPHP\Attribute\Package\Info; use StaticPHP\Attribute\Package\InitPackage; use StaticPHP\Attribute\Package\ResolveBuild; -use StaticPHP\Attribute\Package\Stage; use StaticPHP\Attribute\Package\Target; use StaticPHP\Attribute\Package\Validate; -use StaticPHP\Attribute\PatchDescription; use StaticPHP\Config\PackageConfig; use StaticPHP\DI\ApplicationContext; -use StaticPHP\Exception\EnvironmentException; -use StaticPHP\Exception\SPCException; use StaticPHP\Exception\WrongUsageException; use StaticPHP\Package\Package; use StaticPHP\Package\PackageBuilder; @@ -28,16 +25,11 @@ use StaticPHP\Registry\PackageLoader; use StaticPHP\Runtime\SystemTarget; use StaticPHP\Toolchain\Interface\ToolchainInterface; use StaticPHP\Toolchain\ToolchainManager; -use StaticPHP\Util\DirDiff; use StaticPHP\Util\FileSystem; -use StaticPHP\Util\InteractiveTerm; use StaticPHP\Util\SourcePatcher; -use StaticPHP\Util\SPCConfigUtil; -use StaticPHP\Util\System\UnixUtil; use StaticPHP\Util\V2CompatLayer; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputOption; -use ZM\Logger\ConsoleColor; #[Target('php')] #[Target('php-cli')] @@ -48,6 +40,9 @@ use ZM\Logger\ConsoleColor; #[Target('frankenphp')] class php extends TargetPackage { + use unix; + use windows; + public static function getPHPVersionID(): int { $artifact = ArtifactLoader::getArtifactInstance('php-src'); @@ -242,343 +237,6 @@ class php extends TargetPackage FileSystem::removeDir(BUILD_MODULES_PATH); } - #[BeforeStage('php', [self::class, 'buildconfForUnix'], 'php')] - #[PatchDescription('Patch configure.ac for musl and musl-toolchain')] - #[PatchDescription('Let php m4 tools use static pkg-config')] - public function patchBeforeBuildconf(TargetPackage $package): void - { - // patch configure.ac for musl and musl-toolchain - $musl = SystemTarget::getTargetOS() === 'Linux' && SystemTarget::getLibc() === 'musl'; - FileSystem::backupFile(SOURCE_PATH . '/php-src/configure.ac'); - FileSystem::replaceFileStr( - SOURCE_PATH . '/php-src/configure.ac', - 'if command -v ldd >/dev/null && ldd --version 2>&1 | grep ^musl >/dev/null 2>&1', - 'if ' . ($musl ? 'true' : 'false') - ); - - // let php m4 tools use static pkg-config - FileSystem::replaceFileStr("{$package->getSourceDir()}/build/php.m4", 'PKG_CHECK_MODULES(', 'PKG_CHECK_MODULES_STATIC('); - } - - #[Stage] - public function buildconfForUnix(TargetPackage $package): void - { - InteractiveTerm::setMessage('Building php: ' . ConsoleColor::yellow('./buildconf')); - V2CompatLayer::emitPatchPoint('before-php-buildconf'); - shell()->cd($package->getSourceDir())->exec(getenv('SPC_CMD_PREFIX_PHP_BUILDCONF')); - } - - #[Stage] - public function configureForUnix(TargetPackage $package, PackageInstaller $installer): void - { - InteractiveTerm::setMessage('Building php: ' . ConsoleColor::yellow('./configure')); - V2CompatLayer::emitPatchPoint('before-php-configure'); - $cmd = getenv('SPC_CMD_PREFIX_PHP_CONFIGURE'); - - $args = []; - $version_id = self::getPHPVersionID(); - // PHP JSON extension is built-in since PHP 8.0 - if ($version_id < 80000) { - $args[] = '--enable-json'; - } - // zts - if ($package->getBuildOption('enable-zts', false)) { - $args[] = '--enable-zts --disable-zend-signals'; - if ($version_id >= 80100 && SystemTarget::getTargetOS() === 'Linux') { - $args[] = '--enable-zend-max-execution-timers'; - } - } - // config-file-path and config-file-scan-dir - if ($option = $package->getBuildOption('with-config-file-path', false)) { - $args[] = "--with-config-file-path={$option}"; - } - if ($option = $package->getBuildOption('with-config-file-scan-dir', false)) { - $args[] = "--with-config-file-scan-dir={$option}"; - } - // perform enable cli options - $args[] = $installer->isPackageResolved('php-cli') ? '--enable-cli' : '--disable-cli'; - $args[] = $installer->isPackageResolved('php-fpm') ? '--enable-fpm' : '--disable-fpm'; - $args[] = $installer->isPackageResolved('php-micro') ? match (SystemTarget::getTargetOS()) { - 'Linux' => '--enable-micro=all-static', - default => '--enable-micro', - } : null; - $args[] = $installer->isPackageResolved('php-cgi') ? '--enable-cgi' : '--disable-cgi'; - $embed_type = getenv('SPC_CMD_VAR_PHP_EMBED_TYPE') ?: 'static'; - $args[] = $installer->isPackageResolved('php-embed') ? "--enable-embed={$embed_type}" : '--disable-embed'; - $args[] = getenv('SPC_EXTRA_PHP_VARS') ?: null; - $args = implode(' ', array_filter($args)); - - $static_extension_str = $this->makeStaticExtensionString($installer); - - // run ./configure with args - $this->seekPhpSrcLogFileOnException(fn () => shell()->cd($package->getSourceDir())->setEnv([ - 'CFLAGS' => getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS'), - 'CPPFLAGS' => "-I{$package->getIncludeDir()}", - 'LDFLAGS' => "-L{$package->getLibDir()} " . getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS'), - ])->exec("{$cmd} {$args} {$static_extension_str}"), $package->getSourceDir()); - } - - #[Stage] - public function makeForUnix(TargetPackage $package, PackageInstaller $installer): void - { - V2CompatLayer::emitPatchPoint('before-php-make'); - - logger()->info('cleaning up php-src build files'); - shell()->cd($package->getSourceDir())->exec('make clean'); - - if ($installer->isPackageResolved('php-cli')) { - $package->runStage([self::class, 'makeCliForUnix']); - } - if ($installer->isPackageResolved('php-cgi')) { - $package->runStage([self::class, 'makeCgiForUnix']); - } - if ($installer->isPackageResolved('php-fpm')) { - $package->runStage([self::class, 'makeFpmForUnix']); - } - if ($installer->isPackageResolved('php-micro')) { - $package->runStage([self::class, 'makeMicroForUnix']); - } - if ($installer->isPackageResolved('php-embed')) { - $package->runStage([self::class, 'makeEmbedForUnix']); - } - } - - #[Stage] - public function makeCliForUnix(TargetPackage $package, PackageInstaller $installer, PackageBuilder $builder): void - { - InteractiveTerm::setMessage('Building php: ' . ConsoleColor::yellow('make cli')); - $concurrency = $builder->concurrency; - shell()->cd($package->getSourceDir()) - ->setEnv($this->makeVars($installer)) - ->exec("make -j{$concurrency} cli"); - - $builder->deployBinary("{$package->getSourceDir()}/sapi/cli/php", BUILD_BIN_PATH . '/php'); - $package->setOutput('Binary path for cli SAPI', BUILD_BIN_PATH . '/php'); - } - - #[Stage] - public function makeCgiForUnix(TargetPackage $package, PackageInstaller $installer, PackageBuilder $builder): void - { - InteractiveTerm::setMessage('Building php: ' . ConsoleColor::yellow('make cgi')); - $concurrency = $builder->concurrency; - shell()->cd($package->getSourceDir()) - ->setEnv($this->makeVars($installer)) - ->exec("make -j{$concurrency} cgi"); - - $builder->deployBinary("{$package->getSourceDir()}/sapi/cgi/php-cgi", BUILD_BIN_PATH . '/php-cgi'); - $package->setOutput('Binary path for cgi SAPI', BUILD_BIN_PATH . '/php-cgi'); - } - - #[Stage] - public function makeFpmForUnix(TargetPackage $package, PackageInstaller $installer, PackageBuilder $builder): void - { - InteractiveTerm::setMessage('Building php: ' . ConsoleColor::yellow('make fpm')); - $concurrency = $builder->concurrency; - shell()->cd($package->getSourceDir()) - ->setEnv($this->makeVars($installer)) - ->exec("make -j{$concurrency} fpm"); - - $builder->deployBinary("{$package->getSourceDir()}/sapi/fpm/php-fpm", BUILD_BIN_PATH . '/php-fpm'); - $package->setOutput('Binary path for fpm SAPI', BUILD_BIN_PATH . '/php-fpm'); - } - - #[Stage] - #[PatchDescription('Patch phar extension for micro SAPI to support compressed phar')] - public function makeMicroForUnix(TargetPackage $package, PackageInstaller $installer, PackageBuilder $builder): void - { - $phar_patched = false; - try { - if ($installer->isPackageResolved('ext-phar')) { - $phar_patched = true; - SourcePatcher::patchMicroPhar(self::getPHPVersionID()); - } - InteractiveTerm::setMessage('Building php: ' . ConsoleColor::yellow('make micro')); - // apply --with-micro-fake-cli option - $vars = $this->makeVars($installer); - $vars['EXTRA_CFLAGS'] .= $package->getBuildOption('with-micro-fake-cli', false) ? ' -DPHP_MICRO_FAKE_CLI' : ''; - // build - shell()->cd($package->getSourceDir()) - ->setEnv($vars) - ->exec("make -j{$builder->concurrency} micro"); - - $builder->deployBinary($package->getSourceDir() . '/sapi/micro/micro.sfx', BUILD_BIN_PATH . '/micro.sfx'); - $package->setOutput('Binary path for micro SAPI', BUILD_BIN_PATH . '/micro.sfx'); - } finally { - if ($phar_patched) { - SourcePatcher::unpatchMicroPhar(); - } - } - } - - #[Stage] - public function makeEmbedForUnix(TargetPackage $package, PackageInstaller $installer, PackageBuilder $builder): void - { - InteractiveTerm::setMessage('Building php: ' . ConsoleColor::yellow('make embed')); - $shared_exts = array_filter( - $installer->getResolvedPackages(), - static fn ($x) => $x instanceof PhpExtensionPackage && $x->isBuildShared() && $x->isBuildWithPhp() - ); - $install_modules = $shared_exts ? 'install-modules' : ''; - - // detect changes in module path - $diff = new DirDiff(BUILD_MODULES_PATH, true); - - $root = BUILD_ROOT_PATH; - $sed_prefix = SystemTarget::getTargetOS() === 'Darwin' ? 'sed -i ""' : 'sed -i'; - - shell()->cd($package->getSourceDir()) - ->setEnv($this->makeVars($installer)) - ->exec("{$sed_prefix} \"s|^EXTENSION_DIR = .*|EXTENSION_DIR = /" . basename(BUILD_MODULES_PATH) . '|" Makefile') - ->exec("make -j{$builder->concurrency} INSTALL_ROOT={$root} install-sapi {$install_modules} install-build install-headers install-programs"); - - // ------------- SPC_CMD_VAR_PHP_EMBED_TYPE=shared ------------- - - // process libphp.so for shared embed - $suffix = SystemTarget::getTargetOS() === 'Darwin' ? 'dylib' : 'so'; - $libphp_so = "{$package->getLibDir()}/libphp.{$suffix}"; - if (file_exists($libphp_so)) { - // rename libphp.so if -release is set - if (SystemTarget::getTargetOS() === 'Linux') { - $this->processLibphpSoFile($libphp_so, $installer); - } - // deploy - $builder->deployBinary($libphp_so, $libphp_so, false); - $package->setOutput('Library path for embed SAPI', $libphp_so); - } - - // process shared extensions that built-with-php - $increment_files = $diff->getChangedFiles(); - $files = []; - foreach ($increment_files as $increment_file) { - $builder->deployBinary($increment_file, $increment_file, false); - $files[] = basename($increment_file); - } - if (!empty($files)) { - $package->setOutput('Built shared extensions', implode(', ', $files)); - } - - // ------------- SPC_CMD_VAR_PHP_EMBED_TYPE=static ------------- - - // process libphp.a for static embed - if (!file_exists("{$package->getLibDir()}/libphp.a")) { - return; - } - $ar = getenv('AR') ?: 'ar'; - $libphp_a = "{$package->getLibDir()}/libphp.a"; - shell()->exec("{$ar} -t {$libphp_a} | grep '\\.a$' | xargs -n1 {$ar} d {$libphp_a}"); - UnixUtil::exportDynamicSymbols($libphp_a); - - // deploy embed php scripts - $package->runStage([$this, 'patchEmbedScripts']); - } - - #[Stage] - public function unixBuildSharedExt(PackageInstaller $installer, ToolchainInterface $toolchain): void - { - // collect shared extensions - /** @var PhpExtensionPackage[] $shared_extensions */ - $shared_extensions = array_filter( - $installer->getResolvedPackages(PhpExtensionPackage::class), - fn ($x) => $x->isBuildShared() && !$x->isBuildWithPhp() - ); - if (!empty($shared_extensions)) { - if ($toolchain->isStatic()) { - throw new WrongUsageException( - "You're building against musl libc statically (the default on Linux), but you're trying to build shared extensions.\n" . - 'Static musl libc does not implement `dlopen`, so your php binary is not able to load shared extensions.' . "\n" . - 'Either use SPC_LIBC=glibc to link against glibc on a glibc OS, or use SPC_TARGET="native-native-musl -dynamic" to link against musl libc dynamically using `zig cc`.' - ); - } - FileSystem::createDir(BUILD_MODULES_PATH); - - // backup - FileSystem::backupFile(BUILD_BIN_PATH . '/php-config'); - FileSystem::backupFile(BUILD_LIB_PATH . '/php/build/phpize.m4'); - - FileSystem::replaceFileLineContainsString(BUILD_BIN_PATH . '/php-config', 'extension_dir=', 'extension_dir="' . BUILD_MODULES_PATH . '"'); - FileSystem::replaceFileStr(BUILD_LIB_PATH . '/php/build/phpize.m4', 'test "[$]$1" = "no" && $1=yes', '# test "[$]$1" = "no" && $1=yes'); - } - - try { - logger()->debug('Building shared extensions...'); - foreach ($shared_extensions as $extension) { - InteractiveTerm::setMessage('Building shared PHP extension: ' . ConsoleColor::yellow($extension->getName())); - $extension->buildShared(); - } - } finally { - // restore php-config - if (!empty($shared_extensions)) { - FileSystem::restoreBackupFile(BUILD_BIN_PATH . '/php-config'); - FileSystem::restoreBackupFile(BUILD_LIB_PATH . '/php/build/phpize.m4'); - } - } - } - - #[BuildFor('Darwin')] - #[BuildFor('Linux')] - public function build(TargetPackage $package): void - { - // virtual target, do nothing - if ($package->getName() !== 'php') { - return; - } - - $package->runStage([$this, 'buildconfForUnix']); - $package->runStage([$this, 'configureForUnix']); - $package->runStage([$this, 'makeForUnix']); - - $package->runStage([$this, 'unixBuildSharedExt']); - } - - #[BuildFor('Windows')] - public function buildWin(TargetPackage $package): void - { - throw new EnvironmentException('Not implemented'); - } - - /** - * Patch phpize and php-config if needed - */ - #[Stage] - public function patchEmbedScripts(): void - { - // patch phpize - if (file_exists(BUILD_BIN_PATH . '/phpize')) { - logger()->debug('Patching phpize prefix'); - FileSystem::replaceFileStr(BUILD_BIN_PATH . '/phpize', "prefix=''", "prefix='" . BUILD_ROOT_PATH . "'"); - FileSystem::replaceFileStr(BUILD_BIN_PATH . '/phpize', 's##', 's#/usr/local#'); - $this->setOutput('phpize script path for embed SAPI', BUILD_BIN_PATH . '/phpize'); - } - // patch php-config - if (file_exists(BUILD_BIN_PATH . '/php-config')) { - logger()->debug('Patching php-config prefix and libs order'); - $php_config_str = FileSystem::readFile(BUILD_BIN_PATH . '/php-config'); - $php_config_str = str_replace('prefix=""', 'prefix="' . BUILD_ROOT_PATH . '"', $php_config_str); - // move mimalloc to the beginning of libs - $php_config_str = preg_replace('/(libs=")(.*?)\s*(' . preg_quote(BUILD_LIB_PATH, '/') . '\/mimalloc\.o)\s*(.*?)"/', '$1$3 $2 $4"', $php_config_str); - // move lstdc++ to the end of libs - $php_config_str = preg_replace('/(libs=")(.*?)\s*(-lstdc\+\+)\s*(.*?)"/', '$1$2 $4 $3"', $php_config_str); - FileSystem::writeFile(BUILD_BIN_PATH . '/php-config', $php_config_str); - $this->setOutput('php-config script path for embed SAPI', BUILD_BIN_PATH . '/php-config'); - } - } - - /** - * Seek php-src/config.log when building PHP, add it to exception. - */ - protected function seekPhpSrcLogFileOnException(callable $callback, string $source_dir): void - { - try { - $callback(); - } catch (SPCException $e) { - if (file_exists("{$source_dir}/config.log")) { - $e->addExtraLogFile('php-src config.log', 'php-src.config.log'); - copy("{$source_dir}/config.log", SPC_LOGS_DIR . '/php-src.config.log'); - } - throw $e; - } - } - private function makeStaticExtensionString(PackageInstaller $installer): string { $arg = []; @@ -599,93 +257,4 @@ class php extends TargetPackage logger()->debug("Static extension configure args: {$str}"); return $str; } - - /** - * Make environment variables for php make. - * This will call SPCConfigUtil to generate proper LDFLAGS and LIBS for static linking. - */ - private function makeVars(PackageInstaller $installer): array - { - $config = (new SPCConfigUtil(['libs_only_deps' => true]))->config(array_map(fn ($x) => $x->getName(), $installer->getResolvedPackages())); - $static = ApplicationContext::get(ToolchainInterface::class)->isStatic() ? '-all-static' : ''; - $pie = SystemTarget::getTargetOS() === 'Linux' ? '-pie' : ''; - - return array_filter([ - 'EXTRA_CFLAGS' => getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS'), - 'EXTRA_LDFLAGS_PROGRAM' => getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS') . "{$config['ldflags']} {$static} {$pie}", - 'EXTRA_LDFLAGS' => $config['ldflags'], - 'EXTRA_LIBS' => $config['libs'], - ]); - } - - /** - * Rename libphp.so to libphp-.so if -release is set in LDFLAGS. - */ - private function processLibphpSoFile(string $libphpSo, PackageInstaller $installer): void - { - $ldflags = getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS') ?: ''; - $libDir = BUILD_LIB_PATH; - $modulesDir = BUILD_MODULES_PATH; - $realLibName = 'libphp.so'; - $cwd = getcwd(); - - if (preg_match('/-release\s+(\S+)/', $ldflags, $matches)) { - $release = $matches[1]; - $realLibName = "libphp-{$release}.so"; - $libphpRelease = "{$libDir}/{$realLibName}"; - if (!file_exists($libphpRelease) && file_exists($libphpSo)) { - rename($libphpSo, $libphpRelease); - } - if (file_exists($libphpRelease)) { - chdir($libDir); - if (file_exists($libphpSo)) { - unlink($libphpSo); - } - symlink($realLibName, 'libphp.so'); - shell()->exec(sprintf( - 'patchelf --set-soname %s %s', - escapeshellarg($realLibName), - escapeshellarg($libphpRelease) - )); - } - if (is_dir($modulesDir)) { - chdir($modulesDir); - foreach ($installer->getResolvedPackages(PhpExtensionPackage::class) as $ext) { - if (!$ext->isBuildShared()) { - continue; - } - $name = $ext->getName(); - $versioned = "{$name}-{$release}.so"; - $unversioned = "{$name}.so"; - $src = "{$modulesDir}/{$versioned}"; - $dst = "{$modulesDir}/{$unversioned}"; - if (is_file($src)) { - rename($src, $dst); - shell()->exec(sprintf( - 'patchelf --set-soname %s %s', - escapeshellarg($unversioned), - escapeshellarg($dst) - )); - } - } - } - chdir($cwd); - } - - $target = "{$libDir}/{$realLibName}"; - if (file_exists($target)) { - [, $output] = shell()->execWithResult('readelf -d ' . escapeshellarg($target)); - $output = implode("\n", $output); - if (preg_match('/SONAME.*\[(.+)]/', $output, $sonameMatch)) { - $currentSoname = $sonameMatch[1]; - if ($currentSoname !== basename($target)) { - shell()->exec(sprintf( - 'patchelf --set-soname %s %s', - escapeshellarg(basename($target)), - escapeshellarg($target) - )); - } - } - } - } } diff --git a/src/StaticPHP/Package/PackageBuilder.php b/src/StaticPHP/Package/PackageBuilder.php index e2373253..94c55a66 100644 --- a/src/StaticPHP/Package/PackageBuilder.php +++ b/src/StaticPHP/Package/PackageBuilder.php @@ -99,7 +99,7 @@ class PackageBuilder // ignore copy to self if (realpath($src) !== realpath($dst)) { - shell()->exec('cp ' . escapeshellarg($src) . ' ' . escapeshellarg($dst)); + FileSystem::copy($src, $dst); } // file exist @@ -111,7 +111,7 @@ class PackageBuilder $this->extractDebugInfo($dst); // strip - if (!$this->getOption('no-strip')) { + if (!$this->getOption('no-strip') && SystemTarget::isUnix()) { $this->stripBinary($dst); } @@ -123,6 +123,9 @@ class PackageBuilder } logger()->info("Compressing {$dst} with UPX"); shell()->exec(getenv('UPX_EXEC') . " --best {$dst}"); + } elseif ($upx_option && SystemTarget::getTargetOS() === 'Windows' && $executable) { + logger()->info("Compressing {$dst} with UPX"); + shell()->exec(getenv('UPX_EXEC') . ' --best ' . escapeshellarg($dst)); } return $dst; @@ -136,12 +139,13 @@ class PackageBuilder public function extractDebugInfo(string $binary_path): string { $target_dir = BUILD_ROOT_PATH . '/debug'; - FileSystem::createDir($target_dir); $basename = basename($binary_path); $debug_file = "{$target_dir}/{$basename}" . (SystemTarget::getTargetOS() === 'Darwin' ? '.dwarf' : '.debug'); if (SystemTarget::getTargetOS() === 'Darwin') { + FileSystem::createDir($target_dir); shell()->exec("dsymutil -f {$binary_path} -o {$debug_file}"); } elseif (SystemTarget::getTargetOS() === 'Linux') { + FileSystem::createDir($target_dir); if ($eu_strip = LinuxUtil::findCommand('eu-strip')) { shell() ->exec("{$eu_strip} -f {$debug_file} {$binary_path}") @@ -152,7 +156,8 @@ class PackageBuilder ->exec("objcopy --add-gnu-debuglink={$debug_file} {$binary_path}"); } } else { - throw new SPCInternalException('extractDebugInfo is only supported on Linux and macOS'); + logger()->debug('extractDebugInfo is only supported on Linux and macOS'); + return ''; } return $debug_file; } diff --git a/src/StaticPHP/Runtime/Shell/Shell.php b/src/StaticPHP/Runtime/Shell/Shell.php index 1368c017..bf30d9d8 100644 --- a/src/StaticPHP/Runtime/Shell/Shell.php +++ b/src/StaticPHP/Runtime/Shell/Shell.php @@ -151,9 +151,6 @@ abstract class Shell bool $throw_on_error = true, ?string $cwd = null ): array { - if ($cwd !== null) { - $cwd = $cwd; - } $file_res = null; if ($this->enable_log_file) { // write executed command to the log file using fwrite diff --git a/src/StaticPHP/Util/SourcePatcher.php b/src/StaticPHP/Util/SourcePatcher.php index 42ea8c36..6a16f041 100644 --- a/src/StaticPHP/Util/SourcePatcher.php +++ b/src/StaticPHP/Util/SourcePatcher.php @@ -6,6 +6,7 @@ namespace StaticPHP\Util; use StaticPHP\Attribute\PatchDescription; use StaticPHP\Exception\PatchException; +use StaticPHP\Registry\PackageLoader; /** * SourcePatcher provides static utility methods for patching source files. @@ -194,4 +195,69 @@ class SourcePatcher { FileSystem::restoreBackupFile(SOURCE_PATH . '/php-src/ext/phar/phar.c'); } + + public static function patchPhpSrc(?array $items = null): bool + { + $patch_dir = ROOT_DIR . '/src/globals/patch/php-src-patches'; + // in phar mode, we need to extract all the patch files + if (str_starts_with($patch_dir, 'phar://')) { + $tmp_dir = sys_get_temp_dir() . '/php-src-patches'; + FileSystem::createDir($tmp_dir); + foreach (FileSystem::scanDirFiles($patch_dir) as $file) { + FileSystem::writeFile("{$tmp_dir}/" . basename($file), file_get_contents($file)); + } + $patch_dir = $tmp_dir; + } + $php_package = PackageLoader::getTargetPackage('php'); + if (!file_exists("{$php_package->getSourceDir()}/sapi/micro/php_micro.c")) { + return false; + } + $ver_file = "{$php_package->getSourceDir()}/main/php_version.h"; + if (!file_exists($ver_file)) { + throw new PatchException('php-src patcher (original micro patches)', 'Patch failed, cannot find php source files'); + } + $version_h = FileSystem::readFile("{$php_package->getSourceDir()}/main/php_version.h"); + preg_match('/#\s*define\s+PHP_MAJOR_VERSION\s+(\d+)\s+#\s*define\s+PHP_MINOR_VERSION\s+(\d+)\s+/m', $version_h, $match); + // $ver = "{$match[1]}.{$match[2]}"; + + $major_ver = $match[1] . $match[2]; + if ($major_ver === '74') { + return false; + } + // $check = !defined('DEBUG_MODE') ? ' -q' : ''; + // f_passthru('cd ' . SOURCE_PATH . '/php-src && git checkout' . $check . ' HEAD'); + + if ($items !== null) { + $spc_micro_patches = $items; + } else { + $spc_micro_patches = getenv('SPC_MICRO_PATCHES'); + $spc_micro_patches = $spc_micro_patches === false ? [] : explode(',', $spc_micro_patches); + } + $spc_micro_patches = array_filter($spc_micro_patches, fn ($item) => trim((string) $item) !== ''); + $patch_list = $spc_micro_patches; + $patches = []; + $serial = ['80', '81', '82', '83', '84', '85']; + foreach ($patch_list as $patchName) { + if (file_exists("{$patch_dir}/{$patchName}.patch")) { + $patches[] = "{$patch_dir}/{$patchName}.patch"; + continue; + } + for ($i = array_search($major_ver, $serial, true); $i >= 0; --$i) { + $tryMajMin = $serial[$i]; + if (!file_exists("{$patch_dir}/{$patchName}_{$tryMajMin}.patch")) { + continue; + } + $patches[] = "{$patch_dir}/{$patchName}_{$tryMajMin}.patch"; + continue 2; + } + throw new PatchException('phpmicro patches', "Failed finding patch file or versioned file {$patchName} !"); + } + + foreach ($patches as $patch) { + logger()->info("Patching micro with {$patch}"); + self::patchFile($patch, $php_package->getSourceDir()); + } + + return true; + } }