Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
7b6dc2d
Build PHP binaries with Xdebug
fredrikekelund May 12, 2026
702ea22
WIP
fredrikekelund May 12, 2026
594e5af
New approach
fredrikekelund May 12, 2026
5c15545
Fix
fredrikekelund May 12, 2026
c0d6ba3
Address build log warnings
fredrikekelund May 12, 2026
0513325
WIP
fredrikekelund May 12, 2026
74e219d
WIP
fredrikekelund May 12, 2026
7ec141c
WIP
fredrikekelund May 12, 2026
f610b55
GitHub API token
fredrikekelund May 12, 2026
0d48dff
pkg-config
fredrikekelund May 12, 2026
d35d4ac
pkg-config
fredrikekelund May 12, 2026
a7aa765
Fix libcurl issue
fredrikekelund May 12, 2026
a65dfec
WIP
fredrikekelund May 12, 2026
beab364
Clean up
fredrikekelund May 12, 2026
8132a23
curl fix
fredrikekelund May 12, 2026
b2ac2c5
Embed curl
fredrikekelund May 12, 2026
695279f
More curl
fredrikekelund May 12, 2026
241cb89
Merge branch 'trunk' into rsm-2698-build-php-with-xdebug
fredrikekelund May 12, 2026
34f1d07
Normalize directory structure on macOS
fredrikekelund May 13, 2026
c606c6c
Name GitHub action run using inputs
fredrikekelund May 13, 2026
491ec37
Address review comments
fredrikekelund May 13, 2026
4d8caad
WIP
fredrikekelund May 13, 2026
477e6b1
Rely on existing spc tools where possible
fredrikekelund May 13, 2026
6298024
Fix extension smoke tests failing
fredrikekelund May 13, 2026
b9955ac
Fix xdebug extension testing
fredrikekelund May 13, 2026
630cc54
Cleanup
fredrikekelund May 13, 2026
c4582d4
curl smoke test fix
fredrikekelund May 13, 2026
cacc2cd
Bring back PHP version resolver
fredrikekelund May 13, 2026
a2c1065
Dynamically find latest extension versions on Windows
fredrikekelund May 13, 2026
b209699
Apply phar patch
fredrikekelund May 13, 2026
efc25d0
dom apparently also needs to be static
fredrikekelund May 13, 2026
bf47f9f
Fix phar issue (again)
fredrikekelund May 13, 2026
e60bcae
Make xdebug the only shared extension
fredrikekelund May 13, 2026
28d14f5
Docs
fredrikekelund May 13, 2026
a447f4a
[WIP] Load PHP extensions properly on Windows
fredrikekelund May 13, 2026
b269191
Update the extension list
fredrikekelund May 13, 2026
70598be
Merge branch 'trunk' into rsm-2698-build-php-with-xdebug
fredrikekelund May 13, 2026
b25d8e5
Merge branch 'rsm-2698-build-php-with-xdebug' into f26d/load-xdebug
fredrikekelund May 13, 2026
2cd6ea0
Merge branch 'trunk' into f26d/load-xdebug
fredrikekelund May 19, 2026
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 85 additions & 1 deletion apps/cli/lib/native-php.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import fs from 'fs';
import os from 'os';
import path from 'path';
import { NativePhpSupportedVersion } from '@studio/common/lib/php-binary-metadata';
import { getPhpBinaryPath } from './dependency-management/paths';

// Disabled by default to shrink the attack surface available to PHP code
// running inside a Studio site. Each entry falls into one of:
Expand Down Expand Up @@ -57,6 +58,59 @@ const PHP_DEFAULT_DISABLED_FUNCTIONS = [
'system',
] as const;

// Extensions to enable on Windows via `-d extension=<name>`. Computed as the
// intersection of two sets:
// 1. php_*.dll files that ship as separate DLLs in windows.php.net's
// prebuilt zip (plus the PECL DLLs the workflow overlays: apcu, igbinary,
// redis, ssh2, yaml). Everything else from the macOS list is baked into
// php.exe itself (bcmath, calendar, ctype, dom, filter, iconv, mbregex,
// mysqlnd, pdo, phar, session, simplexml, tokenizer, xml*, zlib) and
// would emit "Module already loaded" warnings if we tried to enable it
// with `extension=`.
// 2. The curated macOS extension list in .github/workflows/build-php-cli-binaries.yml.
// windows.php.net also ships bz2, com_dotnet, enchant, ffi, gmp, ldap,
// odbc, pdo_firebird, pdo_odbc, pdo_pgsql, pgsql, snmp, soap, sysvshm,
// and tidy, but Studio doesn't ship those on macOS, so we don't enable
// them on Windows either to keep behavior symmetric.
// opcache and xdebug are Zend extensions and loaded separately via
// `zend_extension=` (the latter only when config.enableXdebug is true). On
// macOS every extension is baked into the `php` binary by static-php-cli, so
// this list is irrelevant there.
const WINDOWS_PHP_EXTENSIONS = [
'apcu',
'curl',
'dba',
'exif',
'fileinfo',
'ftp',
'gd',
'gettext',
'igbinary',
'intl',
'mbstring',
'mysqli',
'openssl',
'pdo_mysql',
'pdo_sqlite',
'redis',
'shmop',
'sockets',
'sodium',
'sqlite3',
'ssh2',
'xsl',
'yaml',
'zip',
] as const;

function getExtensionDir( phpVersion: NativePhpSupportedVersion ): string {
return path.join( path.dirname( getPhpBinaryPath( phpVersion ) ), 'ext' );
}

function getXdebugFilename(): string {
return process.platform === 'win32' ? 'php_xdebug.dll' : 'xdebug.so';
}

// Process-scoped opcache dir, created lazily and removed when the process exits
let opcacheRootDir: string | null = null;

Expand Down Expand Up @@ -86,7 +140,8 @@ function getOpcacheRootDir(): string {
export function getDefaultPhpArgs(
phpVersion: NativePhpSupportedVersion,
openBasedir: string[] = [],
disallowRiskyFunctions: boolean = false
disallowRiskyFunctions: boolean = false,
enableXdebug: boolean = false
): string[] {
// Partition the file_cache by PHP version: opcache's on-disk script blob
// format isn't stable across minor versions, and reusing a cache populated
Expand All @@ -106,6 +161,35 @@ export function getDefaultPhpArgs(
`opcache.cache_id="studio-${ cacheId }"`,
];

const extensionDir = getExtensionDir( phpVersion );

if ( process.platform === 'win32' ) {
// Load every bundled DLL from the artifact's ext/ directory.
// windows.php.net's prebuilt php.exe doesn't auto-load extensions;
// each one needs an explicit `extension=` (or `zend_extension=` for
// opcache) directive.
args.push( '-d', `extension_dir="${ extensionDir }"` );
args.push( '-d', `zend_extension=opcache` );
for ( const extension of WINDOWS_PHP_EXTENSIONS ) {
args.push( '-d', `extension=${ extension }` );
}
}

if ( enableXdebug ) {
// On macOS the `php` binary has every other extension baked in and ext/
// contains only xdebug.so; on Windows extension_dir is already set
// above. Either way the Zend extension path is ext/<filename>.
if ( process.platform !== 'win32' ) {
args.push( '-d', `extension_dir="${ extensionDir }"` );
}
args.push(
'-d',
`zend_extension="${ path.join( extensionDir, getXdebugFilename() ) }"`,
'-d',
'xdebug.mode=debug'
);
}

if ( openBasedir.length ) {
args.push( '-d', `open_basedir="${ openBasedir.join( path.delimiter ) }"` );
}
Expand Down
6 changes: 5 additions & 1 deletion apps/cli/php-server-child.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ function errorToConsole( ...args: Parameters< typeof console.error > ) {
type SpawnPhpProcessOptions = {
disallowRiskyFunctions?: boolean;
mode?: 'pipe' | 'capture-stdout';
enableXdebug?: boolean;
onlyPathsThatPhpCanAccess?: string[];
phpVersion: NativePhpSupportedVersion;
siteFolder?: string;
Expand All @@ -85,14 +86,16 @@ function spawnPhpProcess(
siteFolder,
signal,
mode = 'pipe',
enableXdebug = false,
onlyPathsThatPhpCanAccess = [],
disallowRiskyFunctions = false,
}: SpawnPhpProcessOptions
): ChildProcess {
const defaultArgs = getDefaultPhpArgs(
phpVersion,
onlyPathsThatPhpCanAccess,
disallowRiskyFunctions
disallowRiskyFunctions,
enableXdebug
);
const phpArgs = [ ...defaultArgs, ...args ];
const phpScriptProcess = spawn( getPhpBinaryPath( phpVersion ), phpArgs, {
Expand Down Expand Up @@ -487,6 +490,7 @@ async function doStartServer(
siteFolder: config.sitePath,
onlyPathsThatPhpCanAccess: Array.from( openBasedirAllowlist ),
disallowRiskyFunctions: true,
enableXdebug: config.enableXdebug,
} );
spawnedChild = serverChild;

Expand Down
Loading