Skip to content

[3.0] Refactor extensions#1038

Draft
crazywhalecc wants to merge 28 commits intov3-devfrom
v3-refactor/extensions
Draft

[3.0] Refactor extensions#1038
crazywhalecc wants to merge 28 commits intov3-devfrom
v3-refactor/extensions

Conversation

@crazywhalecc
Copy link
Owner

@crazywhalecc crazywhalecc commented Feb 19, 2026

What does this PR do?

Refactor extensions from v2.

Other tasks

Extensions

  • amqp
  • apcu
  • ast
  • bcmath
  • brotli
  • bz2
  • calendar
  • com_dotnet
  • ctype
  • curl
  • dba
  • dio
  • dom
  • ds
  • enchant
  • ev
  • event
  • excimer
  • exif
  • ffi
  • fileinfo
  • filter
  • ftp
  • gd
  • gettext
  • glfw
  • gmp
  • gmssl
  • grpc
  • iconv
  • igbinary
  • imagick
  • imap
  • inotify
  • intl
  • ldap
  • libxml
  • lz4
  • maxminddb
  • mbregex
  • mbstring
  • mcrypt
  • memcache
  • memcached
  • mongodb
  • msgpack
  • mysqli
  • mysqlnd
  • mysqlnd_ed25519
  • mysqlnd_parsec
  • oci8
  • odbc
  • opcache
  • openssl
  • opentelemetry
  • parallel
  • password-argon2
  • pcntl
  • pcov
  • pdo
  • pdo_mysql
  • pdo_odbc
  • pdo_pgsql
  • pdo_sqlite
  • pdo_sqlsrv
  • pgsql
  • phar
  • posix
  • protobuf
  • rar
  • rdkafka
  • readline
  • redis
  • session
  • shmop
  • simdjson
  • simplexml
  • snappy
  • snmp
  • soap
  • sockets
  • sodium
  • spx
  • sqlite3
  • sqlsrv
  • ssh2
  • swoole
  • swoole-hook-mysql
  • swoole-hook-odbc
  • swoole-hook-pgsql
  • swoole-hook-sqlite
  • swow
  • sysvmsg
  • sysvsem
  • sysvshm
  • tidy
  • tokenizer
  • trader
  • uuid
  • uv
  • xdebug
  • xhprof
  • xlswriter
  • xml
  • xmlreader
  • xmlwriter
  • xsl
  • xz
  • yac
  • yaml
  • zip
  • zlib
  • zstd

@crazywhalecc crazywhalecc changed the base branch from main to v3-dev February 19, 2026 15:08
@crazywhalecc crazywhalecc requested a review from Copilot February 26, 2026 08:18
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request represents a significant refactoring effort to modernize the extension handling system from v2 to v3.0. The PR introduces a comprehensive smoke testing framework, refactors FrankenPHP build logic, updates branding from "static-php-cli" to "StaticPHP", and makes numerous improvements to the build system architecture. Key changes include moving from a monolithic builder approach to a more modular package-based architecture with proper dependency handling and validation.

Changes:

  • Added comprehensive smoke test framework for CLI, CGI, micro, embed, and FrankenPHP SAPIs with configurable test skipping via --no-smoke-test option
  • Refactored FrankenPHP build process with new smoke tests and improved handling of the --with-frankenphp-app option
  • Rebranded product from "static-php-cli" to "StaticPHP" across documentation and code
  • Added new extension support (apcu) and improved libavif build configuration with optional dependencies
  • Enhanced build system with better error handling, build tracking file rename, and improved Makefile path handling

Reviewed changes

Copilot reviewed 24 out of 24 changed files in this pull request and generated 11 comments.

Show a summary per file
File Description
src/globals/test-extensions.php Modified test configuration to focus on pgsql extension testing (significantly reduced coverage)
src/StaticPHP/Util/BuildRootTracker.php Renamed tracker file from .spc-tracker.json to .build.json
src/StaticPHP/Package/PhpExtensionPackage.php Added comprehensive smoke test methods and improved extension name handling
src/StaticPHP/Command/Dev/LintConfigCommand.php Added MOTD suppression and "No changes" output message
src/SPC/store/pkg/GoXcaddy.php Fixed path handling to use hardcoded 'go-xcaddy' directory
src/SPC/builder/unix/library/postgresql.php Removed aarch64 glibc 2.17 patches (moved to new architecture)
src/SPC/builder/unix/library/libavif.php Added optional library dependencies for improved build flexibility
src/SPC/builder/unix/UnixBuilderBase.php Added quote trimming for FrankenPHP app path and ServerHeader build flag
src/SPC/ConsoleApplication.php Version bump to 2.8.3
src/Package/Target/php/unix.php Major refactoring with new smoke test framework, removed old patches, added sed fix for Makefile paths
src/Package/Target/php/frankenphp.php Renamed methods, added smoke tests, refactored app processing
src/Package/Target/php.php Added frankenphp trait usage and --no-smoke-test option
src/Package/Library/postgresql.php Made explicit_bzero patch macOS-specific
src/Package/Library/libacl.php Added patch description annotation
config/source.json Disabled pre-built flag for libavif
config/lib.json Added libavif dependencies and suggestions
config/pkg/ext/ext-apcu.yml New extension configuration file
README.md Updated branding to StaticPHP
README-zh.md Updated branding to StaticPHP (Chinese)
.github/workflows/build-unix.yml Added with-suggested-libs option and debug log upload

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.


/**
* Run smoke test for the extension on Unix CLI.
* Override this method in a subclass。
Copy link

Copilot AI Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment contains a Chinese period character (。) instead of an English period. This should be changed to maintain consistency with the English codebase.

Suggested change
* Override this method in a subclass
* Override this method in a subclass.

Copilot uses AI. Check for mistakes.
Comment on lines 52 to 58
#[Stage]
public function buildconfForUnix(TargetPackage $package): void
{
InteractiveTerm::setMessage('Building php: ' . ConsoleColor::yellow('./buildconf'));
V2CompatLayer::emitPatchPoint('before-php-buildconf');
// run ./buildconf
shell()->cd($package->getSourceDir())->exec(getenv('SPC_CMD_PREFIX_PHP_BUILDCONF'));
}
Copy link

Copilot AI Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The removal of the TSRM patch for musl TLS symbol visibility and the configure command info patch may cause issues on certain platforms. The patchTSRMBeforeUnixMake method that was removed handled important edge cases for musl builds with non-static toolchains. Verify that these patches are no longer needed or have been moved elsewhere in the codebase.

Copilot uses AI. Check for mistakes.
Comment on lines 13 to 17
public function patchBeforeBuild(): bool
{
// fix aarch64 build on glibc 2.17 (e.g. CentOS 7)
if (SPCTarget::getLibcVersion() === '2.17' && GNU_ARCH === 'aarch64') {
try {
FileSystem::replaceFileStr("{$this->source_dir}/src/port/pg_popcount_aarch64.c", 'HWCAP_SVE', '0');
FileSystem::replaceFileStr(
"{$this->source_dir}/src/port/pg_crc32c_armv8_choose.c",
'#if defined(__linux__) && !defined(__aarch64__) && !defined(HWCAP2_CRC32)',
'#if defined(__linux__) && !defined(HWCAP_CRC32)'
);
} catch (FileSystemException) {
// allow file not-existence to make it compatible with old and new version
}
}
// skip the test on platforms where libpq infrastructure may be provided by statically-linked libraries
FileSystem::replaceFileStr("{$this->source_dir}/src/interfaces/libpq/Makefile", 'invokes exit\'; exit 1;', 'invokes exit\';');
// disable shared libs build
Copy link

Copilot AI Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The removal of PostgreSQL patches for aarch64 builds on glibc 2.17 systems may break builds on older CentOS 7 systems. While the patch description mentions this is to fix compatibility issues, the patches in the new file (src/Package/Library/postgresql.php) still contain this code, so this removal appears to be an incomplete refactoring. Verify that the old file is no longer used and the new implementation covers all necessary cases.

Copilot uses AI. Check for mistakes.
Comment on lines +30 to +34
if (SystemTarget::getTargetOS() === 'Darwin') {
// on macOS, explicit_bzero is available but causes build failure due to detection issues, so we fake it as unavailable
shell()->cd($package->getSourceDir())
->exec('sed -i.backup "s/ac_cv_func_explicit_bzero\" = xyes/ac_cv_func_explicit_bzero\" = x_fake_yes/" ./configure');
}
Copy link

Copilot AI Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PostgreSQL explicit_bzero patch is now only applied on Darwin (macOS), but was previously applied unconditionally. If this function causes issues on other platforms, those builds may now fail. Consider documenting why this is macOS-specific or verifying that other platforms don't need this workaround.

Copilot uses AI. Check for mistakes.
$frankenphpAppPath = $this->getOption('with-frankenphp-app');

if ($frankenphpAppPath) {
$frankenphpAppPath = trim($frankenphpAppPath, "\"'");
Copy link

Copilot AI Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The trimming of quotes from the frankenphpAppPath is duplicated in both UnixBuilderBase.php (line 368) and in the new implementation. This suggests that both code paths might still be in use, or this is dead code. Verify which implementation is being used and ensure consistency.

Copilot uses AI. Check for mistakes.
Comment on lines +359 to +394
$valid_tests = ['cli', 'cgi', 'micro', 'micro-exts', 'embed', 'frankenphp'];
// compat: --without-micro-ext-test is equivalent to --no-smoke-test=micro-exts
if ($builder->getOption('without-micro-ext-test', false)) {
$valid_tests = array_diff($valid_tests, ['micro-exts']);
}
if (is_array($option)) {
/*
1. if option is not in valid tests, throw WrongUsageException
2. if all passed options are valid, remove them from $valid_tests, and run the remaining tests
*/
foreach ($option as $test) {
if (!in_array($test, $valid_tests, true)) {
throw new WrongUsageException("Invalid value for --no-smoke-test: {$test}. Valid values are: " . implode(', ', $valid_tests));
}
$valid_tests = array_diff($valid_tests, [$test]);
}
} elseif ($option === 'all') {
$valid_tests = [];
}
// run cli tests
if (in_array('cli', $valid_tests, true) && $installer->isPackageResolved('php-cli')) {
$package->runStage([$this, 'smokeTestCliForUnix']);
}
// run cgi tests
if (in_array('cgi', $valid_tests, true) && $installer->isPackageResolved('php-cgi')) {
$package->runStage([$this, 'smokeTestCgiForUnix']);
}
// run micro tests
if (in_array('micro', $valid_tests, true) && $installer->isPackageResolved('php-micro')) {
$skipExtTest = !in_array('micro-exts', $valid_tests, true);
$package->runStage([$this, 'smokeTestMicroForUnix'], ['skipExtTest' => $skipExtTest]);
}
// run embed tests
if (in_array('embed', $valid_tests, true) && $installer->isPackageResolved('php-embed')) {
$package->runStage([$this, 'smokeTestEmbedForUnix']);
}
Copy link

Copilot AI Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The smokeTestForUnix method doesn't include a check for 'frankenphp' in the valid_tests array, even though 'frankenphp' is listed as a valid test option on line 359. The FrankenPHP smoke test is executed separately in the build method (line 405), but users who specify --no-smoke-test=frankenphp will have their option validated but not respected. Consider either removing 'frankenphp' from valid_tests or adding the corresponding test execution logic.

Copilot uses AI. Check for mistakes.
Comment on lines 16 to 59
@@ -26,12 +26,12 @@
// 'macos-15-intel', // bin/spc for x86_64
// 'macos-15', // bin/spc for arm64
// 'ubuntu-latest', // bin/spc-alpine-docker for x86_64
// 'ubuntu-22.04', // bin/spc-gnu-docker for x86_64
// 'ubuntu-24.04', // bin/spc for x86_64
// 'ubuntu-22.04-arm', // bin/spc-gnu-docker for arm64
// 'ubuntu-24.04-arm', // bin/spc for arm64
'ubuntu-22.04', // bin/spc-gnu-docker for x86_64
'ubuntu-24.04', // bin/spc for x86_64
'ubuntu-22.04-arm', // bin/spc-gnu-docker for arm64
'ubuntu-24.04-arm', // bin/spc for arm64
// 'windows-2022', // .\bin\spc.ps1
'windows-2025',
// 'windows-2025',
];

// whether enable thread safe
@@ -50,13 +50,13 @@

// If you want to test your added extensions and libs, add below (comma separated, example `bcmath,openssl`).
$extensions = match (PHP_OS_FAMILY) {
'Linux', 'Darwin' => 'mysqli,gmp',
'Linux', 'Darwin' => 'pgsql',
'Windows' => 'com_dotnet',
};

// If you want to test shared extensions, add them below (comma separated, example `bcmath,openssl`).
$shared_extensions = match (PHP_OS_FAMILY) {
'Linux' => 'grpc,mysqlnd_parsec,mysqlnd_ed25519',
'Linux' => '',
Copy link

Copilot AI Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test configuration has commented out all PHP versions except 8.5, and changed the extension tests from 'mysqli,gmp' to 'pgsql'. While this may be intentional for testing specific extensions during the refactor, it significantly reduces test coverage. Consider uncommenting the other PHP versions and platform configurations to ensure comprehensive testing once the pgsql extension refactoring is complete.

Copilot uses AI. Check for mistakes.
Comment on lines +368 to +375
private static function escapeInlineTest(string $code): string
{
return str_replace(
['<?php', 'declare(strict_types=1);', "\n", '"', '$', '!'],
['', '', '', '\"', '\$', '"\'!\'"'],
$code
);
}
Copy link

Copilot AI Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The escapeInlineTest method may not handle all edge cases safely. The current implementation replaces '!' with '"'!'"' which could cause issues with certain test code patterns. Additionally, the method doesn't handle other special shell characters like backticks, semicolons, or pipe operators that could break the shell command. Consider using a more robust escaping mechanism or validating that test files don't contain problematic patterns.

Copilot uses AI. Check for mistakes.
Comment on lines +33 to 34
InteractiveTerm::setMessage('Building frankenphp: ' . ConsoleColor::yellow('processing --with-frankenphp-app option'));
$package->runStage([$this, 'processFrankenphpApp']);
Copy link

Copilot AI Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The InteractiveTerm::setMessage call for 'processing --with-frankenphp-app option' was removed from inside processFrankenphpApp (line 143 removed) and is now set before calling the method (line 33 added). However, this message will always be displayed even when no app path is provided. Consider moving the message back inside processFrankenphpApp to only display it when the option is actually being processed.

Copilot uses AI. Check for mistakes.
Comment on lines 140 to 144
$frankenphpAppPath = $package->getBuildOption('with-frankenphp-app');

if ($frankenphpAppPath) {
InteractiveTerm::setMessage('Building frankenphp: ' . ConsoleColor::yellow('processing --with-frankenphp-app option'));
if (!is_dir($frankenphpAppPath)) {
throw new WrongUsageException("The path provided to --with-frankenphp-app is not a valid directory: {$frankenphpAppPath}");
Copy link

Copilot AI Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new processFrankenphpApp implementation is missing the quote trimming logic that was added to UnixBuilderBase.php (line 368). If users pass quoted paths via command line, the directory existence check will fail. Consider adding 'trim($frankenphpAppPath, ""'")' before the is_dir check to maintain consistency and handle quoted paths properly.

Copilot uses AI. Check for mistakes.
with-suggested-libs:
description: Build with suggested libs
type: boolean
default: false
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should default to true here imo, like we do for Downloader

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm looking for a simpler way to build using GHA, instead of letting people fork it. Like reusable workflow or writing setup-static-php. WDYT?

Reusable workflow may be easier than writing custom action but considering already widely-used, we should think long-term.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should definitely create a github action that builds all required libraries for an extension and then prepares the build environment for an extension by source.

It could be used by PIE extension maintainers to ship precompiled binaries of their extensions with PIE that are statically linked.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants