Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 113 additions & 0 deletions lib/ExtensionRegistry.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
<?php

declare(strict_types=1);

/**
* GENERATED by script/generate-extension-registry.php — do not edit by hand.
*
* The default extension set and the order it loads in (RELEASE-PLAN Phase 2.5). Regenerate with
* `php script/generate-extension-registry.php`; `--check` fails when this file is out of date.
*
* Order is significant and is copied verbatim from what Runtime::loadCoreModules() did before this
* file existed. Some constraints are declared on the modules themselves
* ({@see \PHPCompiler\Module::getExtensionDependencies}) and verified by
* script/check-extension-dependencies.php; others are still only implicit, which is why the order is
* preserved rather than derived.
*
* The entries are literal `new` expressions on purpose: the AOT compiler resolves these statically,
* and instantiating from a string would leave every module unreferenced and uncompiled.
*
* 76 extensions, all default-enabled — matching current behaviour, where every build pays for
* every extension. Selecting a subset is the next step and will filter on
* {@see \PHPCompiler\Module::isDefaultEnabled}.
*/

namespace PHPCompiler;

final class ExtensionRegistry
{
/**
* @return list<Module>
*/
public static function defaultModules(): array
{
return [
new \PHPCompiler\ext\types\Module(),
new \PHPCompiler\ext\spl\Module(),
new \PHPCompiler\ext\ds\Module(),
new \PHPCompiler\ext\intl\Module(),
new \PHPCompiler\ext\zip\Module(),
new \PHPCompiler\ext\libxml\Module(),
new \PHPCompiler\ext\dom\Module(),
new \PHPCompiler\ext\xsl\Module(),
new \PHPCompiler\ext\simplexml\Module(),
new \PHPCompiler\ext\xml\Module(),
new \PHPCompiler\ext\xmlrpc\Module(),
new \PHPCompiler\ext\wddx\Module(),
new \PHPCompiler\ext\xmlreader\Module(),
new \PHPCompiler\ext\xmlwriter\Module(),
new \PHPCompiler\ext\gd\Module(),
new \PHPCompiler\ext\exif\Module(),
new \PHPCompiler\ext\fileinfo\Module(),
new \PHPCompiler\ext\iconv\Module(),
new \PHPCompiler\ext\gettext\Module(),
new \PHPCompiler\ext\mbstring\Module(),
new \PHPCompiler\ext\filter\Module(),
new \PHPCompiler\ext\calendar\Module(),
new \PHPCompiler\ext\ldap\Module(),
new \PHPCompiler\ext\session\Module(),
new \PHPCompiler\ext\bcmath\Module(),
new \PHPCompiler\ext\gmp\Module(),
new \PHPCompiler\ext\stats\Module(),
new \PHPCompiler\ext\opcache\Module(),
new \PHPCompiler\ext\openssl\Module(),
new \PHPCompiler\ext\curl\Module(),
new \PHPCompiler\ext\hash\Module(),
new \PHPCompiler\ext\posix\Module(),
new \PHPCompiler\ext\inotify\Module(),
new \PHPCompiler\ext\pcntl\Module(),
new \PHPCompiler\ext\sockets\Module(),
new \PHPCompiler\ext\zmq\Module(),
new \PHPCompiler\ext\ftp\Module(),
new \PHPCompiler\ext\soap\Module(),
new \PHPCompiler\ext\ffi\Module(),
new \PHPCompiler\ext\ctype\Module(),
new \PHPCompiler\ext\tokenizer\Module(),
new \PHPCompiler\ext\random\Module(),
new \PHPCompiler\ext\igbinary\Module(),
new \PHPCompiler\ext\msgpack\Module(),
new \PHPCompiler\ext\simdjson\Module(),
new \PHPCompiler\ext\yaml\Module(),
new \PHPCompiler\ext\redis\Module(),
new \PHPCompiler\ext\mongodb\Module(),
new \PHPCompiler\ext\snmp\Module(),
new \PHPCompiler\ext\zstd\Module(),
new \PHPCompiler\ext\lzf\Module(),
new \PHPCompiler\ext\lz4\Module(),
new \PHPCompiler\ext\bz2\Module(),
new \PHPCompiler\ext\brotli\Module(),
new \PHPCompiler\ext\sodium\Module(),
new \PHPCompiler\ext\sqlite3\Module(),
new \PHPCompiler\ext\pgsql\Module(),
new \PHPCompiler\ext\odbc\Module(),
new \PHPCompiler\ext\dba\Module(),
new \PHPCompiler\ext\mailparse\Module(),
new \PHPCompiler\ext\enchant\Module(),
new \PHPCompiler\ext\gnupg\Module(),
new \PHPCompiler\ext\pspell\Module(),
new \PHPCompiler\ext\mysqli\Module(),
new \PHPCompiler\ext\tidy\Module(),
new \PHPCompiler\ext\pdo\Module(),
new \PHPCompiler\ext\phar\Module(),
new \PHPCompiler\ext\uri\Module(),
new \PHPCompiler\ext\uuid\Module(),
new \PHPCompiler\ext\uploadprogress\Module(),
new \PHPCompiler\ext\apcu\Module(),
new \PHPCompiler\ext\sysvshm\Module(),
new \PHPCompiler\ext\sysvsem\Module(),
new \PHPCompiler\ext\sysvmsg\Module(),
new \PHPCompiler\ext\reflection\Module(),
new \PHPCompiler\ext\standard\Module(),
];
}
}
83 changes: 7 additions & 76 deletions lib/Runtime.php
Original file line number Diff line number Diff line change
Expand Up @@ -223,82 +223,13 @@ public function setAotDebugSymbols(bool $enabled = true): void
}

private function loadCoreModules(): void {
$this->load(new ext\types\Module);
$this->load(new ext\spl\Module);
$this->load(new ext\ds\Module);
$this->load(new ext\intl\Module);
$this->load(new ext\zip\Module);
$this->load(new ext\libxml\Module);
$this->load(new ext\dom\Module);
$this->load(new ext\xsl\Module);
$this->load(new ext\simplexml\Module);
$this->load(new ext\xml\Module);
$this->load(new ext\xmlrpc\Module);
$this->load(new ext\wddx\Module);
$this->load(new ext\xmlreader\Module);
$this->load(new ext\xmlwriter\Module);
$this->load(new ext\gd\Module);
$this->load(new ext\exif\Module);
$this->load(new ext\fileinfo\Module);
$this->load(new ext\iconv\Module);
$this->load(new ext\gettext\Module);
$this->load(new ext\mbstring\Module);
$this->load(new ext\filter\Module);
$this->load(new ext\calendar\Module);
$this->load(new ext\ldap\Module);
$this->load(new ext\session\Module);
$this->load(new ext\bcmath\Module);
$this->load(new ext\gmp\Module);
$this->load(new ext\stats\Module);
$this->load(new ext\opcache\Module);
$this->load(new ext\openssl\Module);
$this->load(new ext\curl\Module);
$this->load(new ext\hash\Module);
$this->load(new ext\posix\Module);
$this->load(new ext\inotify\Module);
$this->load(new ext\pcntl\Module);
$this->load(new ext\sockets\Module);
$this->load(new ext\zmq\Module);
$this->load(new ext\ftp\Module);
$this->load(new ext\soap\Module);
$this->load(new ext\ffi\Module);
$this->load(new ext\ctype\Module);
$this->load(new ext\tokenizer\Module);
$this->load(new ext\random\Module);
$this->load(new ext\igbinary\Module);
$this->load(new ext\msgpack\Module);
$this->load(new ext\simdjson\Module);
$this->load(new ext\yaml\Module);
$this->load(new ext\redis\Module);
$this->load(new ext\mongodb\Module);
$this->load(new ext\snmp\Module);
$this->load(new ext\zstd\Module);
$this->load(new ext\lzf\Module);
$this->load(new ext\lz4\Module);
$this->load(new ext\bz2\Module);
$this->load(new ext\brotli\Module);
$this->load(new ext\sodium\Module);
$this->load(new ext\sqlite3\Module);
$this->load(new ext\pgsql\Module);
$this->load(new ext\odbc\Module);
$this->load(new ext\dba\Module);
$this->load(new ext\mailparse\Module);
$this->load(new ext\enchant\Module);
$this->load(new ext\gnupg\Module);
$this->load(new ext\pspell\Module);
$this->load(new ext\mysqli\Module);
$this->load(new ext\tidy\Module);
$this->load(new ext\pdo\Module);
$this->load(new ext\phar\Module);
$this->load(new ext\uri\Module);
$this->load(new ext\uuid\Module);
$this->load(new ext\uploadprogress\Module);
$this->load(new ext\apcu\Module);
$this->load(new ext\sysvshm\Module);
$this->load(new ext\sysvsem\Module);
$this->load(new ext\sysvmsg\Module);
$this->load(new ext\reflection\Module);
$this->load(new ext\standard\Module);
// Generated list — see script/generate-extension-registry.php and RELEASE-PLAN Phase 2.5.
// Adding an extension used to mean editing this core file; it now means adding a directory
// and regenerating. Order is unchanged from the 76 hardcoded loads this replaced, verified
// identical at generation time and by test/unit/ExtensionRegistryOrderTest.php.
foreach (ExtensionRegistry::defaultModules() as $module) {
$this->load($module);
}
}

public function loadJit(): JIT {
Expand Down
22 changes: 17 additions & 5 deletions script/check-extension-dependencies.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,25 @@
$root = dirname(__DIR__);
$json = in_array('--json', $argv, true);

// The real order, as Runtime loads it.
$runtimeSrc = (string) file_get_contents($root.'/lib/Runtime.php');
// The real order, from the generated registry (falling back to Runtime on older trees).
$order = [];
if (preg_match('/private function loadCoreModules\(\): void \{(.*?)\n \}/s', $runtimeSrc, $m)
&& preg_match_all('/\$this->load\(new ext\\\\([A-Za-z0-9_]+)\\\\Module\)/', $m[1], $mm)
$registryPath = $root.'/lib/ExtensionRegistry.php';
if (is_file($registryPath)
&& preg_match_all(
'/new \\\\PHPCompiler\\\\ext\\\\([A-Za-z0-9_]+)\\\\Module\\(\\)/',
(string) file_get_contents($registryPath),
$rm
)
) {
$order = $mm[1];
$order = $rm[1];
} else {
// Pre-registry trees still carry the hardcoded list in Runtime::loadCoreModules().
$runtimeSrc = (string) file_get_contents($root.'/lib/Runtime.php');
if (preg_match('/private function loadCoreModules\\(\\): void \\{(.*?)\\n \\}/s', $runtimeSrc, $m)
&& preg_match_all('/\\$this->load\\(new ext\\\\([A-Za-z0-9_]+)\\\\Module\\)/', $m[1], $mm)
) {
$order = $mm[1];
}
}
if ([] === $order) {
fwrite(STDERR, "check-extension-dependencies: could not parse Runtime::loadCoreModules()\n");
Expand Down
25 changes: 19 additions & 6 deletions script/extension-inventory.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,27 @@
}
ksort($onDisk);

// 2. The load list, in order, as Runtime actually loads it.
$runtimeSrc = (string) file_get_contents($root.'/lib/Runtime.php');
$loaded = [];
if (preg_match('/private function loadCoreModules\(\): void \{(.*?)\n \}/s', $runtimeSrc, $m)) {
if (preg_match_all('/\$this->load\(new ext\\\\([A-Za-z0-9_]+)\\\\Module\)/', $m[1], $mm)) {
$loaded = $mm[1];
// The real order, from the generated registry (falling back to Runtime on older trees).
$order = [];
$registryPath = $root.'/lib/ExtensionRegistry.php';
if (is_file($registryPath)
&& preg_match_all(
'/new \\\\PHPCompiler\\\\ext\\\\([A-Za-z0-9_]+)\\\\Module\\(\\)/',
(string) file_get_contents($registryPath),
$rm
)
) {
$order = $rm[1];
} else {
// Pre-registry trees still carry the hardcoded list in Runtime::loadCoreModules().
$runtimeSrc = (string) file_get_contents($root.'/lib/Runtime.php');
if (preg_match('/private function loadCoreModules\\(\\): void \\{(.*?)\\n \\}/s', $runtimeSrc, $m)
&& preg_match_all('/\\$this->load\\(new ext\\\\([A-Za-z0-9_]+)\\\\Module\\)/', $m[1], $mm)
) {
$order = $mm[1];
}
}
$loaded = $order;

// 3. File counts — Phase 2.5 sizes the core/optional split by these.
$fileCount = static function (string $dir): int {
Expand Down
129 changes: 129 additions & 0 deletions script/generate-extension-registry.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
<?php

declare(strict_types=1);

/**
* Generates lib/ExtensionRegistry.php from the load list in Runtime::loadCoreModules().
*
* RELEASE-PLAN Phase 2.5: "a per-extension manifest enumerated at build time, replacing 75
* constructor calls". This is the first half — move the list out of a hand-maintained method in a
* core file and into a generated one, WITHOUT changing the order.
*
* Two deliberate constraints:
*
* 1. The generated file emits literal `new \PHPCompiler\ext\<name>\Module()` expressions, not
* dynamic instantiation from strings. The AOT compiler resolves these statically; a
* `new $className` would leave the modules unreferenced and they would not be compiled in.
*
* 2. The order is copied verbatim from the current hardcoded list. Ordering constraints here are
* real (libxml before dom before xsl) and partly still undeclared, so deriving a fresh order
* from dependencies is a separate, later step that must be proven equivalent first.
*
* Usage:
* php script/generate-extension-registry.php # write lib/ExtensionRegistry.php
* php script/generate-extension-registry.php --check # fail if the file is out of date
*/

$root = dirname(__DIR__);
$check = in_array('--check', $argv, true);
$target = $root.'/lib/ExtensionRegistry.php';

$runtimeSrc = (string) file_get_contents($root.'/lib/Runtime.php');

// Prefer the hardcoded list while it still exists; once Runtime consumes the registry, the
// committed registry is the source of truth and regeneration is a no-op that still verifies.
$order = [];
if (preg_match('/private function loadCoreModules\(\): void \{(.*?)\n \}/s', $runtimeSrc, $m)
&& preg_match_all('/\$this->load\(new ext\\\\([A-Za-z0-9_]+)\\\\Module\)/', $m[1], $mm)
) {
$order = $mm[1];
}

if ([] === $order && is_file($target)) {
// Runtime already delegates; re-read the committed registry so --check stays meaningful.
$existing = (string) file_get_contents($target);
if (preg_match_all('/new \\\\PHPCompiler\\\\ext\\\\([A-Za-z0-9_]+)\\\\Module\(\)/', $existing, $em)) {
$order = $em[1];
}
}

if ([] === $order) {
fwrite(STDERR, "generate-extension-registry: could not determine the load order\n");
exit(2);
}

$missing = [];
foreach ($order as $name) {
if (!is_file($root.'/ext/'.$name.'/Module.php')) {
$missing[] = $name;
}
}
if ([] !== $missing) {
fwrite(STDERR, 'generate-extension-registry: load list names extensions with no Module.php: '
.implode(', ', $missing)."\n");
exit(2);
}

$lines = [];
foreach ($order as $name) {
$lines[] = sprintf(' new \PHPCompiler\ext\%s\Module(),', $name);
}
$body = implode("\n", $lines);
$count = count($order);

$out = <<<PHP
<?php

declare(strict_types=1);

/**
* GENERATED by script/generate-extension-registry.php — do not edit by hand.
*
* The default extension set and the order it loads in (RELEASE-PLAN Phase 2.5). Regenerate with
* `php script/generate-extension-registry.php`; `--check` fails when this file is out of date.
*
* Order is significant and is copied verbatim from what Runtime::loadCoreModules() did before this
* file existed. Some constraints are declared on the modules themselves
* ({@see \\PHPCompiler\\Module::getExtensionDependencies}) and verified by
* script/check-extension-dependencies.php; others are still only implicit, which is why the order is
* preserved rather than derived.
*
* The entries are literal `new` expressions on purpose: the AOT compiler resolves these statically,
* and instantiating from a string would leave every module unreferenced and uncompiled.
*
* {$count} extensions, all default-enabled — matching current behaviour, where every build pays for
* every extension. Selecting a subset is the next step and will filter on
* {@see \\PHPCompiler\\Module::isDefaultEnabled}.
*/

namespace PHPCompiler;

final class ExtensionRegistry
{
/**
* @return list<Module>
*/
public static function defaultModules(): array
{
return [
{$body}
];
}
}

PHP;

if ($check) {
$current = is_file($target) ? (string) file_get_contents($target) : '';
if ($current === $out) {
printf("generate-extension-registry: ok — lib/ExtensionRegistry.php is current (%d extensions)\n", $count);
exit(0);
}
fwrite(STDERR, "generate-extension-registry: lib/ExtensionRegistry.php is OUT OF DATE\n");
fwrite(STDERR, " run: php script/generate-extension-registry.php\n");
exit(1);
}

file_put_contents($target, $out);
printf("generate-extension-registry: wrote lib/ExtensionRegistry.php (%d extensions)\n", $count);
exit(0);
Loading
Loading