Category
bootstrap | stdlib
Problem
In-tree modules register builtins (hash(), json_encode(), json_decode(), …) but extension_loaded('hash') and extension_loaded('json') return false, and those names are missing from get_loaded_extensions().
Framework/feature probes and self-host scripts gate on extension_loaded() exactly like Zend — false negatives cause fallback paths or fatal misconfiguration even when functions work.
Verified 2026-06-07:
./script/docker-exec.sh -- bash -lc 'source script/php-env.sh
php bin/vm.php -r "var_export(function_exists(\"hash\")); echo \"\\n\";
var_export(extension_loaded(\"hash\")); echo \"\\n\";
var_export(in_array(\"hash\", get_loaded_extensions(), true)); echo \"\\n\";
var_export(function_exists(\"json_encode\")); echo \"\\n\";
var_export(extension_loaded(\"json\")); echo \"\\n\";"'
# true / false / false / true / false
php-src reference
- php/php-src
ext/standard/info.c — PHP_FUNCTION(extension_loaded), get_loaded_extensions
- Module registration:
ext/hash/hash.c, ext/json/json.c — zend_module_entry names
Repro
<?php
declare(strict_types=1);
foreach (['hash', 'json', 'date', 'random'] as $ext) {
echo $ext, ': fn=', (int) function_exists($ext === 'hash' ? 'hash' : ($ext === 'json' ? 'json_encode' : $ext)),
' loaded=', (int) extension_loaded($ext), "\n";
}
print_r(get_loaded_extensions());
Zend: extension_loaded('hash') true when ext/hash enabled; list includes module names.
VM today: functions may work while extension_loaded is false (see probe above).
Scope (this repo)
| Layer |
Path |
Notes |
| Module registry |
ext/*/Module.php, runtime loader in lib/Runtime.php |
Track logical extension names when modules register |
| Builtins |
ext/standard/extension_loaded.php, get_loaded_extensions.php |
Read registry, not host extension_loaded() |
| Bootstrap |
script/bootstrap-inventory.php probes |
align with M5 vendor/self-host gates |
| Tests |
test/compliance/cases/stdlib/extension_loaded_in_tree.phpt |
hash/json/date at minimum |
PHP-in-PHP first: registry table in PHP (ext/standard/ModuleRegistry.php or similar), not new C module list.
Umbrella: #4839 full registry parity — implement this slice first (modules that already have handlers).
Done when
Verification
./script/docker-exec.sh -- bash -lc 'source script/php-env.sh && vendor/bin/phpunit --filter extension_loaded_in_tree'
php script/bootstrap-inventory.php --check
Links
Category
bootstrap|stdlibProblem
In-tree modules register builtins (
hash(),json_encode(),json_decode(), …) butextension_loaded('hash')andextension_loaded('json')returnfalse, and those names are missing fromget_loaded_extensions().Framework/feature probes and self-host scripts gate on
extension_loaded()exactly like Zend — false negatives cause fallback paths or fatal misconfiguration even when functions work.Verified 2026-06-07:
php-src reference
ext/standard/info.c—PHP_FUNCTION(extension_loaded),get_loaded_extensionsext/hash/hash.c,ext/json/json.c—zend_module_entrynamesRepro
Zend:
extension_loaded('hash')true when ext/hash enabled; list includes module names.VM today: functions may work while
extension_loadedis false (see probe above).Scope (this repo)
ext/*/Module.php, runtime loader inlib/Runtime.phpext/standard/extension_loaded.php,get_loaded_extensions.phpextension_loaded()script/bootstrap-inventory.phpprobestest/compliance/cases/stdlib/extension_loaded_in_tree.phptPHP-in-PHP first: registry table in PHP (
ext/standard/ModuleRegistry.phpor similar), not new C module list.Umbrella: #4839 full registry parity — implement this slice first (modules that already have handlers).
Done when
extension_loaded('hash')andextension_loaded('json')true when respective handlers registeredget_loaded_extensions()includeshash,json,standard,types, … matching active in-tree modulesextension_loaded('nonexistent')still false./script/ci-fast.sh --filter extension_loaded_in_treegreenVerification
./script/docker-exec.sh -- bash -lc 'source script/php-env.sh && vendor/bin/phpunit --filter extension_loaded_in_tree' php script/bootstrap-inventory.php --checkLinks