diff --git a/.github/workflows/windows-build.yml b/.github/workflows/windows-build.yml index 9f364472..496a5e64 100644 --- a/.github/workflows/windows-build.yml +++ b/.github/workflows/windows-build.yml @@ -408,6 +408,50 @@ jobs: throw "Expected one Windows release package, found $($packages.Count)" } + - name: Verify portable Windows PHP configuration + if: startsWith(github.ref, 'refs/tags/') && matrix.php == '8.5' + shell: pwsh + run: | + $ErrorActionPreference = 'Stop' + $package = Get-ChildItem 'tpc_v*_windows_*.zip' -File | Select-Object -First 1 + $destination = Join-Path $env:RUNNER_TEMP ('typephp-release-check-' + [guid]::NewGuid()) + Expand-Archive -LiteralPath $package.FullName -DestinationPath $destination + $roots = @(Get-ChildItem $destination -Directory) + if ($roots.Count -ne 1) { + throw "Expected one release package directory, found $($roots.Count)" + } + $root = $roots[0].FullName + $phpExe = Join-Path $root 'php.exe' + $phpIni = Join-Path $root 'php.ini' + $checkFile = Join-Path $destination 'check-php-ini.php' + $check = @' + &1 | Out-String).Trim() + if ($LASTEXITCODE -ne 0 -or $output -ne 'portable-php-ini-ok') { + throw "Relocated PHP runtime validation failed in ${workingDirectory}: $output" + } + } finally { + Pop-Location + } + } + } finally { + $env:PHP_INI_SCAN_DIR = $previousScanDir + } + - name: Upload Windows release package if: startsWith(github.ref, 'refs/tags/') && matrix.php == '8.5' uses: actions/upload-artifact@v4 diff --git a/package.php b/package.php index 5d0e430c..f17b0356 100755 --- a/package.php +++ b/package.php @@ -228,6 +228,9 @@ PHPX_HOME must point to the bundled phpx directory. Its required files include: phpx\lib\phpx.lib phpx\src\misc +The bundled php.ini uses extension_dir = "./ext/" instead of the CI runner's +absolute extension path. + TEXT; mustWriteFile($windowsSetupFile, $windowsSetup); @@ -254,6 +257,18 @@ if (!is_dir($phpDir)) { // php8embed.lib 必须保留;部分 PHP SDK 仅在根目录提供该导入库。 copyDirectory($phpDir, $topLevelDir, ['dev/php8ts.lib'], null, ['zip']); + // Do not ship setup-php's runner-specific C:\tools\php\ext setting. + // Rewrite only the staged copy; the CI installation remains untouched. + $packagedPhpIni = "{$topLevelDir}/php.ini"; + if (!is_file($packagedPhpIni)) { + throw new RuntimeException("打包目录缺少 php.ini: {$packagedPhpIni}"); + } + $phpIni = file_get_contents($packagedPhpIni); + if (!is_string($phpIni)) { + throw new RuntimeException("无法读取 php.ini: {$packagedPhpIni}"); + } + mustWriteFile($packagedPhpIni, makeWindowsPhpIniPortable($phpIni)); + $packagedEmbedLib = "{$topLevelDir}/{$phpEmbedLibRelativePath}"; if (!is_file($packagedEmbedLib)) { echo "错误: php8embed.lib 未复制到打包目录 - {$packagedEmbedLib}\n"; @@ -426,6 +441,9 @@ if (!$zip->close()) { $requiredArchiveEntries = [ "{$topLevelDir}/{$compilerExe}", + "{$topLevelDir}/php.exe", + "{$topLevelDir}/php.ini", + "{$topLevelDir}/ext/php_zip.dll", "{$topLevelDir}/WINDOWS-SETUP.txt", "{$topLevelDir}/phpx.dll", "{$topLevelDir}/{$phpRuntimeDllRelativePath}", @@ -780,6 +798,29 @@ function copyDirectory(string $src, string $dest, array $excludeDirs = [], ?stri } } +/** Preserve enabled extensions and line endings while removing the CI path. */ +function makeWindowsPhpIniPortable(string $ini): string +{ + $bom = str_starts_with($ini, "\xEF\xBB\xBF") ? "\xEF\xBB\xBF" : ''; + $ini = substr($ini, strlen($bom)); + $portable = preg_replace( + '/^[\t ]*extension_dir[\t ]*=[^\r\n]*/mi', + 'extension_dir = "./ext/"', + $ini, + -1, + $replacements, + ); + if (!is_string($portable)) { + throw new RuntimeException('Unable to rewrite extension_dir in php.ini'); + } + if ($replacements === 0) { + $newline = str_contains($ini, "\r\n") ? "\r\n" : "\n"; + // Prepend to the global section, not a trailing [PATH=...] section. + $portable = 'extension_dir = "./ext/"' . $newline . $ini; + } + return $bom . $portable; +} + function mustCreateDirectory(string $dir): void { if (!is_dir($dir) && !mkdir($dir, 0755, true) && !is_dir($dir)) {