One module whose files could not load (billing 1.1.0 shipped
ionCube-encoded without its module.lic) killed every admin request and
every console.php command with an uncatchable loader fatal.
- bootAll(): a module that throws while booting is dropped for that
process; the other modules and the panel carry on.
- Module event listeners are wrapped: what they throw is logged and the
core operation that dispatched the event completes.
- ModuleQuarantine: a shutdown function reads a fatal raised inside a
module directory (parse/compile/ionCube licence errors, not memory or
time limits or uncaught exceptions) and records the module with a
fingerprint of its files in Modules/.quarantine.json. ModuleLoader
skips it, so only the process that hit the fatal is lost. Changed
files (an update, a rollback, a .lic) lift the record. The file is
written via an exclusive temp file + rename, never through a link.
- ModuleManager never requires a quarantined module's code: install and
update refuse with the reason, uninstall/delete skip its uninstall()
hook and still drop its schema from database_drop.sql. The module
list shows the quarantine as a warning.