Recovery final readonly class
Minn\Runtime\Recovery · public/minn/src/Minn/Runtime/Recovery.php · 234 lines
Recovery from a fatal in someone else's code. When a plugin or theme kills the boot of a request, the file it died in names it; a second such failure inside ten minutes records it as paused, and the next request loads without it: the site comes back on its own instead of staying down until a human reads the log. One failure is only noted: a pause is a site-wide decision, and a single request can be a crafted one. Failures after boot (a route, a template) never pause anything; see Failure::armRecovery().
The paused list is stored where WordPress stores it, in the same shape (`paused_plugins` keyed by plugin file, `paused_themes` by stylesheet, each holding type, file, line and message), so a site that ejects back to WordPress finds the pause it left with. The strikes live in the engine's own `minn_recovery_strikes` option.
Constants and properties
| const | PLUGINS_OPTION = 'paused_plugins' | |
| const | THEMES_OPTION = 'paused_themes' | |
| const | STRIKES_OPTION = 'minn_recovery_strikes' | |
| const | WINDOW = 600 | Seconds inside which a second boot failure pauses the extension. |
| private | readonly Minn\Content\Site $site | Promoted from the constructor. |
| private | readonly string $contentDir | Promoted from the constructor. |
Public
| new | __construct(Minn\Content\Site $site, string $contentDir)# | |||||||
| method | blame(string $file, array $realpaths = array ( ), ?array $active = NULL): ?array# | The extension a file belongs to: a plugin as the active `folder/file.php` whose folder holds the file (or the bare file of a single-file plugin), a theme as its slug. The name is the one the loader checks against the paused list, so a fatal deep inside a plugin's subfolders pauses the plugin and not a folder nothing is called by. Anything outside the plugin and theme folders belongs to nobody, and is never paused: a fatal in the engine or in core is not a plugin's fault and pausing something would not fix it. A symlinked plugin reports its real location; the realpath map (real folder to the folder under plugins/) brings it home first.
@return array{kind: 'plugin'|'theme', name: string}|null | ||||||
| method | pause(array $blamed, array $error): bool# | Records an extension as paused. Returns false when it was already paused, so a caller can tell a fresh failure from a repeat and only notify once.
| ||||||
| method | strike(array $blamed): bool# | Notes a boot failure against an extension. True when it is the second inside the window, which is when the caller pauses it; the first is only remembered. Strikes older than the window are dropped as they are read, so the option never grows.
| ||||||
| method | pausedPlugins(): array# | The plugins recovery mode has paused. @return array<string, array<string, mixed>> | ||||||
| method | pausedThemes(): array# | The themes recovery mode has paused. @return array<string, array<string, mixed>> | ||||||
| method | paused(string $kind): array# | The extensions of one kind ('plugin' or 'theme') recovery has paused. @return array<string, array<string, mixed>> | ||||||
| method | resumeKind(string $kind): int# | Lets every extension of one kind load again. Returns how many were released. | ||||||
| method | resume(string $kind, string $name): bool# | Lets an extension load again. Returns false when it was not paused. | ||||||
| method | resumeAll(): int# | Lets everything load again. Returns how many were released. |
Internals 6
Private and protected methods: how the class does its work. Not a contract; they change whenever the code is tidied.
| private | pluginFile(array $parts, array $active): string# | The active plugin file a path under plugins/ belongs to: the entry in the folder the path starts with, the bare file itself for a single-file plugin, and the folder alone when no active entry claims it.
| ||||||
| private | activePlugins(): array# | @return list<string> | ||||||
| private | strikes(): array# | @return array<string, list<int>> | ||||||
| private | forgetStrikes(array $blamed): void# |
| ||||||
| private | read(string $option): array# | @return array<string, array<string, mixed>> | ||||||
| private | write(string $option, array $paused): void# |
|
Nothing matches.