API / Minn\Runtime / Recovery

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

constPLUGINS_OPTION = 'paused_plugins'
constTHEMES_OPTION = 'paused_themes'
constSTRIKES_OPTION = 'minn_recovery_strikes'
constWINDOW = 600

Seconds inside which a second boot failure pauses the extension.

privatereadonly Minn\Content\Site $site

Promoted from the constructor.

privatereadonly string $contentDir

Promoted from the constructor.

Public

new__construct(Minn\Content\Site $site, string $contentDir)#
methodblame(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.

$realpathsarray<string, string>
$activelist<string>|nullthe active plugin files; read from the site when omitted
@return array{kind: 'plugin'|'theme', name: string}|null
methodpause(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.

$blamedarray{kind: string, name: string}
$errorarray{type: int, file: string, line: int, message: string}
methodstrike(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.

$blamedarray{kind: string, name: string}
methodpausedPlugins(): array#

The plugins recovery mode has paused.

@return array<string, array<string, mixed>>
methodpausedThemes(): array#

The themes recovery mode has paused.

@return array<string, array<string, mixed>>
methodpaused(string $kind): array#

The extensions of one kind ('plugin' or 'theme') recovery has paused.

@return array<string, array<string, mixed>>
methodresumeKind(string $kind): int#

Lets every extension of one kind load again. Returns how many were released.

methodresume(string $kind, string $name): bool#

Lets an extension load again. Returns false when it was not paused.

methodresumeAll(): 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.

privatepluginFile(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.

$partslist<string>the path segments under plugins/
$activelist<string>
privateactivePlugins(): array#
@return list<string>
privatestrikes(): array#
@return array<string, list<int>>
privateforgetStrikes(array $blamed): void#
$blamedarray{kind: string, name: string}
privateread(string $option): array#
@return array<string, array<string, mixed>>
privatewrite(string $option, array $paused): void#
$pausedarray<string, array<string, mixed>>