static-php-cli/src/SPC/store/SourcePatcher.php

654 lines
29 KiB
PHP
Raw Normal View History

2023-04-30 12:42:19 +08:00
<?php
declare(strict_types=1);
namespace SPC\store;
use SPC\builder\BuilderBase;
2024-12-20 12:18:34 +08:00
use SPC\builder\linux\SystemUtil;
2024-01-10 21:08:25 +08:00
use SPC\builder\unix\UnixBuilderBase;
use SPC\builder\windows\WindowsBuilder;
use SPC\exception\ExecutionException;
2023-04-30 12:42:19 +08:00
use SPC\exception\FileSystemException;
use SPC\exception\PatchException;
2025-06-29 16:00:17 +08:00
use SPC\util\SPCTarget;
2023-04-30 12:42:19 +08:00
class SourcePatcher
{
public static function init(): void
2023-04-30 12:42:19 +08:00
{
2025-06-28 23:33:13 +07:00
// FileSystem::addSourceExtractHook('swow', [__CLASS__, 'patchSwow']);
FileSystem::addSourceExtractHook('openssl', [__CLASS__, 'patchOpenssl11Darwin']);
FileSystem::addSourceExtractHook('swoole', [__CLASS__, 'patchSwoole']);
FileSystem::addSourceExtractHook('php-src', [__CLASS__, 'patchPhpLibxml212']);
FileSystem::addSourceExtractHook('php-src', [__CLASS__, 'patchGDWin32']);
FileSystem::addSourceExtractHook('php-src', [__CLASS__, 'patchFfiCentos7FixO3strncmp']);
FileSystem::addSourceExtractHook('sqlsrv', [__CLASS__, 'patchSQLSRVWin32']);
FileSystem::addSourceExtractHook('pdo_sqlsrv', [__CLASS__, 'patchSQLSRVWin32']);
FileSystem::addSourceExtractHook('yaml', [__CLASS__, 'patchYamlWin32']);
FileSystem::addSourceExtractHook('libyaml', [__CLASS__, 'patchLibYaml']);
FileSystem::addSourceExtractHook('php-src', [__CLASS__, 'patchImapLicense']);
FileSystem::addSourceExtractHook('ext-imagick', [__CLASS__, 'patchImagickWith84']);
FileSystem::addSourceExtractHook('libaom', [__CLASS__, 'patchLibaomForAlpine']);
2025-08-27 13:04:08 +07:00
FileSystem::addSourceExtractHook('pkg-config', [__CLASS__, 'patchPkgConfigForGcc15']);
2025-06-28 23:33:13 +07:00
FileSystem::addSourceExtractHook('attr', [__CLASS__, 'patchAttrForAlpine']);
FileSystem::addSourceExtractHook('gmssl', [__CLASS__, 'patchGMSSL']);
2023-04-30 12:42:19 +08:00
}
public static function patchBeforeBuildconf(BuilderBase $builder): void
2023-04-30 12:42:19 +08:00
{
2025-05-22 12:27:41 +07:00
foreach ($builder->getExts() as $ext) {
if ($ext->patchBeforeBuildconf() === true) {
2025-06-20 17:11:52 +07:00
logger()->info("Extension [{$ext->getName()}] patched before buildconf");
2023-04-30 12:42:19 +08:00
}
}
2025-03-11 07:44:31 +01:00
foreach ($builder->getLibs() as $lib) {
if ($lib->patchBeforeBuildconf() === true) {
2025-06-20 17:11:52 +07:00
logger()->info("Library [{$lib->getName()}]patched before buildconf");
2025-03-11 07:44:31 +01:00
}
}
2024-01-10 21:08:25 +08:00
// patch windows php 8.1 bug
if (PHP_OS_FAMILY === 'Windows' && $builder->getPHPVersionID() >= 80100 && $builder->getPHPVersionID() < 80200) {
logger()->info('Patching PHP 8.1 windows Fiber bug');
FileSystem::replaceFileStr(
SOURCE_PATH . '\php-src\win32\build\config.w32',
"ADD_FLAG('LDFLAGS', '$(BUILD_DIR)\\\\Zend\\\\jump_' + FIBER_ASM_ARCH + '_ms_pe_masm.obj');",
"ADD_FLAG('ASM_OBJS', '$(BUILD_DIR)\\\\Zend\\\\jump_' + FIBER_ASM_ARCH + '_ms_pe_masm.obj $(BUILD_DIR)\\\\Zend\\\\make_' + FIBER_ASM_ARCH + '_ms_pe_masm.obj');"
);
FileSystem::replaceFileStr(
SOURCE_PATH . '\php-src\win32\build\config.w32',
"ADD_FLAG('LDFLAGS', '$(BUILD_DIR)\\\\Zend\\\\make_' + FIBER_ASM_ARCH + '_ms_pe_masm.obj');",
''
);
}
// Fix PHP VS version
if ($builder instanceof WindowsBuilder) {
// get vs version
$vc = \SPC\builder\windows\SystemUtil::findVisualStudio();
$vc_matches = match ($vc['version']) {
'vs17' => ['VS17', 'Visual C++ 2022'],
'vs16' => ['VS16', 'Visual C++ 2019'],
default => ['unknown', 'unknown'],
};
// patch php-src/win32/build/confutils.js
FileSystem::replaceFileStr(
SOURCE_PATH . '\php-src\win32\build\confutils.js',
'var name = "unknown";',
"var name = short ? \"{$vc_matches[0]}\" : \"{$vc_matches[1]}\";return name;"
);
}
2025-08-03 01:12:28 +08:00
// patch configure.ac
$musl = SPCTarget::getLibc() === 'musl';
FileSystem::backupFile(SOURCE_PATH . '/php-src/configure.ac');
2025-08-03 01:12:28 +08:00
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')
2025-08-03 01:12:28 +08:00
);
if (getenv('SPC_LIBC') === false && ($libc = SPCTarget::getLibc()) !== null) {
putenv("SPC_LIBC={$libc}");
}
2024-07-28 14:02:17 +08:00
// patch php-src/build/php.m4 PKG_CHECK_MODULES -> PKG_CHECK_MODULES_STATIC
FileSystem::replaceFileStr(SOURCE_PATH . '/php-src/build/php.m4', 'PKG_CHECK_MODULES(', 'PKG_CHECK_MODULES_STATIC(');
if ($builder->getOption('enable-micro-win32')) {
2025-06-28 23:45:02 +07:00
self::patchMicroWin32();
} else {
2025-06-28 23:45:02 +07:00
self::unpatchMicroWin32();
}
2023-04-30 12:42:19 +08:00
}
/**
* Source patcher runner before configure
*
* @param BuilderBase $builder Builder
*/
public static function patchBeforeConfigure(BuilderBase $builder): void
2023-04-30 12:42:19 +08:00
{
2025-05-21 13:19:51 +07:00
foreach ($builder->getExts() as $ext) {
$patch = $builder instanceof WindowsBuilder ? $ext->patchBeforeWindowsConfigure() : $ext->patchBeforeConfigure();
if ($patch === true) {
2025-06-20 17:11:52 +07:00
logger()->info("Extension [{$ext->getName()}] patched before configure");
}
}
2025-03-10 10:25:35 +01:00
foreach ($builder->getLibs() as $lib) {
if ($lib->patchBeforeConfigure() === true) {
2025-06-20 17:11:52 +07:00
logger()->info("Library [{$lib->getName()}] patched before configure");
2025-03-10 10:25:35 +01:00
}
}
// patch capstone
if (is_unix()) {
FileSystem::replaceFileRegex(SOURCE_PATH . '/php-src/configure', '/have_capstone="yes"/', 'have_capstone="no"');
}
if (file_exists(SOURCE_PATH . '/php-src/configure.ac.bak')) {
// restore configure.ac
FileSystem::restoreBackupFile(SOURCE_PATH . '/php-src/configure.ac');
}
2023-04-30 12:42:19 +08:00
}
public static function patchMicro(?array $items = null): bool
2023-04-30 12:42:19 +08:00
{
if (!file_exists(SOURCE_PATH . '/php-src/sapi/micro/php_micro.c')) {
return false;
}
$ver_file = SOURCE_PATH . '/php-src/main/php_version.h';
if (!file_exists($ver_file)) {
throw new FileSystemException('Patch failed, cannot find php source files');
}
$version_h = FileSystem::readFile(SOURCE_PATH . '/php-src/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' : '';
2023-04-30 12:42:19 +08:00
// 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');
2025-08-27 14:36:05 +07:00
$spc_micro_patches = $spc_micro_patches === false ? [] : explode(',', $spc_micro_patches);
}
2025-08-27 14:36:05 +07:00
$spc_micro_patches = array_filter($spc_micro_patches, fn ($item) => trim((string) $item) !== '');
2024-08-20 11:04:42 +08:00
$patch_list = $spc_micro_patches;
2023-04-30 12:42:19 +08:00
$patches = [];
2025-07-15 21:14:02 +08:00
$serial = ['80', '81', '82', '83', '84', '85'];
2023-04-30 12:42:19 +08:00
foreach ($patch_list as $patchName) {
if (file_exists(SOURCE_PATH . "/php-src/sapi/micro/patches/{$patchName}.patch")) {
$patches[] = "sapi/micro/patches/{$patchName}.patch";
continue;
}
for ($i = array_search($major_ver, $serial, true); $i >= 0; --$i) {
$tryMajMin = $serial[$i];
if (!file_exists(SOURCE_PATH . "/php-src/sapi/micro/patches/{$patchName}_{$tryMajMin}.patch")) {
continue;
}
$patches[] = "sapi/micro/patches/{$patchName}_{$tryMajMin}.patch";
continue 2;
}
throw new PatchException('phpmicro patches', "Failed finding patch file or versioned file {$patchName} !");
2023-04-30 12:42:19 +08:00
}
2024-08-20 11:04:42 +08:00
foreach ($patches as $patch) {
logger()->info("Patching micro with {$patch}");
2025-06-30 19:41:32 +08:00
self::patchFile(SOURCE_PATH . "/php-src/{$patch}", SOURCE_PATH . '/php-src');
2024-08-20 11:04:42 +08:00
}
2023-05-01 12:50:01 +08:00
2023-04-30 12:42:19 +08:00
return true;
}
2023-10-14 11:18:02 +08:00
/**
* Use existing patch file for patching
*
* @param string $patch_name Patch file name in src/globals/patch/ or absolute path
* @param string $cwd Working directory for patch command
* @param bool $reverse Reverse patches (default: False)
2023-10-14 11:18:02 +08:00
*/
public static function patchFile(string $patch_name, string $cwd, bool $reverse = false): bool
{
try {
if (FileSystem::isRelativePath($patch_name)) {
$patch_file = ROOT_DIR . "/src/globals/patch/{$patch_name}";
} else {
$patch_file = $patch_name;
}
if (!file_exists($patch_file)) {
return false;
}
2023-10-14 11:18:02 +08:00
$patch_str = FileSystem::convertPath($patch_file);
if (!file_exists($patch_str)) {
throw new PatchException($patch_name, "Patch file [{$patch_str}] does not exist");
}
2023-10-14 11:18:02 +08:00
// Copy patch from phar
if (str_starts_with($patch_str, 'phar://')) {
$filename = pathinfo($patch_file, PATHINFO_BASENAME);
file_put_contents(SOURCE_PATH . "/{$filename}", file_get_contents($patch_file));
$patch_str = FileSystem::convertPath(SOURCE_PATH . "/{$filename}");
}
// detect
$detect_reverse = !$reverse;
$detect_cmd = 'cd ' . escapeshellarg($cwd) . ' && '
. (PHP_OS_FAMILY === 'Windows' ? 'type' : 'cat') . ' ' . escapeshellarg($patch_str)
. ' | patch --dry-run -p1 -s -f ' . ($detect_reverse ? '-R' : '')
. ' > ' . (PHP_OS_FAMILY === 'Windows' ? 'NUL' : '/dev/null') . ' 2>&1';
exec($detect_cmd, $output, $detect_status);
if ($detect_status === 0) {
return true;
}
// apply patch
$apply_cmd = 'cd ' . escapeshellarg($cwd) . ' && '
. (PHP_OS_FAMILY === 'Windows' ? 'type' : 'cat') . ' ' . escapeshellarg($patch_str)
. ' | patch -p1 ' . ($reverse ? '-R' : '');
f_passthru($apply_cmd);
return true;
} catch (ExecutionException $e) {
// If patch failed, throw exception
throw new PatchException($patch_name, "Patch file [{$patch_name}] failed to apply", previous: $e);
}
2023-10-14 11:18:02 +08:00
}
2023-04-30 12:42:19 +08:00
public static function patchOpenssl11Darwin(): bool
{
if (PHP_OS_FAMILY === 'Darwin' && !file_exists(SOURCE_PATH . '/openssl/VERSION.dat') && file_exists(SOURCE_PATH . '/openssl/test/v3ext.c')) {
FileSystem::replaceFileStr(SOURCE_PATH . '/openssl/test/v3ext.c', '#include <stdio.h>', '#include <stdio.h>' . PHP_EOL . '#include <string.h>');
2023-04-30 12:42:19 +08:00
return true;
}
return false;
}
Feature perfect swoole extension config (#297) * improve swoole static build config * improve swoole static build config * improve swoole static build config * improve swoole static build config * improve swoole static build config * add cares config * update swoole depend config * update swoole depend config * update cares build config * update workflow tests.yaml config * fix setup-runtime * test with clang build * test with clang build * update cares build config * test * test * test * test * test * test * test * test * test * test * test * test * test * test * update cares license * test build * test build * test build * test build * test add enable libpq * test add enable libpq * test add enable libpq * test add enable libpq * test add enable libpq * test add enable libpq * test add enable libpq * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * update * update * update * update * update * update * update * update * update * compatible old * fix code format * fix code format * add swoole test case * add swoole test case * add phpstan ignore error * add phpstan ignore error * add phpstan ignore error * add phpstan ignore error * add phpstan ignore error * update phpstan.neon * update swoole extension test case * update swoole test case * adjust config order and depends * revert LinuxBuilder * remove swoole.phpt * re-adjust swoole args * update test-extensions and some PHPDoc * revert: debian and alpine clang doctor install * revert: MacOSBuilder * fix: extract hook for archive not working * revert: build tests * use addon mode to swoole database hook * add hook tests * test minimal * test minimal * sort config --------- Co-authored-by: crazywhalecc <jesse2061@outlook.com>
2024-01-03 10:31:21 +08:00
public static function patchSwoole(): bool
{
// swoole hook needs pdo/pdo.h
FileSystem::replaceFileStr(
SOURCE_PATH . '/php-src/ext/swoole/config.m4',
'PHP_ADD_INCLUDE([$ext_srcdir])',
"PHP_ADD_INCLUDE( [\$ext_srcdir] )\n PHP_ADD_INCLUDE([\$abs_srcdir/ext])"
);
2025-06-28 23:33:13 +07:00
// swoole 5.1.3 build fix
// get swoole version first
$file = SOURCE_PATH . '/php-src/ext/swoole/include/swoole_version.h';
// Match #define SWOOLE_VERSION "5.1.3"
$pattern = '/#define SWOOLE_VERSION "(.+)"/';
if (preg_match($pattern, file_get_contents($file), $matches)) {
$version = $matches[1];
} else {
$version = '1.0.0';
}
if ($version === '5.1.3') {
self::patchFile('spc_fix_swoole_50513.patch', SOURCE_PATH . '/php-src/ext/swoole');
}
2025-08-27 08:31:48 +07:00
if (version_compare($version, '6.0.0', '>=') && version_compare($version, '6.1.0', '<')) {
// remove when https://github.com/swoole/swoole-src/pull/5848 is merged
self::patchFile('swoole_fix_date_time.patch', SOURCE_PATH . '/php-src/ext/swoole');
// remove when https://github.com/swoole/swoole-src/pull/5847 is merged
self::patchFile('swoole_fix_odbclibs.patch', SOURCE_PATH . '/php-src/ext/swoole');
}
Feature perfect swoole extension config (#297) * improve swoole static build config * improve swoole static build config * improve swoole static build config * improve swoole static build config * improve swoole static build config * add cares config * update swoole depend config * update swoole depend config * update cares build config * update workflow tests.yaml config * fix setup-runtime * test with clang build * test with clang build * update cares build config * test * test * test * test * test * test * test * test * test * test * test * test * test * test * update cares license * test build * test build * test build * test build * test add enable libpq * test add enable libpq * test add enable libpq * test add enable libpq * test add enable libpq * test add enable libpq * test add enable libpq * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * test * update * update * update * update * update * update * update * update * update * compatible old * fix code format * fix code format * add swoole test case * add swoole test case * add phpstan ignore error * add phpstan ignore error * add phpstan ignore error * add phpstan ignore error * add phpstan ignore error * update phpstan.neon * update swoole extension test case * update swoole test case * adjust config order and depends * revert LinuxBuilder * remove swoole.phpt * re-adjust swoole args * update test-extensions and some PHPDoc * revert: debian and alpine clang doctor install * revert: MacOSBuilder * fix: extract hook for archive not working * revert: build tests * use addon mode to swoole database hook * add hook tests * test minimal * test minimal * sort config --------- Co-authored-by: crazywhalecc <jesse2061@outlook.com>
2024-01-03 10:31:21 +08:00
return true;
}
public static function patchBeforeMake(BuilderBase $builder): void
2023-04-30 12:42:19 +08:00
{
2024-01-10 21:08:25 +08:00
if ($builder instanceof UnixBuilderBase) {
FileSystem::replaceFileStr(SOURCE_PATH . '/php-src/Makefile', 'install-micro', '');
}
if (!SPCTarget::isStatic() && SPCTarget::getLibc() === 'musl') {
// we need to patch the symbol to global visibility, otherwise extensions with `initial-exec` TLS model will fail to load
FileSystem::replaceFileStr(
SOURCE_PATH . '/php-src/TSRM/TSRM.h',
'#define TSRMLS_MAIN_CACHE_DEFINE() TSRM_TLS void *TSRMLS_CACHE TSRM_TLS_MODEL_ATTR = NULL;',
'#define TSRMLS_MAIN_CACHE_DEFINE() TSRM_TLS __attribute__((visibility("default"))) void *TSRMLS_CACHE TSRM_TLS_MODEL_ATTR = NULL;',
);
} else {
FileSystem::replaceFileStr(
SOURCE_PATH . '/php-src/TSRM/TSRM.h',
'#define TSRMLS_MAIN_CACHE_DEFINE() TSRM_TLS __attribute__((visibility("default"))) void *TSRMLS_CACHE TSRM_TLS_MODEL_ATTR = NULL;',
'#define TSRMLS_MAIN_CACHE_DEFINE() TSRM_TLS void *TSRMLS_CACHE TSRM_TLS_MODEL_ATTR = NULL;',
);
}
// no asan
// if (strpos(file_get_contents(SOURCE_PATH . '/php-src/Makefile'), 'CFLAGS_CLEAN = -g') === false) {
// FileSystem::replaceFileStr(SOURCE_PATH . '/php-src/Makefile', 'CFLAGS_CLEAN = ', 'CFLAGS_CLEAN = -g -fsanitize=address ');
// }
// call extension patch before make
foreach ($builder->getExts() as $ext) {
if ($ext->patchBeforeMake() === true) {
2025-06-20 17:11:52 +07:00
logger()->info("Extension [{$ext->getName()}] patched before make");
}
2023-04-30 22:40:48 +08:00
}
foreach ($builder->getLibs() as $lib) {
if ($lib->patchBeforeMake() === true) {
2025-06-20 17:11:52 +07:00
logger()->info("Library [{$lib->getName()}] patched before make");
}
}
if (str_contains((string) getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS'), '-release')) {
FileSystem::replaceFileLineContainsString(
SOURCE_PATH . '/php-src/ext/standard/info.c',
'#ifdef CONFIGURE_COMMAND',
'#ifdef NO_CONFIGURE_COMMAND',
);
} else {
FileSystem::replaceFileLineContainsString(
SOURCE_PATH . '/php-src/ext/standard/info.c',
'#ifdef NO_CONFIGURE_COMMAND',
'#ifdef CONFIGURE_COMMAND',
);
}
2023-04-30 12:42:19 +08:00
}
public static function patchHardcodedINI(array $ini = []): bool
{
$cli_c = SOURCE_PATH . '/php-src/sapi/cli/php_cli.c';
$cli_c_bak = SOURCE_PATH . '/php-src/sapi/cli/php_cli.c.bak';
$micro_c = SOURCE_PATH . '/php-src/sapi/micro/php_micro.c';
$micro_c_bak = SOURCE_PATH . '/php-src/sapi/micro/php_micro.c.bak';
$embed_c = SOURCE_PATH . '/php-src/sapi/embed/php_embed.c';
$embed_c_bak = SOURCE_PATH . '/php-src/sapi/embed/php_embed.c.bak';
// Try to reverse backup file
$find_str = 'const char HARDCODED_INI[] =';
$patch_str = '';
foreach ($ini as $key => $value) {
$patch_str .= "\"{$key}={$value}\\n\"\n";
}
$patch_str = "const char HARDCODED_INI[] =\n{$patch_str}";
// Detect backup, if we have backup, it means we need to reverse first
if (file_exists($cli_c_bak) || file_exists($micro_c_bak) || file_exists($embed_c_bak)) {
self::unpatchHardcodedINI();
}
// Backup it
$result = file_put_contents($cli_c_bak, file_get_contents($cli_c));
$result = $result && file_put_contents($micro_c_bak, file_get_contents($micro_c));
$result = $result && file_put_contents($embed_c_bak, file_get_contents($embed_c));
if ($result === false) {
return false;
}
// Patch it
FileSystem::replaceFileStr($cli_c, $find_str, $patch_str);
FileSystem::replaceFileStr($micro_c, $find_str, $patch_str);
FileSystem::replaceFileStr($embed_c, $find_str, $patch_str);
return true;
}
public static function unpatchHardcodedINI(): bool
{
$cli_c = SOURCE_PATH . '/php-src/sapi/cli/php_cli.c';
$cli_c_bak = SOURCE_PATH . '/php-src/sapi/cli/php_cli.c.bak';
$micro_c = SOURCE_PATH . '/php-src/sapi/micro/php_micro.c';
$micro_c_bak = SOURCE_PATH . '/php-src/sapi/micro/php_micro.c.bak';
$embed_c = SOURCE_PATH . '/php-src/sapi/embed/php_embed.c';
$embed_c_bak = SOURCE_PATH . '/php-src/sapi/embed/php_embed.c.bak';
if (!file_exists($cli_c_bak) && !file_exists($micro_c_bak) && !file_exists($embed_c_bak)) {
return false;
}
$result = file_put_contents($cli_c, file_get_contents($cli_c_bak));
$result = $result && file_put_contents($micro_c, file_get_contents($micro_c_bak));
$result = $result && file_put_contents($embed_c, file_get_contents($embed_c_bak));
@unlink($cli_c_bak);
@unlink($micro_c_bak);
@unlink($embed_c_bak);
return $result;
}
2024-01-10 21:08:25 +08:00
public static function patchMicroPhar(int $version_id): void
{
FileSystem::backupFile(SOURCE_PATH . '/php-src/ext/phar/phar.c');
FileSystem::replaceFileStr(
SOURCE_PATH . '/php-src/ext/phar/phar.c',
'static zend_op_array *phar_compile_file',
"char *micro_get_filename(void);\n\nstatic zend_op_array *phar_compile_file"
);
if ($version_id < 80100) {
// PHP 8.0.x
FileSystem::replaceFileStr(
SOURCE_PATH . '/php-src/ext/phar/phar.c',
'if (strstr(file_handle->filename, ".phar") && !strstr(file_handle->filename, "://")) {',
'if ((strstr(file_handle->filename, micro_get_filename()) || strstr(file_handle->filename, ".phar")) && !strstr(file_handle->filename, "://")) {'
);
} else {
// PHP >= 8.1
FileSystem::replaceFileStr(
SOURCE_PATH . '/php-src/ext/phar/phar.c',
'if (strstr(ZSTR_VAL(file_handle->filename), ".phar") && !strstr(ZSTR_VAL(file_handle->filename), "://")) {',
'if ((strstr(ZSTR_VAL(file_handle->filename), micro_get_filename()) || strstr(ZSTR_VAL(file_handle->filename), ".phar")) && !strstr(ZSTR_VAL(file_handle->filename), "://")) {'
);
}
}
public static function unpatchMicroPhar(): void
{
FileSystem::restoreBackupFile(SOURCE_PATH . '/php-src/ext/phar/phar.c');
}
/**
* Fix the compilation issue of sqlsrv and pdo_sqlsrv on Windows (/sdl check is too strict and will cause Zend compilation to fail)
*/
public static function patchSQLSRVWin32(string $source_name): bool
{
$source_name = preg_replace('/[^a-z_]/', '', $source_name);
if (file_exists(SOURCE_PATH . '/php-src/ext/' . $source_name . '/config.w32')) {
FileSystem::replaceFileStr(SOURCE_PATH . '/php-src/ext/' . $source_name . '/config.w32', '/sdl', '');
return true;
}
return false;
}
public static function patchYamlWin32(): bool
{
FileSystem::replaceFileStr(SOURCE_PATH . '/php-src/ext/yaml/config.w32', "lib.substr(lib.length - 6, 6) == '_a.lib'", "lib.substr(lib.length - 6, 6) == '_a.lib' || 'yes' == 'yes'");
return true;
}
public static function patchLibYaml(string $name, string $target): bool
{
if (!file_exists("{$target}/cmake/config.h.in")) {
FileSystem::createDir("{$target}/cmake");
copy(ROOT_DIR . '/src/globals/extra/libyaml_config.h.in', "{$target}/cmake/config.h.in");
}
if (!file_exists("{$target}/YamlConfig.cmake.in")) {
copy(ROOT_DIR . '/src/globals/extra/libyaml_yamlConfig.cmake.in', "{$target}/yamlConfig.cmake.in");
}
return true;
}
/**
* Patch imap license file for PHP < 8.4
*/
public static function patchImapLicense(): bool
{
if (!file_exists(SOURCE_PATH . '/php-src/ext/imap/LICENSE') && is_dir(SOURCE_PATH . '/php-src/ext/imap')) {
file_put_contents(SOURCE_PATH . '/php-src/ext/imap/LICENSE', file_get_contents(ROOT_DIR . '/src/globals/extra/Apache_LICENSE'));
return true;
}
return false;
}
2024-11-03 13:56:36 +08:00
/**
* Patch imagick for PHP 8.4
*/
public static function patchImagickWith84(): bool
{
// match imagick version id
$file = SOURCE_PATH . '/php-src/ext/imagick/php_imagick.h';
if (!file_exists($file)) {
return false;
}
$content = file_get_contents($file);
if (preg_match('/#define PHP_IMAGICK_EXTNUM\s+(\d+)/', $content, $match) === 0) {
return false;
}
$extnum = intval($match[1]);
if ($extnum < 30800) {
2025-06-28 23:45:02 +07:00
self::patchFile('imagick_php84_before_30800.patch', SOURCE_PATH . '/php-src/ext/imagick');
return true;
}
return false;
2025-06-04 08:58:46 +07:00
}
public static function patchFfiCentos7FixO3strncmp(): bool
{
2025-06-29 16:00:17 +08:00
if (!($ver = SPCTarget::getLibcVersion()) || version_compare($ver, '2.17', '>')) {
return false;
}
2025-06-06 23:49:58 +07:00
if (!file_exists(SOURCE_PATH . '/php-src/main/php_version.h')) {
return false;
}
2025-06-06 23:49:58 +07:00
$file = file_get_contents(SOURCE_PATH . '/php-src/main/php_version.h');
if (preg_match('/PHP_VERSION_ID (\d+)/', $file, $match) !== 0 && intval($match[1]) < 80316) {
return false;
}
2025-06-28 23:45:02 +07:00
self::patchFile('ffi_centos7_fix_O3_strncmp.patch', SOURCE_PATH . '/php-src');
return true;
2024-11-03 13:56:36 +08:00
}
2025-08-27 13:04:08 +07:00
public static function patchPkgConfigForGcc15(): bool
{
self::patchFile('pkg-config_gcc15.patch', SOURCE_PATH . '/pkg-config');
return true;
}
2024-12-20 12:18:34 +08:00
public static function patchLibaomForAlpine(): bool
{
if (PHP_OS_FAMILY === 'Linux' && SystemUtil::isMuslDist()) {
2025-06-28 23:45:02 +07:00
self::patchFile('libaom_posix_implict.patch', SOURCE_PATH . '/libaom');
2024-12-20 12:18:34 +08:00
return true;
}
return false;
}
2025-03-10 09:46:20 +01:00
public static function patchAttrForAlpine(): bool
{
if (PHP_OS_FAMILY === 'Linux' && SystemUtil::isMuslDist() || PHP_OS_FAMILY === 'Darwin') {
2025-06-28 23:45:02 +07:00
self::patchFile('attr_alpine_gethostname.patch', SOURCE_PATH . '/attr');
2025-03-10 09:46:20 +01:00
return true;
}
return false;
}
2024-01-10 21:08:25 +08:00
/**
* Patch cli SAPI Makefile for Windows.
*/
public static function patchWindowsCLITarget(): void
{
// search Makefile code line contains "$(BUILD_DIR)\php.exe:"
$content = FileSystem::readFile(SOURCE_PATH . '/php-src/Makefile');
$lines = explode("\r\n", $content);
$line_num = 0;
$found = false;
foreach ($lines as $v) {
if (str_contains($v, '$(BUILD_DIR)\php.exe:')) {
2024-01-10 21:08:25 +08:00
$found = $line_num;
break;
}
++$line_num;
}
if ($found === false) {
throw new PatchException('Windows Makefile patching for php.exe target', 'Cannot patch windows CLI Makefile, Makefile does not contain "$(BUILD_DIR)\php.exe:" line');
2024-01-10 21:08:25 +08:00
}
$lines[$line_num] = '$(BUILD_DIR)\php.exe: generated_files $(DEPS_CLI) $(PHP_GLOBAL_OBJS) $(CLI_GLOBAL_OBJS) $(STATIC_EXT_OBJS) $(ASM_OBJS) $(BUILD_DIR)\php.exe.res $(BUILD_DIR)\php.exe.manifest';
$lines[$line_num + 1] = "\t" . '"$(LINK)" /nologo $(PHP_GLOBAL_OBJS_RESP) $(CLI_GLOBAL_OBJS_RESP) $(STATIC_EXT_OBJS_RESP) $(STATIC_EXT_LIBS) $(ASM_OBJS) $(LIBS) $(LIBS_CLI) $(BUILD_DIR)\php.exe.res /out:$(BUILD_DIR)\php.exe $(LDFLAGS) $(LDFLAGS_CLI) /ltcg /nodefaultlib:msvcrt /nodefaultlib:msvcrtd /ignore:4286';
2025-10-16 00:55:21 +08:00
FileSystem::writeFile(SOURCE_PATH . '/php-src/Makefile', implode("\r\n", $lines));
}
/**
* Patch cgi SAPI Makefile for Windows.
*/
public static function patchWindowsCGITarget(): void
{
// search Makefile code line contains "$(BUILD_DIR)\php.exe:"
$content = FileSystem::readFile(SOURCE_PATH . '/php-src/Makefile');
$lines = explode("\r\n", $content);
$line_num = 0;
$found = false;
foreach ($lines as $v) {
if (str_contains($v, '$(BUILD_DIR)\php-cgi.exe:')) {
$found = $line_num;
break;
}
++$line_num;
}
if ($found === false) {
throw new PatchException('Windows Makefile patching for php-cgi.exe target', 'Cannot patch windows CGI Makefile, Makefile does not contain "$(BUILD_DIR)\php-cgi.exe:" line');
}
$lines[$line_num] = '$(BUILD_DIR)\php-cgi.exe: generated_files $(DEPS_CGI) $(CGI_GLOBAL_OBJS) $(PHP_GLOBAL_OBJS) $(STATIC_EXT_OBJS) $(ASM_OBJS) $(BUILD_DIR)\$(PHPLIB) $(BUILD_DIR)\php-cgi.exe.res $(BUILD_DIR)\php-cgi.exe.manifest';
$lines[$line_num + 1] = "\t" . '@"$(LINK)" /nologo $(PHP_GLOBAL_OBJS_RESP) $(CGI_GLOBAL_OBJS_RESP) $(STATIC_EXT_OBJS_RESP) $(STATIC_EXT_LIBS) $(ASM_OBJS) $(LIBS) $(LIBS_CGI) $(BUILD_DIR)\php-cgi.exe.res /out:$(BUILD_DIR)\php-cgi.exe $(LDFLAGS) $(LDFLAGS_CGI) /ltcg /nodefaultlib:msvcrt /nodefaultlib:msvcrtd /ignore:4286';
2024-01-10 21:08:25 +08:00
FileSystem::writeFile(SOURCE_PATH . '/php-src/Makefile', implode("\r\n", $lines));
}
public static function patchPhpLibxml212(): bool
{
$file = file_get_contents(SOURCE_PATH . '/php-src/main/php_version.h');
if (preg_match('/PHP_VERSION_ID (\d+)/', $file, $match) !== 0) {
$ver_id = intval($match[1]);
if ($ver_id < 80000) {
self::patchFile('spc_fix_alpine_build_php80.patch', SOURCE_PATH . '/php-src');
return true;
}
if ($ver_id < 80100) {
self::patchFile('spc_fix_libxml2_12_php80.patch', SOURCE_PATH . '/php-src');
self::patchFile('spc_fix_alpine_build_php80.patch', SOURCE_PATH . '/php-src');
return true;
}
if ($ver_id < 80200) {
2024-11-23 11:51:21 +08:00
// self::patchFile('spc_fix_libxml2_12_php81.patch', SOURCE_PATH . '/php-src');
self::patchFile('spc_fix_alpine_build_php80.patch', SOURCE_PATH . '/php-src');
return true;
}
return false;
}
return false;
}
public static function patchGDWin32(): bool
{
$file = file_get_contents(SOURCE_PATH . '/php-src/main/php_version.h');
if (preg_match('/PHP_VERSION_ID (\d+)/', $file, $match) !== 0) {
$ver_id = intval($match[1]);
if ($ver_id < 80200) {
// see: https://github.com/php/php-src/commit/243966177e39eb71822935042c3f13fa6c5b9eed
FileSystem::replaceFileStr(SOURCE_PATH . '/php-src/ext/gd/libgd/gdft.c', '#ifndef MSWIN32', '#ifndef _WIN32');
}
// custom config.w32, because official config.w32 is hard-coded many things
$origin = $ver_id >= 80100 ? file_get_contents(ROOT_DIR . '/src/globals/extra/gd_config_81.w32') : file_get_contents(ROOT_DIR . '/src/globals/extra/gd_config_80.w32');
file_put_contents(SOURCE_PATH . '/php-src/ext/gd/config.w32.bak', file_get_contents(SOURCE_PATH . '/php-src/ext/gd/config.w32'));
return file_put_contents(SOURCE_PATH . '/php-src/ext/gd/config.w32', $origin) !== false;
}
return false;
}
/**
* Add additional `static-php-cli.version` ini value for PHP source.
*/
public static function patchSPCVersionToPHP(string $version = 'unknown'): void
{
2025-08-01 02:25:12 +08:00
// detect patch (remove this when 8.3 deprecated)
$file = FileSystem::readFile(SOURCE_PATH . '/php-src/main/main.c');
if (!str_contains($file, 'static-php-cli.version')) {
logger()->debug('Inserting static-php-cli.version to php-src');
$file = str_replace('PHP_INI_BEGIN()', "PHP_INI_BEGIN()\n\tPHP_INI_ENTRY(\"static-php-cli.version\",\t\"{$version}\",\tPHP_INI_ALL,\tNULL)", $file);
FileSystem::writeFile(SOURCE_PATH . '/php-src/main/main.c', $file);
}
}
public static function patchMicroWin32(): void
{
// patch micro win32
if (!file_exists(SOURCE_PATH . '\php-src\sapi\micro\php_micro.c.win32bak')) {
copy(SOURCE_PATH . '\php-src\sapi\micro\php_micro.c', SOURCE_PATH . '\php-src\sapi\micro\php_micro.c.win32bak');
FileSystem::replaceFileStr(SOURCE_PATH . '\php-src\sapi\micro\php_micro.c', '#include "php_variables.h"', '#include "php_variables.h"' . "\n#define PHP_MICRO_WIN32_NO_CONSOLE 1");
}
}
public static function unpatchMicroWin32(): void
{
if (file_exists(SOURCE_PATH . '\php-src\sapi\micro\php_micro.c.win32bak')) {
rename(SOURCE_PATH . '\php-src\sapi\micro\php_micro.c.win32bak', SOURCE_PATH . '\php-src\sapi\micro\php_micro.c');
}
}
public static function patchGMSSL(): void
{
FileSystem::replaceFileStr(SOURCE_PATH . '/gmssl/src/hex.c', 'unsigned char *OPENSSL_hexstr2buf(const char *str, size_t *len)', 'unsigned char *GMSSL_hexstr2buf(const char *str, size_t *len)');
FileSystem::replaceFileStr(SOURCE_PATH . '/gmssl/src/hex.c', 'OPENSSL_hexchar2int', 'GMSSL_hexchar2int');
}
2023-04-30 12:42:19 +08:00
}