Repository navigation
fix: keep custom extensions registered after PHP restarts - #2788
Open
aguinaldotupy wants to merge 1 commit into
Open
aguinaldotupy wants to merge 1 commit into
aguinaldotupy wants to merge 1 commit into
Conversation
Contributor
|
Hi @aguinaldotupy, Thanks for taking over #2579 and bringing the fix forward! Glad to see this work moving ahead. Since your PR builds on our original contribution, would you be open to adding me as a co-author? Thanks again for your work on this! |
register_internal_extensions() freed the module list after the first php_module_startup(), so every later startup registered no custom extension: their functions, classes and constants were gone after a main thread reboot (file watcher, the admin `workers/restart` endpoint, `opcache_reset()`) and after `Shutdown()` and `Init()`, which Caddy runs on config reloads. Keep the C copy of the list for the process lifetime and make register_extensions() install the hook only once. PHP destroys the module registry and resets `module_started` on every module shutdown, and registers its own static modules the same way on each startup. Fixes php#2572 Co-authored-by: ousamabenyounes <benyounes.ousama@gmail.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
aguinaldotupy
force-pushed
the
fix/2572-extensions-after-reboot
branch
from
October 29, 2026 00:01
e7e7d7f to
44972c0
Compare
Author
|
Hi @ousamabenyounes, of course — the fix builds directly on your work in #2579. I've added you as co-author on the commit ( |
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Prepared with an AI assistant (Claude), which wrote the patch and ran the tests below; I asked for it.
Fixes #2572.
register_internal_extensions()freed the module list after the firstphp_module_startup(), so every later startup registered no custom extension. Their functions, classes and constants disappeared after a main thread reboot (file watcher,RestartWorkers()and the admin restart endpoint,opcache_reset()) and afterShutdown()andInit(), which Caddy runs on config reloads.This picks up #2579 by @ousamabenyounes, rebased on
main, and follows the review it got there.Changes
register_internal_extensions()keeps the C copy of the module list for the process lifetime, so each startup registers the modules again.register_extensions()returns early once the hook is installed, or when the list is empty. A second call can no longer make the hook its own predecessor.This matches what PHP does for its own static modules:
zend_startup()createsmodule_registryon each startup, andmodule_destructor()runs MSHUTDOWN, frees the ZTS globals id and resetsmodule_startedon each shutdown. Re-registering the same staticzend_module_entryis therefore a fresh registration.Tests
internal/testextnow restarts PHP twice, first withRestartWorkers()and then withShutdown()andInit(), and checks after each restart that both test modules are loaded.ext2gets a function, a MINIT and module globals (GINIT), so the test covers the ZTS globals id being freed and allocated again. MINIT increments a process-wide counter and exposes it asEXT2_MINIT_CALLS, which must read 1, 2 and then 3. IfRestartWorkers()returns without rebooting, the count stays at 1 and the test fails, so no API change is needed to observe the reboot.RED on
main(dev image, PHP 8.5 ZTS debug,go test -race ./internal/testext):GREEN with the fix:
Also run in the dev image:
gotestsum -- -race ./...for the library (1192 tests, 8 skipped because the image has no pcntl) and forcaddy/, theinternal/testexttest 20 times in fresh processes, golangci-lint (0 issues),go mod tidy -diff, and super-linter slim v9.0.0 with the CI settings (clang-format 22.1.3 included).Not in this PR
RegisterExtension()after the firstInit()is still ignored silently. Reporting it would need an API change, so it belongs in a separate PR.Unable to start builtin modulesandphp_main()ignores the startup result. Every startup registers the same list into a new registry, so a reboot should not fail where the first boot succeeded. I can handle the startup result in a follow-up if you want it.