Minn\Runtime
The WordPress runtime plugins load against. 112 classes, 607 public methods and 280 internals. This page is the public surface; each class name opens the whole class.
Abilities final class
public/minn/src/Minn/Runtime/Abilities.php · 226 lines · Open the class →
The abilities registry behind WP_Abilities_Registry, WP_Ability_Categories_Registry and the wp_*_ability facade, as the reference keeps it (probes abilities-registry, abilities-classes). The categories init and then the abilities init fire once each, lazily, on the first lookup of their kind, each handed its registry. The registries themselves take a registration at any time; only the wp_register_* functions insist on the init action. A registration is checked as the reference checks it (name shape, duplicates, a known category, then the properties the ability and category classes check) and refused with its notice. An ability's meta gains the default annotations, show_in_rest (public's value when unset) and public.
| static | initializeCategories(): void | Fires the categories action once. |
| static | initialize(): void | Fires the categories action, then the abilities action, each once. |
| static | registerCategory(string $slug, array $args): ?array | Registers a category; its row, or null with the reference's notice. @return array<string, mixed>|null @return array<string, mixed>|null |
| static | register(string $name, array $args): ?array | Registers an ability; its row, or null with the reference's notice. @return array<string, mixed>|null @return array<string, mixed>|null |
| static | unregister(string $name): ?array | Removes an ability; its row, or null with the reference's notice. @return array<string, mixed>|null @return array<string, mixed>|null |
| static | unregisterCategory(string $slug): ?array | Removes a category; its row, or null with the reference's notice. @return array<string, mixed>|null @return array<string, mixed>|null |
| static | find(string $name): ?array | One ability, or null with the reference's notice. @return array<string, mixed>|null @return array<string, mixed>|null |
| static | ability(string $name): ?array | One ability, or null, asked after without a notice. @return array<string, mixed>|null @return array<string, mixed>|null |
| static | findCategory(string $slug): ?array | One category, or null with the reference's notice. @return array<string, mixed>|null @return array<string, mixed>|null |
| static | category(string $slug): ?array | One category, or null, asked after without a notice. @return array<string, mixed>|null @return array<string, mixed>|null |
| static | all(): array | Every ability, by name. @return array<string, array> @return array<string, array> |
| static | allCategories(): array | Every category, by slug. @return array<string, array> @return array<string, array> |
| static | abilityRefusal(array $args): ?string | Why an ability's properties are refused, as WP_Ability::prepare_properties says it, or null. @param array<string, mixed> $args @param array<string, mixed> $args |
| static | categoryRefusal(array $args): ?string | Why a category's properties are refused, as WP_Ability_Category::prepare_properties says it, or null. @param array<string, mixed> $args @param array<string, mixed> $args |
| static | meta(array $meta): array | An ability's meta with the reference's defaults: the three annotations first, show_in_rest (public's value when unset), then public. @return array<string, mixed> |
| inside | state(), save(), forget() |
AbilityRun final class
public/minn/src/Minn/Runtime/AbilityRun.php · 28 lines · Open the class →
The checks of an ability's run, as the reference makes them (probes abilities-registry, abilities-classes); WP_Ability::execute strings them: input left out takes the input schema's default, input given to an ability without an input schema is refused, input that its schema refuses is ability_invalid_input; a permission callback that does not answer true is ability_invalid_permissions (an error it returns is reported as a notice); then wp_before_execute_ability, the callback (an error it returns is the answer), the output checked against the output schema (ability_invalid_output), and wp_after_execute_ability.
| static | input(string $name, array $schema, mixed $input): WP_Error|true | True, or why the input does not fit. |
| static | allowed(string $name, mixed $answer): WP_Error|true | True, or the error a permission answer other than true becomes (an error it returns is reported as a notice). |
| static | output(string $name, array $schema, mixed $output): WP_Error|true | True, or why the output does not fit the output schema. |
AccountFlows final class
public/minn/src/Minn/Runtime/AccountFlows.php · 140 lines · Open the class →
The account functions a sign-in page and plugins call, with the reference's checks, words, hooks and mail (probe account-flows): asking for a password reset (retrieve_password), resetting one (reset_password), registering (register_new_user), and telling the site and the new user about a registration (wp_new_user_notification).
| static | retrievePassword(string $login): WP_Error|bool | A reset link mailed to the account a login or email names: true, or the refusal (no name, no such account, a plugin's error, reset not allowed, or the mail failing). |
| static | resetPassword(WP_User $user, string $password): void | A new password for a user, announced before (password_reset) and after (after_password_reset). |
| static | registerNewUser(string $login, string $email): WP_Error|int | A new account for a login and an email, with a generated password and the nag to change it: its id, or every refusal at once (the name empty, illegal, taken or barred; the email empty, malformed or taken; whatever register_post and registration_errors add). |
| static | newUserNotification(int $userId, mixed $deprecated, string $notify): void | A new account announced: to the site's address (unless only the user is told), then to the user with a link to set their password (unless only the site is, or the old call shape asks for nobody). |
| inside | resetLink() |
AjaxController final readonly class
public/minn/src/Minn/Runtime/AjaxController.php · 80 lines · Open the class →
admin-ajax.php, the endpoint plugins post their front-end work to: a form submitted without a reload, a spam check, a background job. The action a request names runs the handlers registered as wp_ajax_{action} for a signed-in visitor and wp_ajax_nopriv_{action} for anyone else, answered as the reference answers them (contracts/runtime.md "admin-ajax.php").
It is an admin request: is_admin() is true while the plugins load and admin_init fires before the handler. A handler usually ends the request itself (wp_send_json, wp_die, exit), so the endpoint's headers go out before it runs; one that returns is followed by the reference's "0".
| const | PATH = '/wp-admin/admin-ajax.php' | |
| static | claims(Minn\Http\Request $request): bool | Whether a request is for the endpoint, which the runtime boots as an admin request. |
| method | dispatch(Minn\Http\Request $request): Minn\Http\Response* /wp-admin/admin-ajax.php (public) | Runs the handlers registered for the action the request names. |
| inside | action(), allowedOrigin(), registerCore() |
AllowedOptions final class
public/minn/src/Minn/Runtime/AllowedOptions.php · 26 lines · Open the class →
The settings-page allowlist plugins extend: option group => the option names a settings form in that group may save. The engine renders no settings pages, so the list is only recorded, never enforced.
| static | merge(array $add, array $into): array | The allowlist with each new name appended to its group once, groups created as needed. A group handed as anything but a list adds nothing. @param array<string, mixed> $add@param array<string, list<string>> $into@return array<string, list<string>> |
ApplicationPasswordEvents final class
public/minn/src/Minn/Runtime/ApplicationPasswordEvents.php · 46 lines · Open the class →
Application password changes made over REST, as the reference makes them (probe rest-application-password-hooks): the prepared item (the name, and an app id a new one is given) through rest_pre_insert_application_password, then WP_Application_Passwords (which tells plugins through wp_create_, wp_update_ and wp_delete_application_password), then rest_after_insert_application_password.
| static | create(int $userId, array $body, WP_REST_Request $request): WP_Error|array | A new password: its record and its plain text in groups of four, or the reference's refusal. @param array<string, mixed> $body@return array{0: array<string, mixed>, 1: string}|\WP_Error |
| static | update(int $userId, string $uuid, array $body, WP_REST_Request $request): WP_Error|array | A password renamed (when the body names it): its record as it stands. @param array<string, mixed> $body@return array<string, mixed>|\WP_Error |
| inside | prepared() |
ApplicationPasswordSignIn final class
public/minn/src/Minn/Runtime/ApplicationPasswordSignIn.php · 85 lines · Open the class →
wp_authenticate_application_password as the reference answers it (probe application-passwords-api): a user found already stands; outside an API request (application_password_is_api_request) nothing is tried; then the user by login or email, the feature's availability (for the site, for the user), and each of the user's passwords, a match passing through wp_authenticate_application_password_errors, recorded, and announced (application_password_did_authenticate); every refusal announced through application_password_failed_authentication.
| static | authenticate(mixed $input, string $username, string $password): mixed | The signed-in user, the input unchanged, or the refusal. |
| static | validate(mixed $input): mixed | determine_current_user's application password step: an earlier answer stands; else the user the REST request's application password signed in (the engine checked the password before plugins loaded), heard again as the reference hears it: the feature's availability, wp_authenticate_application_password_errors, then application_password_did_authenticate; a refusal is announced and leaves the request signed out. |
| inside | refusal() |
ArchiveLinks final class
public/minn/src/Minn/Runtime/ArchiveLinks.php · 41 lines · Open the class →
A post type's archive address and its feed, as the reference gives them (probe archive-links). The posts' archive is the posts page when the front is a page with one, else the bare home; a type with an archive lives under its named archive or its rewrite slug (after the structure's front unless it declines it), or at ?post_type= without rewrites or pretty permalinks; any other type has none (false, no filter). Its feed is the archive's /feed/ (/feed/{type}/ for one not the default) when the type's rewrite takes feeds, else ?feed= with the type spelled out.
| static | archive(string $postType): string|false | get_post_type_archive_link. |
| static | feed(string $postType, string $feed): string|false | get_post_type_archive_feed_link. |
AssetEdits final class
public/minn/src/Minn/Runtime/AssetEdits.php · 33 lines · Open the class →
What a plugin did to $wp_scripts->registered or $wp_styles->registered directly, as wp_default_scripts callbacks do (a dependency dropped, a source swapped, an entry unset or added), handed to the registry: each handle as the view showed it last against the view as the plugin left it.
| static | apply(Minn\Runtime\Assets $assets, array $edited, array $synced): void | The edits applied. The view is rebuilt by every change the registry takes, so the edited view is read from the copy handed in. @param array<string, mixed> $edited the view as the plugin left it@param array<string, array{0: mixed, 1: list<string>, 2: mixed, 3: mixed, 4: mixed}> $synced each handle as the view last showed it (source, dependencies, version, args, data) |
Assets final class
public/minn/src/Minn/Runtime/Assets.php · 432 lines · Open the class →
The registry behind wp_register_/wp_enqueue_ for scripts and styles: handles, sources, dependencies, versions, inline additions, localized data, and the head/footer split. Printing follows the reference's tag shapes as far as captured; see contracts/runtime.md.
| new | __construct(string $kind) | |
| method | watch(Closure $listener, ?Closure $before = NULL): void | A listener called after every change, so a plugin-facing view can stay current; and one called before a script's data changes, so what a plugin set in that view lands first. |
| method | setQueue(array $queue): void | The queue as a plugin left it after editing the view directly. |
| method | register(string $handle, string|false $src, array $deps, string|bool|null $ver, mixed $extra): bool | Registers an asset under a handle. |
| method | revise(string $handle, string|false $src, array $deps, string|bool|null $ver, mixed $extra): bool | An existing handle's source, dependencies, version and arguments replaced, its data, inline code and place in the queue kept. |
| method | externalHosts(string $ownHost): array | The hosts the queued assets load from, other than the site's own. @return list<string> hosts of enqueued sources (dependencies first) away from the given host, for dns-prefetch hints |
| method | deregister(string $handle): void | Forgets an asset. |
| method | enqueue(string $handle): void | Queues an asset for printing (probe placeholders-admin). One that is neither registered nor among the reference's own handles waits, and joins the queue when it is registered; one of the reference's own the engine ships no file for is queued as the reference would queue it. |
| method | dequeue(string $handle): void | Removes an asset from the queue, or from the handles waiting for registration. |
| method | registered(string $handle): bool | Whether a handle is registered, by the site or as one the reference registers itself (data/default-assets.json). |
| method | dependencies(string $handle): array | A handle's dependencies: as registered, or as the reference registers it. @return list<string> @return list<string> |
| method | dependents(string $handle): array | The registered handles that name a handle among their dependencies. @return list<string> @return list<string> |
| method | reaches(array $from, string $handle): bool | Whether a handle is a dependency, however deep, of any of the given handles. @param list<string> $from |
| method | enqueued(string $handle): bool | Queued directly, or pulled in as the dependency of something queued, however deep. The reference answers the same way, and plugin code leans on it: WooCommerce only attaches its settings blob when it finds `wc-settings` "enqueued", and nothing queues that handle by name, it only ever rides in as a dependency. |
| method | doneList(): array | The handles printed so far. @return list<string> |
| method | setDone(array $done): void | The printed handles as a plugin left them after editing the view directly. |
| method | done(string $handle): bool | Whether a handle has been printed. |
| method | addInline(string $handle, string $code, string $position): bool | Attaches inline code to an asset. |
| method | addData(string $handle, string $key, mixed $value): bool | Attaches a data key to an asset. |
| method | setTranslations(string $handle, string $domain, string $path): bool | Names the text domain and folder a script's translations come from, and makes the script depend on wp-i18n. |
| method | data(string $handle, string $key): mixed | A data key of an asset, or false. |
| method | localize(string $handle, string $name, array $data): bool | Attaches a localized object to a script. |
| method | toPrint(): array | Every queued handle not yet printed, dependencies first. @return list<string> @return list<string> |
| method | withPath(): array | Enqueued handles that name a file on disk, mapped to that path. A style registered with a `path` datum is saying it can be inlined; whether it is small enough to be worth inlining is the caller's decision. @return array<string, string> handle => path |
| method | unsource(string $handle): void | Drops a handle's source so it prints as markup rather than a link. |
| method | markDone(string $handle): void | Records a handle as printed. |
| method | item(string $handle): ?array | One registered asset, or null. |
| method | items(): array | Every registered asset. @return array<string, array<string, mixed>> |
| method | queue(): array | The handles queued. @return list<string> |
| method | kind(): string | Whether these are scripts or styles. |
| inside | settle(), changed(), defaults(), held(), ordered() |
Avatar final class
public/minn/src/Minn/Runtime/Avatar.php · 62 lines · Open the class →
Avatars the way get_avatar_data and get_avatar decide them: the argument defaults, the email an id, user, post, or comment names, the Gravatar URL, and the <img> attributes. Behaviour pinned by contracts/fixtures/api/functions.json.
| static | dataArgs(array $args): array | The data arguments normalised: sizes to integers, the default token, the rating lowercased. |
| static | hash(string $email): string | The Gravatar hash an email yields; '' for no email. |
| static | hashFromAddress(string $address): ?string | A hash given as an md5.gravatar.com address, else null. |
| static | urlArgs(array $args): array | The Gravatar URL's query arguments for the data arguments. @return array<string, string|int|false> @return array<string, string|int|false> |
| static | classes(int $size, bool $isDefault, mixed $extra): array | The <img> classes: the size class, a default marker, the caller's own. @return list<string> @return list<string> |
| static | extraAttributes(string $extra, mixed $loading, mixed $decoding): string | The extra attribute string with loading and decoding added when the caller did not set them. |
BlockFilters final class
public/minn/src/Minn/Runtime/BlockFilters.php · 127 lines · Open the class →
The block-level filters plugin code hooks (pre_render_block, render_block_data, render_block, render_block_{name}), applied around the engine's own renderer so a plugin sees every block the page renders, not only the ones it registered.
| const | NATIVE_DATA_DONE = array ( 'wp_render_elements_support_styles' => 10, 'wp_render_block_style_variation_support_styles' => 10, 'block_core_gallery_data_id_backcompatibility' => 10, ) | The data filters the engine's renderer does itself for a block it renders natively: element styles, style variations, a gallery's image ids. |
| const | NATIVE_RENDER_DONE = array ( 'wp_render_elements_class_name' => 10, 'wp_render_block_style_variation_class_name' => 10, 'wp_render_layout_support_flag' => array ( 0 => 10, 1 => '_minn_render_child_layout_support', ), 'block_core_paragraph_add_class' => 10, ) | The render filters it does itself: the element and style variation classes, the layout's container classes (a child's own layout left to a stand-in), the paragraph's class. |
| static | active(): bool | Whether any block filter is registered. |
| static | toArray(Minn\Blocks\Block $block): array | A block as the parsed array plugins receive. @return array<string, mixed> the parsed-array shape plugin code reads |
| static | fromArray(array $parsed): Minn\Blocks\Block | A block from the parsed array plugins hand back. |
| static | data(array $parsed, ?object $parent, array $done = array ( )): mixed | A parsed block through render_block_data, without the callbacks done already (NATIVE_DATA_DONE for a block the engine's renderer renders). @param array<string, mixed> $parsed@param array<string, int|array{0: int, 1: string}> $done |
| static | rendered(string $html, array $parsed, ?object $instance, array $done = array ( )): string | A rendered block through render_block and its per-name filter, without the callbacks done already (NATIVE_RENDER_DONE, with its stand-ins, for a block the engine's renderer renders). @param array<string, mixed> $parsed@param array<string, int|array{0: int, 1: string}> $done |
| static | before(Minn\Blocks\Block $block, array $done = array ( )): Minn\Blocks\Block|string | A short-circuit from pre_render_block, or the block as render_block_data left it. @param array<string, int|array{0: int, 1: string}> $done the data filters done already |
| static | after(Minn\Blocks\Block $block, string $html, array $done = array ( )): string | A rendered block through render_block and its per-name filter. @param array<string, int|array{0: int, 1: string}> $done the render filters done already |
| inside | context() |
BlockHooks final class
public/minn/src/Minn/Runtime/BlockHooks.php · 69 lines · Open the class →
The Block Hooks API on the engine's own front end: a plugin asks for its block to be inserted next to an anchor block in a template part or a pattern (WooCommerce puts the mini-cart after the navigation in a header), and the markup the renderer reads carries those insertions.
The facade owns the traversal (wp-api/blocks.php); this is the seam the theme blocks call, and it stays inert until a plugin actually hooks something, so a site without such a plugin parses nothing extra.
| static | active(): bool | Whether block hooks are registered (the runtime's functions are there only once it is up). |
| static | forPart(string $markup, string $slug, string $area): string | A template part's markup with its hooked blocks inserted. The context is the part itself: a plugin reads its area to tell a header from a footer, and its content to see whether its block is already there. |
| static | registeredPattern(string $slug): ?string | A plugin-registered pattern's content, hooks already applied by the registry, for a slug the theme does not carry. Null when the runtime is not up or nothing registered that name. |
| static | forPattern(string $markup, string $slug, array $blockTypes, array $categories): string | A theme pattern's markup with its hooked blocks inserted, with the pattern array as the context (blockTypes and categories are how a plugin recognises a header pattern). @param list<string> $blockTypes@param list<string> $categories |
BlockMetadata final class
public/minn/src/Minn/Runtime/BlockMetadata.php · 95 lines · Open the class →
block.json to the settings a block type registers with: the property renames, the script and style handles (registered through the closures, one per entry), the view script modules, the block hooks positions, and the render template as a callback. Behaviour pinned by contracts/fixtures/api/blocks.json.
| static | settings(array $metadata, Closure $scriptHandle, Closure $styleHandle, Closure $moduleId, Closure $render): array | Block settings from a block.json, the way the reference maps its properties. @param Closure(array, string, int): (string|false) $scriptHandle registers one script entry, answering its handle@param Closure(array, string, int): (string|false) $styleHandle registers one style entry@param Closure(array, string, int): (string|false) $moduleId registers one view script module entry@param Closure(string): ?Closure $render the render callback for a template path, or null when the file is missing@return array<string, mixed> |
| static | assetHandle(string $block, string $field, int $index = 0): string | The script or style handle a block.json field registers under; core blocks keep the `wp-block-` spelling. |
| inside | handles() |
BlockTemplates final class
public/minn/src/Minn/Runtime/BlockTemplates.php · 67 lines · Open the class →
Block templates plugins register at runtime, by their namespaced name ("plugin//slug"). A theme file or a saved template of the same slug wins; otherwise the registered content renders for that slug.
| method | register(string $name, array $args): array|string | The registered row, or the refusal code the reference reports. |
| method | unregister(string $name): ?array | Forgets a registered template; the row, or null. |
| method | all(): array | Every registered template. @return array<string, array<string, mixed>> by registered name |
| method | get(string $name): ?array | One registered template, or null. |
| method | bySlug(string $slug): ?array | A registered template by slug, or null. |
BlockWidget final class
public/minn/src/Minn/Runtime/BlockWidget.php · 30 lines · Open the class →
A block widget's legacy class name. Every widget the block editor saves is one block of markup, and a classic theme styles it by the widget class the equivalent legacy widget used, so the wrapper carries a second class named after the FIRST block in the content. A block with no legacy equivalent adds nothing.
| const | BASE_CLASS = 'widget_block' | |
| static | classNameFor(array $blocks): string | The legacy widget class a block widget maps to. @param list<array<string, mixed>> $blocks the parsed content |
BrowserGlobals final class
public/minn/src/Minn/Runtime/BrowserGlobals.php · 45 lines · Open the class →
The browser and web server a request names, as the reference's globals have them from boot ($is_chrome, $is_safari, $is_IE, $is_apache, $is_nginx, …; probe vars-matrix): one browser at most, read from the user agent in the reference's order (Lynx, Edge, Opera, Chrome, Safari, Internet Explorer on a Mac then elsewhere, Gecko, Netscape 4), an iPhone being a mobile Safari; the server from SERVER_SOFTWARE, IIS 7 and up apart. Chrome Frame's switch is the facade's (use_google_chrome_frame).
| const | NAMES = array ( 0 => 'is_lynx', 1 => 'is_gecko', 2 => 'is_winIE', 3 => 'is_macIE', 4 => 'is_opera', 5 => 'is_NS4', 6 => 'is_safari', 7 => 'is_chrome', 8 => 'is_iphone', 9 => 'is_IE', 10 => 'is_edge', 11 => 'is_apache', 12 => 'is_IIS', 13 => 'is_iis7', 14 => 'is_nginx', 15 => 'is_caddy', ) | The globals, in the order the reference sets them. |
| static | detect(string $agent, string $software): array | Each global's value for this user agent and server software. @return array<string, bool> |
| inside | browser() |
CommentCloser final readonly class
public/minn/src/Minn/Runtime/CommentCloser.php · 30 lines · Open the class →
The Discussion setting that closes comments on old posts. Observed on the reference: only the "post" type closes (the types filter changes nothing), status is not consulted, the age is whole days on post_date_gmt and must exceed the setting (a fifteen-day-old post stays open at fifteen), and zero days switches the rule off.
| new | __construct(bool $enabled, int $days) | |
| method | open(bool $open, Minn\Content\PostRecord|array|null $post, int $now): bool | Whether comments stay open on a post under the close-after-days setting. |
| method | closes(string $postType, string $postDateGmt, array $types, int $now): bool | Whether the setting closes a post of $postType published at $postDateGmt: the type is one of $types, the setting is on, and the post is older than the days allowed. The posts a single query hands over (_close_comments_for_old_posts) take their types from the close_comments_for_post_types filter, which is honoured there. @param list<string> $types |
CommentEvents final readonly class
public/minn/src/Minn/Runtime/CommentEvents.php · 254 lines · Open the class →
What the reference's REST comments controller tells plugins, for the engine's own: with plugins loaded every change goes through the runtime's comment functions (wp_insert_comment, wp_update_comment, wp_set_comment_status, wp_trash_comment, wp_delete_comment), which fire the reference's actions in its order, and the REST actions follow. Without a booted runtime the rows are written as before and nothing is told.
| new | __construct(Minn\Content\Comments $comments) | |
| method | live(): bool | Whether plugins are loaded to be told anything. |
| method | allow(string $address, string $email, string $dateGmt): void | The check a new comment passes on the reference before it is written: check_comment_flood, with the address, the email and the GMT date. |
| method | restCreate(array $prepared, Minn\Http\Request $request): int | A comment created over REST with plugins loaded, as the reference's controller creates one (probe rest-comment-save): the request's fields through rest_preprocess_comment, the signed-in author filled in, the content check (allow_empty_comment), the date, the length check, wp_allow_comment (a duplicate is a 409, a flood a 400), rest_pre_insert_comment, then wp_insert_comment over wp_filter_comment; a refusal is its REST error. @param array<string, mixed> $prepared the request's fields as the controller prepares them |
| method | restUpdate(Minn\Content\CommentRecord $comment, array $prepared, ?string $status, Minn\Http\Request $request): void | A comment changed over REST with plugins loaded: the request's fields through rest_preprocess_comment, the content and length checks, wp_update_comment, then the status, as the reference's controller changes one. @param array<string, mixed> $prepared |
| static | changeStatus(int $id, string $asked): void | A REST status asked for, as the reference's controller changes it: nothing when it is already so; approve and hold through wp_set_comment_status with that word, spam, unspam, trash and untrash through their own functions. |
| method | insert(array $columns): int | Writes a new comment and returns its id; an approved one is counted on its post. @param array<string, mixed> $columns @param array<string, mixed> $columns |
| method | update(Minn\Content\CommentRecord $comment, array $columns, ?string $status): void | An edit through REST: the fields, as wp_update_comment writes them (which tells plugins even when nothing changed), then the status through wp_set_comment_status when it moves. @param array<string, string> $columns |
| method | trash(Minn\Content\CommentRecord $comment): void | Moves a comment to the trash, keeping its status and the time for the way back. |
| method | delete(Minn\Content\CommentRecord $comment): void | Removes a comment for good. |
| method | restSaved(int $id, Minn\Http\Request $request, ?Minn\Content\CommentRecord $before): void | rest_insert_comment, then rest_after_insert_comment, with the comment as it stands and the request; no $before is a new comment. |
| method | restDeleted(Minn\Content\CommentRecord $comment, array $data, Minn\Http\Request $request): void | rest_delete_comment, after a trash or a delete, with the comment as it was and the response. @param array<string, mixed> $data @param array<string, mixed> $data |
| inside | preprocessed(), requireContent(), field(), requireLengths(), refusal() |
CommentFeedQuery final class
public/minn/src/Minn/Runtime/CommentFeedQuery.php · 50 lines · Open the class →
The comments a comments feed's main query carries, as the reference finds them (probe comment-feed-query). For a listing (the site's, an archive's, a search's) they come before its posts, which are then narrowed to the posts those comments are on; for a single post they come after it. Either way the comment_feed_* filters shape the query (handed the WP_Query), only approved comments that are not notes count, newest first, as many as a feed carries.
| static | listing(WP_Query $query, Minn\Runtime\PostQueryParts $parts, object $wpdb): void | A listing's comments, then its posts narrowed to theirs: the posts query's join and where as they stand after posts_join. |
| static | single(WP_Query $query, object $wpdb): void | A single post's comments, once the query has found the post. |
| inside | request(), keep() |
CommentForm final class
public/minn/src/Minn/Runtime/CommentForm.php · 107 lines · Open the class →
The comment form's submission with plugins loaded (wp_handle_comment_submission), in the order the reference runs it (contracts/runtime.md "The comment form"): a reply to a comment still held is refused; the post is looked at, each refusal with its action (comment_id_not_found, comment_closed, comment_on_trash, comment_on_draft, comment_on_password_protected) and pre_comment_on_post when it takes comments; the signed-in user's own name and addresses replace the form's, or a site that requires sign-in refuses; the required fields, an empty comment (allow_empty_comment) and the column lengths are checked; then wp_new_comment stores it, where preprocess_comment, the duplicate and flood checks and pre_comment_approved have their say. A refusal is a WP_Error whose data is the status the form answers with; none means a blank page.
| static | submit(array $form): WP_Comment|WP_Error | The stored comment, or the refusal the form answers with. @param array<string, mixed> $form the posted fields, unslashed |
| inside | store(), postRefusal(), fieldRefusal() |
CommentPages final class
public/minn/src/Minn/Runtime/CommentPages.php · 115 lines · Open the class →
Which page of a post's comments a comment falls on, and the link that opens it there, as the reference works them out (probe comment-pages): the page size from the arguments, the comments_per_page query variable or the option (no pages at all when comments are not paged); a reply takes its top-level comment's page while comments are threaded; a page is the count of older top-level comments over the page size. The default page loses its number when the oldest comments come first.
| static | pageOf(mixed $commentId, array $args): ?int | The page a comment is on, through get_page_of_comment (handed the arguments as settled and as given); a threaded reply answers with its top-level comment's page. Null for a comment that is not there. @param array<string, mixed> $args |
| static | linkPage(?WP_Comment $comment, array $args): mixed | The page a comment's link names (before get_comment_link): the one asked for, the loop's, or the comment's own; '' for the default page when the oldest comments come first. @param array<string, mixed> $args |
| static | shown(mixed $cpage): mixed | The comments page being shown (the cpage query variable) as the list's links and reply links name it: '' for the default page when the oldest comments come first. |
| static | link(string $permalink, mixed $page): string | A post's link opened at a page of its comments, as pretty or plain links write it. |
| inside | olderQuery() |
CommentQuery final readonly class
public/minn/src/Minn/Runtime/CommentQuery.php · 28 lines · Open the class →
The approval breakdown wp_count_comments reports (comment lists run through WP_Comment_Query and Minn\Runtime\CommentQueryRunner).
| new | __construct(Minn\Db $db) | |
| method | breakdown(int $postId): array | The counts wp_count_comments reports, for one post or the site. @return array<string, int> @return array<string, int> |
CommentQueryRunner final class
public/minn/src/Minn/Runtime/CommentQueryRunner.php · 131 lines · Open the class →
WP_Comment_Query as the reference runs it (probe wp-comment-query-sql): the variables filled and handed to pre_get_comments, the meta query's SQL, comments_pre_query (which may answer), the clauses (the date query among them) handed to comments_clauses, the request, found_comments_query when a limited query counts what it found, then the ids, the count, or the comments through the_comments, threaded or flattened when asked. Results are not cached between queries (the reference keeps them in the object cache by last_changed).
| new | __construct(object $wpdb) | |
| method | run(WP_Comment_Query $query): mixed | Runs the query, as WP_Comment_Query::get_comments() does: a count, ids, or comments. |
| method | ids(WP_Comment_Query $query, array $meta): array|int | The comment IDs for the query's vars (get_comment_ids), or the count when it counts; the request is written, and the found rows read when the request counts them and found some. @param array{join?: string, where?: string} $meta@return list<int>|int |
| inside | request(), comments() |
CommentQueryWhere final class
public/minn/src/Minn/Runtime/CommentQueryWhere.php · 196 lines · Open the class →
WP_Comment_Query's WHERE pieces and the posts join, in the reference's order (probe wp-comment-query-sql): the approval statuses (with the readers whose held comments show), the post, the id lists, the author's email and url, karma, the types (notes left out unless asked for), the parent (0 for a threaded or flat query that names none), the user, the search, the post's own fields, the author lists, then the meta and date queries.
| new | __construct(object $wpdb) | |
| method | pieces(WP_Comment_Query $query, array $meta): array | The WHERE pieces (joined with AND by the caller) and the posts join ('' when no post field is asked after). @param array{join?: string, where?: string} $meta the meta query's SQL, when it has clauses@return array{0: list<string>, 1: string} |
| static | searchSql(string $search, array $columns): string | A LIKE test over the columns (get_search_sql), as an AND clause. @param list<string> $columns @param list<string> $columns |
| inside | approved(), post(), types(), parentAndUser(), search(), posts(), given(), listOf(), bare() |
CommentThreads final class
public/minn/src/Minn/Runtime/CommentThreads.php · 68 lines · Open the class →
A threaded or flat comment query's descendants as the reference fills them (probe wp-comment-query-sql): one more comment query a level, with the query's own variables but the level's parents, no parent, no limit and no count; a threaded query hangs each reply under its parent, marks every comment's children as known and keeps the top level by id, a flat one lists the top level and then each level in its query's order.
| method | fill(WP_Comment_Query $query, array $top): array | The query's top-level comments with their descendants filled in. @param list<\WP_Comment> $top@return array<int, \WP_Comment> |
| static | flatten(array $children, array $args): array | A comment's children, then each one's own, depth first, as a list. @param array<array-key, mixed> $children@param array<string, mixed> $args get_children()'s arguments, handed down@return list<\WP_Comment> |
Connectors final class
public/minn/src/Minn/Runtime/Connectors.php · 221 lines · Open the class →
The connectors registry: the external services a site talks to (AI providers, spam filters, cloud services) and how each authenticates. Rows are normalised on the way in, the way the reference keeps them; the facade class hands them back to plugin code.
| method | register(string $id, array $args): ?Minn\Runtime\Refusal | Registers a connector, or the refusal. |
| method | unregister(string $id): ?array | Forgets a connector; its row, or null. @return array<string, mixed>|null the row that was registered, null when there was none |
| method | all(): array | Every connector. @return array<string, array<string, mixed>> |
| method | get(string $id): ?array | One connector, or null. |
| method | has(string $id): bool | Whether a connector is registered. |
| method | registerAiProviders(Closure $loaded): void | The reference's three AI providers. Whether a provider's plugin is active is asked of the caller's closure, by the connector's id, when asked (probe connector-defaults). @param Closure(string): bool $loaded |
| method | registerAkismet(Closure $loaded): void | Akismet, the spam filter connector the reference adds after the AI providers. Whether its plugin is active is asked of the caller's closure. @param Closure(): bool $loaded |
| static | mask(string $key): string | Keys of four characters or fewer are shown whole; longer ones keep their last four behind at most sixteen bullets. |
| static | keySource(string $setting, string $envVar, string $constant, Closure $option): string | Where a key comes from, in the reference's precedence: the environment, then a constant, then the stored option; empty values do not count. @param Closure(string): mixed $option |
| static | parseCredentials(string $value): array | "user:password" split at the first colon, both halves trimmed; anything else is empty credentials. |
| static | sanitizeCredentials(mixed $value, Closure $clean): array | Stored credentials are an array of two text fields; a string, even a "user:password" one, is not accepted from storage. @param Closure(string): string $clean |
| static | credentials(array $auth, Closure $option, Closure $clean): array | The credentials a connector authenticates with, from the same three sources as a key, plus where they came from. @param Closure(string): mixed $option@param Closure(string): string $clean |
| inside | normalise() |
Constants final class
public/minn/src/Minn/Runtime/Constants.php · 91 lines · Open the class →
The constants plugin code expects: the fixed set from data/constants.json (captured from the reference) and the per-site ones computed here. Nothing already defined is touched, so wp-config.php keeps the last word.
| static | define(Minn\Runtime\Runtime $runtime): void | Defines the constants the reference defines at boot. |
| inside | maxMemoryLimit() |
CronTable final class
public/minn/src/Minn/Runtime/CronTable.php · 133 lines · Open the class →
The cron option's shape, operated on as data: timestamp => hook => key => entry, kept in natural timestamp order. The key is the reference's own (a hash of the serialized argument list), so both stacks read one table.
| static | key(array $args): string | The key an event's arguments hash to. @param array<int, array<string, array<string, array<string, mixed>>>> $crons |
| static | fromBlob(?string $blob): array | The table read from the option's stored blob: the version marker dropped, timestamps in order, an unreadable or absent blob empty. @return array<int, array<string, array<string, array<string, mixed>>>> |
| static | hasNear(array $crons, int $timestamp, string $hook, string $key, int $window): bool | Whether the same hook and arguments are already scheduled within the window around the timestamp. |
| static | insert(array $crons, int $timestamp, string $hook, string $key, array $entry): array | The table with an event added at a timestamp. |
| static | remove(array $crons, int $timestamp, string $hook, string $key): array | The table with one event removed. |
| static | removeHook(array $crons, string $hook): array | The table with every event of a hook removed. @return array{0: array, 1: int} the table without the hook, and how many entries went |
| static | timestampsFor(array $crons, string $hook, string $key): array | When a hook and key are scheduled. @return list<int> every timestamp the hook and arguments are scheduled at |
| static | find(array $crons, string $hook, string $key, ?int $timestamp): ?array | The entry for the hook and arguments at a timestamp, or at the next one when no timestamp is given, as [timestamp, entry]. |
| static | nextRun(int $timestamp, int $interval, int $now): int | The next run of a recurring event: one interval from now, aligned to the original timestamp when it is in the past. |
| static | due(array $crons, int $now): array | The timestamps at or before now, in order. |
CurrentUser final class
public/minn/src/Minn/Runtime/CurrentUser.php · 13 lines · Open the class →
Who the request is, as the reference settles it before init: the user determine_current_user names (the engine's own session, through wp_validate_auth_cookie, when no plugin hooks it), which the request's reader then follows. A plugin that signs in by its own token or cookie (JWT, OAuth, single sign-on) is heard; one that names nobody signs the request out.
| static | settle(Minn\Runtime\Runtime $runtime): int | Settles the request's user and reader; returns the user's id (0 for nobody). |
DbDelta final readonly class
public/minn/src/Minn/Runtime/DbDelta.php · 125 lines · Open the class →
dbDelta as the reference does it: a CREATE TABLE statement creates the table when it is missing, otherwise the columns and keys the statement has and the table lacks are added. Nothing is ever dropped or altered.
| new | __construct(Minn\Db $db) | |
| static | creates(array $queries): array | The CREATE TABLE statements in a batch, keyed by table name. @param list<string> $queries @return array<string, string> @param list<string> $queries @return array<string, string> |
| method | apply(array $creates, bool $execute): array | Applies CREATE TABLE statements as dbDelta does; the statements run. @param array<string, string> $creates @return array<string, string> what was (or would be) done, by table or table.column |
| method | tables(): array | Every table in the database. @return list<string> |
| method | columns(string $table): array | A table's columns with their definitions. @return array<string, array<string, mixed>> by column name |
| method | indexNames(string $table): array | A table's index names. @return list<string> lowercase key names |
| method | run(string $ddl): void | Runs one DDL statement. |
| inside | definitions() |
DebugMode final class
public/minn/src/Minn/Runtime/DebugMode.php · 104 lines · Open the class →
PHP's error settings from the debug constants, as the reference's boot sets them (wp_debug_mode(); contracts/fixtures/debug-mode.json holds the oracle's answer for each combination, and the unit suite replays it). WP_DEBUG reports every level, or only errors and warnings. With it on, WP_DEBUG_DISPLAY turns display on or off (null leaves PHP's own setting) and WP_DEBUG_LOG sends the log to a file. A request that answers a machine never displays errors.
| const | QUIET = 4983 | What PHP reports while WP_DEBUG is off: errors and warnings, no notices, strict or deprecation messages. |
| static | settings(array $constants, string $contentDir): array | The settings the debug constants ask for: the error level, and the ini values to set (none for what they leave alone). @param array<string, mixed> $constants the debug constants defined, by name; a missing one reads as its default@return array{level: int, ini: array<string, string>} |
| static | logPath(mixed $setting, string $contentDir): ?string | Where WP_DEBUG_LOG sends the log. True, 1 or "true" in any case mean debug.log in the content folder; any other string but "" and "0" is the path itself, "false" included; anything else keeps no log. |
| static | answersMachine(array $constants, array $server): bool | Whether what the boot knows marks a request that answers a machine, whose errors must never print into the answer: XML-RPC, REST or ms-files declared at all, an install or ajax declared true, or a request that asks for or sends JSON (a JSONP or XML one is not). The facade's wp_debug_mode() asks the same through wp_installing(), wp_doing_ajax() and wp_is_json_request(), filters included. @param array<string, mixed> $constants the request constants defined, by name@param array<string, mixed> $server the request's server variables |
| static | defined(): array | The debug and request constants defined right now, by name. @return array<string, mixed> |
| static | apply(array $settings): void | Sets PHP's error level and ini values to what settings() asked for. @param array{level: int, ini: array<string, string>} $settings |
Deferrals final class
public/minn/src/Minn/Runtime/Deferrals.php · 66 lines · Open the class →
The switches an importer flips for the length of a request (probe plugin-helpers): term and comment counts deferred until the switch goes off again, when what was put off is counted at once, and cache additions suspended so a bulk read does not fill the cache. Each answers what it is now; a value that is not a boolean asks without changing it. Kept in the request's state, so a worker's next request starts with them off.
| static | terms(mixed $defer): bool | wp_defer_term_counting: on, off, or asked; off counts what was put off. |
| static | comments(mixed $defer): bool | wp_defer_comment_counting: on, off, or asked; off counts what was put off. |
| static | cacheAddition(mixed $suspend): bool | wp_suspend_cache_addition: on, off, or asked. |
| static | putOffTerms(array $terms, string $taxonomy): bool | Puts terms' recount off while counting is deferred; false when it is not (count now). @param list<int> $terms @param list<int> $terms |
| static | putOffComments(int $post): bool | Puts a post's comment recount off while counting is deferred; false when it is not (count now). |
| inside | flip() |
EarlyFilters final class
public/minn/src/Minn/Runtime/EarlyFilters.php · 20 lines · Open the class →
Filters that run before the runtime exists, over the hooks added that early: WP-CLI's add_wp_hook writes them into $wp_filter as plain arrays (tag => priority => id => function and accepted_args). The reference has its hook API by then; the engine reads the same arrays directly.
| static | apply(string $tag, mixed $value, mixed ...$args): mixed | The value through every early callback on the tag, lowest priority first; as given when there are none. |
FileTypeCheck final class
public/minn/src/Minn/Runtime/FileTypeCheck.php · 77 lines · Open the class →
A file's type from its content as much as its name, as the reference's wp_check_filetype_and_ext decides it (probe upload-filters): an image the server can measure is what its bytes say, and a name with the wrong image extension is corrected (a.png holding a JPEG becomes a.jpg); anything claiming to be such an image and not being one has no type; any other type must match what the content is, plain text standing for a few text types and an office or archive container for the document types; and the type must be one the site allows.
| const | IMAGES = array ( 'image/jpeg' => 'jpg', 'image/png' => 'png', 'image/gif' => 'gif', 'image/bmp' => 'bmp', 'image/tiff' => 'tif', 'image/webp' => 'webp', 'image/avif' => 'avif', 'image/heic' => 'heic', ) | The image types measured by their bytes, and the extension each takes. |
| static | check(string $file, string $filename, ?array $mimes, array $images): array | ext, type, proper_filename, and the content's own type (real_mime). @param array<string, string>|null $mimes@param array<string, string> $images getimagesize_mimes_to_exts@return array{ext: string|false, type: string|false, proper_filename: string|false, real_mime: string|false} |
| inside | imageMime(), contentFits() |
FileUpload final class
public/minn/src/Minn/Runtime/FileUpload.php · 105 lines · Open the class →
A file a plugin hands to wp_handle_upload or wp_handle_sideload, taken in the reference's order (probe upload-filters, contracts/runtime.md "Uploads"): the action's prefilter, where a plugin sanitizes the file or refuses it with an error; its overrides; the upload's own errors, an empty file and, for a form upload, the form and the upload itself; the type, from the file's content as much as its name (wp_check_filetype_and_ext), refused unless the site allows it or the user may upload anything; the uploads folder; a unique, clean name (wp_unique_filename); pre_move_uploaded_file, where a plugin may move it itself; the move; and wp_handle_upload over the result.
| static | handle(array $file, array|false $overrides, ?string $time, string $action): array | The stored file (file, url, type) or ['error' => message]. @param array<string, mixed> $file name, type, tmp_name, size, error@param array<string, mixed>|false $overrides@return array<string, mixed> |
| inside | refusal(), move() |
Heartbeat final class
public/minn/src/Minn/Runtime/Heartbeat.php · 30 lines · Open the class →
The heartbeat a signed-in page beats through admin-ajax.php, answered as the reference answers it (suite ajax): the heartbeat nonce checked, and when it is stale or wrong, wp_refresh_nonces asked for fresh ones (a wrong one ends the beat there, marked expired); otherwise what the page sent goes through heartbeat_received, the answer through heartbeat_send, and heartbeat_tick hears it before the server's time is added.
| static | answer(array $post): array | The answer for a beat, from the posted fields (unslashed). @param array<string, mixed> $post@return array<string, mixed> |
Hooks final class
public/minn/src/Minn/Runtime/Hooks.php · 354 lines · Open the class →
The hook registry plugin code registers into and the engine fires. Semantics come from contracts/fixtures/api/hooks.json: callbacks run by ascending priority then insertion order; a callback added at a higher priority during a run takes part in that run, one added at the current or a lower priority waits for the next; a callback removed before its turn is skipped; the "all" hook sees every firing with the hook name first and every argument regardless of its accepted count.
| method | onNew(Closure $observer): void | Called with each hook name the first time a callback registers under it. |
| method | storage(): array | The live registry, for a hook object to share by reference. |
| method | actionCounters(): array | How often each action, or each filter, has run, by hook. |
| method | filterCounters(): array | How often each filter has run, by hook, by reference for the facade's global. |
| method | stackRef(): array | The hooks running now, outermost first, by reference for the facade\'s globals. |
| method | currentPriority(string $hook): int|false | The priority a running hook is at, or false when it is idle. |
| method | add(string $hook, callable|array|string $callback, string|int $priority = 10, int $accepted = 1): bool | Adds a callback to a hook at a priority. |
| method | remove(string $hook, callable|array|string $callback, string|int $priority = 10): bool | Removes a callback from a hook. |
| method | removeAll(string $hook, string|int|false $priority = false): bool | Every callback of a hook, or only those at one priority. Always true, as observed. |
| method | has(string $hook, callable|array|string|false $callback = false): int|bool | With a callback: its lowest priority, or false; without: whether anything is registered. |
| method | hasBeyond(string $hook, array $done): bool | Whether anything is hooked besides the callbacks the engine does the work of itself (named function => priority), as filterWithout skips them. @param array<string, int> $done |
| method | filter(string $hook, array $args): mixed | Runs a filter and returns the value. @param list<mixed> $args the value first |
| method | filterWithout(string $hook, array $args, array $done): mixed | Runs a filter without the callbacks a caller has already done the work of, named function => priority: the engine renders post content through its own pipeline, then runs the_content for everything else hooked there. A callback a plugin removed is simply not there to skip. A skipped callback can name a stand-in, [priority, function], that runs in its place: what of its work the engine has left to do. @param list<mixed> $args the value first@param array<string, int|array{0: int, 1: string}> $done |
| method | action(string $hook, array $args): void | Runs an action. With no arguments its callbacks still get one, an empty string, as do_action() gives them in the reference (a callback that requires a parameter is not short of one). @param list<mixed> $args |
| method | actionRef(string $hook, array $args): void | Runs an action with exactly the arguments given, none included (do_action_ref_array). @param list<mixed> $args |
| method | actionRefArray(string $hook, array $args): void | do_action_ref_array: the action's callbacks get the arguments, and an 'all' callback gets them as the one array they were passed in, as the reference hands them on. @param list<mixed> $args |
| method | filterRefArray(string $hook, array $args): mixed | apply_filters_ref_array: as filter(), with an 'all' callback handed the arguments as one array. @param list<mixed> $args |
| method | actionsDone(string $hook): int | How often an action has run. |
| method | filtersDone(string $hook): int | How often a filter has run. |
| method | current(): string|false | The hook running now, or false. |
| method | doing(?string $hook): bool | Whether a hook, or any hook, is running. |
| method | registered(): array | Every hook with callbacks, by name. @return array<string, array<int, list<callable>>> a read-only view for diagnostics |
| method | callAll(array $args): void | The 'all' callbacks run with the arguments given, the hook's name first (_wp_call_all_hook). @param list<mixed> $args @param list<mixed> $args |
| inside | run(), nextPriority(), fireAll(), id() |
Interactivity final class
public/minn/src/Minn/Runtime/Interactivity.php · 534 lines · Open the class →
Server-side directive processing for the Interactivity API: the state and config stores, and the pass that resolves data-wp-bind, data-wp-class, data-wp-style, data-wp-text, and data-wp-each against state and context before the markup leaves the server. Behaviour pinned by the interactivity probe fixture.
| method | whenRouterRegion(Closure $callback): void | What to run when processing meets a router region. |
| method | state(?string $namespace, array $state = array ( )): array | Reads or extends a namespace's state. @return array<string, mixed> |
| method | allState(): array | Every namespace's state, for the client. @return array<string, array<string, mixed>> every namespace's state, for the client |
| method | allConfig(): array | Every namespace's config, for the client. @return array<string, array<string, mixed>> |
| method | config(string $namespace, array $config = array ( )): array | Reads or extends a namespace's config. @return array<string, mixed> |
| method | context(?string $namespace = NULL): array | The current directive context of a namespace. @return array<string, mixed> |
| method | element(): ?array | The element whose directives are being evaluated. @return array<string, mixed>|null |
| method | process(string $html): string | HTML with its directives resolved on the server. |
| inside | namespace(), tokenize(), walk(), applyDirectives(), bind(), interactiveNamespace(), pushContext(), evaluate(), expandEach(), markChildren(), attributeMap(), relative(), escape(), camel(), follow() |
Meta final readonly class
public/minn/src/Minn/Runtime/Meta.php · 242 lines · Open the class →
The four meta tables behind get_metadata and friends: reads by object, and the row-level writes the update and delete rules need.
| new | __construct(Minn\Db $db) | |
| static | knows(string $type): bool | Whether an object type has a meta table. |
| static | clausesFromQueryVars(array $queryVars): array | The meta query in flat query vars (meta_key, meta_value, the compare and type variants) as one clause, AND'd ahead of an explicit meta_query as its own group, as WP_Meta_Query::parse_query_vars reads them. An empty meta_value is no value. @param array<string, mixed> $queryVars@return array<array-key, mixed> |
| method | all(string $type, int $objectId): array | Every row of an object's meta, values as stored, grouped by key in id order. @return array<string, list<string>> @return array<string, list<string>> |
| method | prime(string $type, array $objectIds): array | update_meta_cache: each object's meta as all() gives it, the cached ones from the object cache and the rest read in one query and cached; an object with none has an empty list. @param list<int> $objectIds@return array<int, array<string, list<string>>> |
| method | keys(string $type): array | Every meta key an object type uses, once each, in key order. @return list<string> @return list<string> |
| method | listing(string $type, int $objectId): array | Every row of an object's meta by key, then id, as the post meta box lists them: the key, the value, the row's id and the object's, all as stored (strings). @return list<array<string, string>> |
| method | rowsOf(string $type, int $objectId): array | Every row of an object's meta in id order, for removing them one by one. @return list<array{meta_id: int, meta_key: string, meta_value: string}> @return list<array{meta_id: int, meta_key: string, meta_value: string}> |
| method | byId(string $type, int $metaId): ?array | One meta row by its id, under the table's own column names, values as stored; null when there is none. @return array<string, string>|null @return array<string, string>|null |
| method | columns(string $type): array | The column holding the object's id, and the one holding the row's: post_id and meta_id, user_id and umeta_id. @return array{0: string, 1: string} @return array{0: string, 1: string} |
| method | rewrite(string $type, int $metaId, string $key, string $stored): void | Sets one row's key and stored value. |
| method | add(string $type, int $objectId, string $key, string $stored): int | Inserts a meta row and returns its id. |
| method | matching(string $type, int $objectId, string $key): array | The rows of one key on one object, in id order. @return list<array{meta_id: int, meta_value: string}> @return list<array{meta_id: int, meta_value: string}> |
| static | idsToUpdate(array $rows, string $stored, ?string $previous): array | Which of a key's rows an update touches: none when no previous value was named and the first row already holds the value, all of them when no previous value was named, otherwise only the rows holding it. @param list<array<string, mixed>> $rows@return list<int> |
| method | updateRows(string $type, array $ids, string $stored): void | Sets the value of the given meta rows. |
| method | find(string $type, ?int $objectId, string $key, ?string $stored): array | The rows a delete would take: by key, for one object or all, optionally only a stored value. @return list<array{meta_id: int, object_id: int}> @return list<array{meta_id: int, object_id: int}> |
| method | objectsBelow(string $type, string $key, int $bound): array | The objects whose value under the key is a number below the bound, lowest id first. @return list<int> @return list<int> |
| method | deleteRows(string $type, array $ids): void | Deletes the given meta rows. @param list<int> $ids |
| inside | spec() |
MetaKeys final class
public/minn/src/Minn/Runtime/MetaKeys.php · 187 lines · Open the class →
The meta keys code registers, kept where the reference keeps them ($wp_meta_keys: object type => subtype => key => arguments), with what a registration wires up (probe meta-api). A key's sanitize and auth callbacks hang on their filters (with _for_{subtype} for a subtype's key); a key with no auth callback gets __return_true, or __return_false when protected. A default is served through filter_default_metadata. A registration is refused (with the reference's notice) for an array shown in REST without its items' schema, revisions where the type or subtype has none, and, once its callbacks are hooked, a default its schema does not accept. The old form (callbacks as plain arguments) hooks them and returns false.
| static | register(string $objectType, string $key, mixed $args, mixed $deprecated): bool | Registers a key; true when it joins the registry. |
| static | unregister(string $objectType, string $key, string $subtype): bool | Takes a key out of the registry, and its callbacks off their filters. |
| static | of(string $objectType, string $subtype = ''): array | The keys registered for an object type and subtype ('' for every subtype), in registration order. @return array<string, array<string, mixed>> @return array<string, array<string, mixed>> |
| static | forObject(string $objectType, string $subtype): array | The keys that apply to one object: those for every subtype, then its subtype's. @return array<string, array<string, mixed>> @return array<string, array<string, mixed>> |
| static | defaultValue(mixed $value, int $objectId, string $key, bool $single, string $objectType): mixed | The registered default for a key as filter_default_metadata answers: the one for every subtype, else the object's subtype's; the value handed in when neither registered one. |
| static | subtype(string $objectType, int $objectId): string | The object's subtype: a post's type, a term's taxonomy, comment or user while they exist; '' otherwise. |
| static | capabilities(string $capability, int $userId, array $args): ?array | A meta capability (edit, add or delete a post's, comment's, term's or user's meta) as the reference maps it: what editing the object needs, then the capability itself when the key is not allowed (protected, or refused by its auth filter); nothing for an object that does not exist. Null for any other capability. @param list<mixed> $args the object id, then the key@return list<string>|null |
| inside | refusal(), defaultFits() |
MetaTypes final class
public/minn/src/Minn/Runtime/MetaTypes.php · 21 lines · Open the class →
Meta types a plugin brought, by the table it named on $wpdb as "{$type}meta" (WooCommerce's order items keep theirs in woocommerce_order_itemmeta). The reference reads any such table with "{$type}_id" for the object and meta_id for the row; Meta does the same.
| static | register(string $type, string $table): bool | Registers a meta type by its full table name; refused unless both are plain identifiers. |
| static | table(string $type): ?string | The full table name of a registered meta type, or null. |
OEmbed final class
public/minn/src/Minn/Runtime/OEmbed.php · 114 lines · Open the class →
oEmbed as data: provider matching against the wildcard table, response parsing, and the markup an oEmbed payload becomes.
| static | providerFor(array $providers, string $url): ?string | The endpoint of the provider whose mask matches a URL, or null. @param array<string, array{0: string, 1: bool}> $providers mask => [endpoint with {format}, mask is a regex] |
| static | parseJson(string $body): ?array | An oEmbed JSON body as an array, or null. @return array<string, mixed>|null |
| static | parseXml(string $body): ?array | An oEmbed XML body as an array, or null. @return array<string, mixed>|null |
| static | html(array $data, string $url, Closure $escUrl, Closure $escAttr, Closure $escHtml): ?string | The embed HTML for an oEmbed response, or null. @param array<string, mixed> $data the oEmbed payload@param Closure(string): string $escUrl @param Closure(string): string $escAttr @param Closure(string): string $escHtml |
| static | discovered(string $html, array $linktypes): ?string | The provider endpoint a page names in its head: the first link of each oEmbed type (the linktypes map type to json or xml), JSON before XML, its address entity-decoded. Null when it names none. @param array<string, string> $linktypes |
| static | stripNewlines(string $html): string | Drops newlines from embed markup while leaving the inside of <pre> blocks untouched. |
ObjectCache final class
public/minn/src/Minn/Runtime/ObjectCache.php · 63 lines · Open the class →
The per-request object cache behind wp_cache_*: groups of keys, nothing persistent.
| method | get(string $key, string $group, ?bool $found = NULL): mixed | A cached value, or false. |
| method | set(string $key, mixed $value, string $group): bool | Stores a value. |
| method | add(string $key, mixed $value, string $group): bool | Stores a value only when the key is empty. |
| method | delete(string $key, string $group): bool | Removes a key. |
| method | flush(): bool | Empties the cache. |
| method | sizes(): array | Each group's size, its entries serialized, in bytes, in the order the groups were first written: what WP_Object_Cache::stats prints. @return array<string, int> |
| method | flushGroup(string $group): bool | Empties one group. |
ObjectTerms final class
public/minn/src/Minn/Runtime/ObjectTerms.php · 30 lines · Open the class →
wp_get_object_terms's handling of taxonomies registered with their own query arguments, as the reference handles them: among several, each such taxonomy is asked on its own with its arguments over the caller's; alone, its arguments join the caller's.
| static | byTaxonomyArgs(array $objectIds, array $taxonomies, array $args): array | The terms asked for apart, the taxonomies left (keys as they were), and the arguments for those. @param list<int> $objectIds@param array<int, string> $taxonomies@param array<string, mixed> $args@return array{0: list<mixed>, 1: array<int, string>, 2: array<string, mixed>} |
OptionSanitizer final class
public/minn/src/Minn/Runtime/OptionSanitizer.php · 114 lines · Open the class →
A core option's value cleaned as the reference's sanitize_option cleans it (probe sanitize-option), before sanitize_option_{$option}: counts as whole numbers, the site's name and tagline escaped, formats and mail settings stripped of markup, addresses checked and kept as they were (with a settings error) when they are not addresses, word lists split and trimmed, the timezone and language checked against what exists. Any other option is left to its filter, where register_setting puts a plugin's sanitize_callback.
| static | clean(string $option, mixed $value): mixed | The value as sanitize_option leaves it, through sanitize_option_{$option}. |
| inside | rule(), lists(), email(), perPage(), structure(), words(), domains(), languages() |
Options final class
public/minn/src/Minn/Runtime/Options.php · 498 lines · Open the class →
Options as plugin code sees them, kept where the reference keeps them: wp_cache's options group (OptionsGroup) holds the autoloaded list, the names known unset, and a key per option not autoloaded, so a plugin that reads, writes or drops those keys is heard. get_option() and wp_load_alloptions() pass the list through pre_wp_load_alloptions and alloptions. A value written this request is held as it was written (an int stays an int, true stays true), arrays and objects as stored. Each stored text is decoded once while it stays the same, except one holding an object, decoded afresh on every read as the reference does: a plugin that changes the object it was handed and saves it must find the stored copy still different, or its change is never written.
| const | GUARDED = array ( 0 => 'minn_runtime_symbols', 1 => 'minn_recovery_strikes', 2 => 'minn_cron_lock', ) | The options the engine keeps for itself: the symbol-gate cache a plugin could otherwise rewrite to pass its own gate, the recovery strikes, the cron lock, and the sign-in throttle rows. The facade refuses to write them; the engine writes them through this class directly. |
| const | ON_DEMAND = array ( 0 => 'minn_admin_changelog', 1 => 'minn_changelog', 2 => 'minn_release', 3 => 'minn_plugin_info', 4 => 'minn_updates', 5 => '_transient_minn_admin_uploads_size', 6 => '_transient_timeout_minn_admin_uploads_size', 7 => 'minn_admin_uploads_size_lock', ) | The engine's own caches, read on demand and written with autoload off (Site::setOption). Rows an older engine wrote autoloaded are set right the first time a loaded list holds them. |
| const | AUTOLOAD_VALUES = array ( 0 => 'yes', 1 => 'on', 2 => 'auto-on', 3 => 'auto', ) | The autoload column values that load an option with every request, in the reference's order. |
| const | EVERY_REQUEST = array ( 0 => 'minn_runtime_symbols', 1 => 'paused_plugins', 2 => 'WPLANG', 3 => 'https_migration_required', 4 => 'minn_admin_post_types', 5 => 'minn_admin_taxonomies', ) | Options not autoloaded that are read on every request (the symbol survey, recovery's paused plugins, the site language, the HTTPS migration flag, and the post types and taxonomies Minn Admin keeps and registers as it loads): read together as a request begins, as one read rather than one each. |
| new | __construct(Minn\Db $db) | |
| method | isFor(Minn\Db $db): bool | Whether this store reads through the given connection. |
| static | guarded(string $name): bool | Whether plugin code is refused a write to this option. |
| method | autoloaded(bool $force = false): mixed | The autoloaded list as wp_load_alloptions() gives it: pre_wp_load_alloptions may answer first (told whether the cache was forced), and then alloptions is not heard; otherwise the list the group holds, through alloptions, which may add or change entries without the group keeping them. |
| method | filtered(string $name, mixed ...$default): mixed | An option as get_option() reads it, through the filters plugins hook: pre_option_{name}, then pre_option, may answer first; then the autoloaded list is read through its filters on every read; an unset option is its default (false when none is given) through default_option_{name}, which is told whether one was given; a set one goes through option_{name}. |
| method | raw(string $name): ?string | An option's stored text, as Db::option() gives it, or null when it is unset: from the list or the group, a value written this request in its stored form (true as "1", a list serialized). |
| method | get(string $name): mixed | An option's value, decoded, or null when unset; home and siteurl answer to wp-config.php's constants. |
| method | prime(array $names): void | Reads several options at once (wp_prime_option_caches): the ones not held yet in one query, each kept under its own key or noted unset, as a read of each one alone would leave them. @param list<string> $names |
| method | exists(string $name): bool | Whether an option exists. |
| method | knownMissing(string $name): bool | Whether a read this request already found the option unset (the reference's notoptions). |
| method | add(string $name, mixed $value, string $autoload = 'auto'): bool | Writes a new option. add_option has already decided it is new (its value is still the default); a row that is there anyway is written over, as the reference's add does, and false comes back when it already held this value. An autoloaded one joins the list. |
| method | upsert(string $name, string $stored, string $autoload): bool | Writes an option row whether or not one exists, as the reference does for every add: two requests that both find a transient missing and both add it must not fail on the unique name, so the second write lands over the first. True when the row was inserted or changed, false when the same value was already there. |
| method | update(string $name, mixed $value, ?string $autoload = NULL): bool | False when the value is unchanged, as the reference reports. |
| method | markAutoload(string $name): bool | Flips the autoload column; false when the option is missing or already so. The list is read again, the option's own key dropped. |
| method | unmarkAutoload(string $name): bool | Stops an option loading on every request; false when it already did not, or is unset. It leaves the list. |
| method | delete(string $name): bool | Removes an option: out of the list or its own key, and known unset. |
| method | expiredTransientNames(int $now, string $prefix = '_transient_timeout_'): array | The names of transients whose expiry has passed. The timeout row is the one that knows, so the sweep reads those and hands back the bare names. @return list<string> |
| method | forget(string $name): void | Brings the group back in step with an option the engine wrote behind the store (its own rows, the CLI): the row is read again, into the list when it autoloads, and nothing else written this request is lost. |
| static | toStorage(mixed $value): string | What the reference stores (maybe_serialize): arrays and objects serialized, a string that looks serialized serialized again so it reads back as that string, other scalars as their string form. |
| static | fromStorage(string $raw): mixed | A stored option value decoded the way the reference reads it. |
| inside | listed(), missing(), lookup(), decode(), remember(), found(), holdsObject(), switchAutoload() |
OptionsGroup final class
public/minn/src/Minn/Runtime/OptionsGroup.php · 57 lines · Open the class →
wp_cache's options group, where option reads are kept as the reference keeps them: the autoloaded list under alloptions, the names a read found unset under notoptions, and one key per option not autoloaded. The engine's own object cache holds it, or an object-cache.php drop-in once the facade hands over its functions.
A drop-in's cache outlives the request, and the engine writes some options behind the store (its own state, the CLI), so a key left by an earlier request is never trusted: each is read from the database the first time a request asks for it, then kept in step for the rest of it.
| const | GROUP = 'options' | |
| static | useExternal(Closure $get, Closure $set, Closure $delete): void | Hands the group to an object-cache.php drop-in's functions. |
| method | get(string $key, ?bool $found = NULL): mixed | A key's value, found only when this request may trust it. |
| method | set(string $key, mixed $value): void | Stores a key; a drop-in is written only when it holds something else. |
| method | delete(string $key): void | Removes a key. |
Pages final class
public/minn/src/Minn/Runtime/Pages.php · 136 lines · Open the class →
get_pages() as the reference shapes it: its arguments as a post query (WP_Query, so its filters shape it), the tree order of the result, then the get_pages filter. wp_list_pages, wp_dropdown_pages and the navigation's page list take their pages from here.
| const | DEFAULTS = array ( 'child_of' => 0, 'sort_order' => 'ASC', 'sort_column' => 'post_title', 'hierarchical' => 1, 'exclude' => array ( ), 'include' => array ( ), 'meta_key' => '', 'meta_value' => '', 'authors' => '', 'parent' => -1, 'exclude_tree' => array ( ), 'number' => '', 'offset' => 0, 'post_type' => 'page', 'post_status' => 'publish', ) | |
| static | get(array $args): array | The pages get_pages() answers for parsed arguments. @param array<string, mixed> $args@return array<int, \WP_Post> |
| static | queryArgs(array $parsed, array $include, array $exclude): array | The post query the get_pages() arguments amount to, shaped as the reference hands it to get_pages_query_args (probe filter-inputs-2): the order by the columns as named, then (unless pages are picked by id) the excluded ones and the meta pair, the number or -1 and the offset, the type, the statuses as a list and the cache flags; then the picked pages, the authors and the parent, each when given. @param list<int> $include@param list<int> $exclude |
| static | arrange(array $pages, array $parsed): array | The pages as the reference leaves them: with a hierarchy (unless a parent is named or pages are picked by id) or a child_of, only what descends from that page (the root by default), each parent followed by its subtree; then any branch left out, its places left empty. @param list<object> $pages objects with ID and post_parent@return array<int, object> |
| static | children(array $pages, int $parent): array | What descends from a page within the list, depth first, siblings in list order (get_page_children); a page whose parent is not reached is left out. @param list<object> $pages@return list<object> |
| static | descendants(array $pages, int $parent): array | Every page under one ancestor, in list order. @param list<object> $pages @return list<object> @param list<object> $pages @return list<object> |
Patterns final class
public/minn/src/Minn/Runtime/Patterns.php · 161 lines · Open the class →
The block pattern, pattern category, and block style registries as data. Entries registered after init are remembered separately, because the editor asks for those on their own.
| method | registerPattern(mixed $name, mixed $properties): ?Minn\Runtime\Refusal | Registers a block pattern, or the refusal. |
| method | unregisterPattern(string $name): bool | Forgets a pattern. |
| method | pattern(string $name): ?array | One pattern, or null. |
| method | patterns(): array | Every pattern. @return list<array<string, mixed>> |
| method | patternsAfterInit(): array | The patterns registered after init, which the editor lists separately. |
| method | registerCategory(mixed $name, mixed $properties): ?Minn\Runtime\Refusal | Registers a pattern category, or the refusal. |
| method | unregisterCategory(string $name): bool | Forgets a category. |
| method | category(string $name): ?array | One category, or null. |
| method | categories(): array | Every category. @return list<array<string, mixed>> |
| method | categoriesAfterInit(): array | The categories registered after init. |
| method | registerStyle(mixed $blocks, mixed $properties): ?Minn\Runtime\Refusal | Registers a block style, or the refusal. @param string|list<string> $blocks |
| method | unregisterStyle(string $block, string $style): bool | Forgets a block style. |
| method | style(string $block, string $style): ?array | One block style, or null. |
| method | styles(?string $block = NULL): array | Every block style, or one block's. @return array<string, array<string, array<string, mixed>>> |
PlaceholderTrace final class
public/minn/src/Minn/Runtime/PlaceholderTrace.php · 37 lines · Open the class →
Records every call into a generated placeholder while a site opts in by having wp-content/minn-placeholder-trace.log on disk: one line per call with the symbol, the plugin file that called it, and the request. The log says which placeholders deserve behaviour; an absent file costs one stat per request.
| static | deadEnd(string $symbol): void | Logs a call to a method of a real class that Minn answers with nothing by design (an admin-screen helper): compat-status counts the method as a dead end, where a hit() marks one still owed. |
| static | hit(string $symbol): void | Logs a placeholder symbol being called, when the trace file exists. |
Placeholders final class
public/minn/src/Minn/Runtime/Placeholders.php · 71 lines · Open the class →
The printf placeholders plugin code hands wpdb::prepare, filled the way the reference fills them: a bare %s is escaped and quoted; a numbered (%1$s), padded (%5s) or precise (%.2f) one is formatted and escaped but never quoted, so a plugin can name a table with it; %d and %f cast; %i backticks an identifier; %% is a literal. Numbered placeholders address the argument list directly while unnumbered ones count from the first on their own, as vsprintf does. A placeholder with no argument behind it empties the whole query, as the reference does.
| static | fill(string $query, array $args, callable $escape): ?string | Fills a query's placeholders from the arguments; null when one has no argument. @param list<mixed> $args@param callable(string): string $escape the connection's string escape |
| static | prepare(string $query, array $args, callable $escape): array | fill() with what wpdb::prepare reports alongside: how many placeholders the query has (%% not among them) and the type of each argument it could not use (an array or an object fills as ''). @param list<mixed> $args@param callable(string): string $escape the connection's string escape@return array{query: ?string, placeholders: int, unsupported: list<string>} |
| inside | filled(), quoted(), text() |
PluginActivation final class
public/minn/src/Minn/Runtime/PluginActivation.php · 70 lines · Open the class →
Switching a plugin on and off the way the reference's activate_plugin and deactivate_plugins do (probe plugin-activation), for the facade and for the engine's own REST routes and WP-CLI verbs alike. Activating checks the plugin's requirements, then loads its main file through the runtime's gate (so its activation hook is registered, silent or not), fires activate_plugin and activate_<file> (which runs that hook), writes active_plugins sorted through update_option, and fires activated_plugin; whatever the plugin printed meanwhile makes the answer unexpected_output, with the plugin left active, as the reference leaves it. Silent skips the actions, not the load. A plugin the gate will not load (it needs functions or classes the runtime lacks) is refused rather than recorded active with code that could never run its activation hook. Deactivating fires deactivate_plugin, deactivate_<file> and deactivated_plugin for each plugin, then writes the list once.
| static | activate(string $plugin, ?Minn\Runtime\Hooks $announce, ?Closure $networkStore = NULL): ?Minn\Runtime\Refusal | Activates one plugin ("dir/file.php"), announcing it to the hooks given, or to nobody (silent); null when it is active afterwards with nothing to report. |
| static | deactivate(array $plugins, ?Minn\Runtime\Hooks $announce): void | Deactivates plugins ("dir/file.php" each), announcing each to the hooks given, or to nobody (silent); those not active are passed over. @param list<string> $plugins |
| inside | active() |
PluginRemoval final class
public/minn/src/Minn/Runtime/PluginRemoval.php · 124 lines · Open the class →
Deleting plugins as the reference's delete_plugins does it: each in turn is uninstalled first when it can be (its uninstall.php, or the callback it registered), then delete_plugin, its folder (or file) removed, deleted_plugin, and its installed translations removed with it; the updates offered for the deleted ones are forgotten. A plugin already gone counts as deleted. Only what lies inside the plugins folder is ever removed, and a symlinked folder is unlinked, never followed.
| static | delete(array $plugins): WP_Error|bool | delete_plugins: true, false for none asked, or the error naming those left behind. @param array<int, string> $plugins plugin files, relative to the plugins folder |
| static | uninstall(string $plugin): ?bool | uninstall_plugin: pre_uninstall_plugin, then the plugin's uninstall.php (WP_UNINSTALL_PLUGIN naming it) or the callback it registered (heard on uninstall_<file>, with the plugin loaded); either is forgotten once run. |
| inside | deleteOne(), removeFiles(), removeTranslations(), forget(), forgetUpdates() |
PluginRequirements final class
public/minn/src/Minn/Runtime/PluginRequirements.php · 66 lines · Open the class →
Whether a plugin can be switched on here, as the reference's validate_plugin_requirements answers (probe plugin-activation): the plugin header's "Requires at least" against the WordPress version the site speaks, its "Requires PHP" against the running PHP, and every slug in its "Requires Plugins" installed and active (a readme.txt is not read). A refusal carries the reference's code, its HTML message word for word, and for missing plugins the slugs found inactive (slug => name) and not installed (slug => slug). The slugs are taken sorted (one that is not a lower-case slug is passed over), and named in that order.
| static | check(string $plugin): ?Minn\Runtime\Refusal | Null when the plugin may be activated, otherwise why not. |
| inside | dependencies(), installed() |
PluginUpdates final class
public/minn/src/Minn/Runtime/PluginUpdates.php · 65 lines · Open the class →
The update offers the site's own plugins publish. A plugin that hosts itself, or sells itself, is not in the wordpress.org directory and never appears in the directory's answer: it publishes its offer by filtering the update transient WordPress reads, which is the only place anyone learns of it. The engine asks the same question of the runtime, in the shape the reference asks it, so a self-hosted plugin is offered its update here exactly as it would be on WordPress.
The transient carries `checked` (every installed plugin and its version), and a plugin's updater returns early when that is empty, so the installed versions are what make the question answerable.
| static | supplied(array $installed): array | What the site's plugins offer for themselves, empty when no runtime is booted or nothing filters the transient. @param array<string, string> $installed plugin file => installed version@return array{plugins: array<string, array<string, mixed>>, no_update: array<string, array<string, mixed>>} |
| static | fromFiltered(mixed $filtered, array $installed): array | The filtered transient as the state's two buckets: offers under `plugins`, plugins that answered "current" under `no_update`, both as arrays, and only for files this site actually has. @param array<string, string> $installed@return array{plugins: array<string, array<string, mixed>>, no_update: array<string, array<string, mixed>>} |
| inside | bucket() |
Plugins final class
public/minn/src/Minn/Runtime/Plugins.php · 272 lines · Open the class →
Loads the site's plugins into the runtime the way the reference does: mu-plugins first, then active_plugins in stored order, each file included once. A plugin loads only when the static symbol read finds nothing the runtime lacks; otherwise it is reported and skipped so the site keeps rendering. The lifecycle actions fire between the phases.
| static | load(Minn\Runtime\Runtime $runtime): void | Loads the active plugins as code and fires the boot hooks. Recovery is armed for exactly this window: a failure here is one every visitor would hit, so it may be recorded against its extension. |
| static | loadTheme(Minn\Runtime\Runtime $runtime): void | The theme's setup without the plugins: setup_theme, the theme's functions.php, after_setup_theme, then wp_loaded. The probe runtime ends its boot with this, so a theme's supports are what a request's would be and a late declaration is judged as one; it skips init, whose work the probes compare on their own. |
| static | loaded(): array | The plugins that loaded. @return list<string> |
| static | skipped(): array | The plugins the symbol gate refused, with what they lacked. @return array<string, array<string, mixed>> plugin file => why it did not load |
| static | loadNow(string $plugin, Minn\Runtime\Runtime $runtime): ?string | Loads one plugin's main file now, as activating it does: the reference includes the file (once) before activate_plugin fires, so the plugin's activation hook is in place to hear activate_<file>, and announces no plugin_loaded for it. The same gate as the boot decides, its folder walked again first; null once the plugin is running, otherwise why the gate refused it. |
| static | isLoaded(string $plugin): bool | True when the named plugin file is running as code this request. |
| inside | boot(), loadThemeFunctions(), rememberThemeDomain(), includeFile(), registerRealpath(), isolatedInclude() |
PostData final class
public/minn/src/Minn/Runtime/PostData.php · 68 lines · Open the class →
The loop's view of a post, as the reference's generate_postdata and get_the_content give it (probes the-content, excerpt): the content split into pages at <!--nextpage--> (the line breaks beside the marker go with it) and handed to content_pagination, the page the query asks for, and "more" when the main query shows a single post, a page or a feed; then the content of that page, cut at the more tag with a more link (or run on past it when "more" is set), the teaser dropped when asked or marked <!--noteaser-->.
| static | generate(WP_Post $post, ?WP_Query $query = NULL): array | id, authordata, currentday, currentmonth, page, pages, multipage, more, numpages. The page comes from the query given (the main query otherwise); more is set on a single, a page or a feed, or past the first page of a post split into pages (probe query-compat). @return array<string, mixed> |
| static | content(?string $moreLinkText, WP_Post $post, array $elements): string | The content get_the_content gives: the asked page, cut at the more tag unless "more" is set. @param array<string, mixed> $elements as generate() gives them, with strip_teaser when the teaser should go |
PostEvents final readonly class
public/minn/src/Minn/Runtime/PostEvents.php · 165 lines · Open the class →
What the reference's REST controllers tell plugins about a post they write, for the engine's own controllers: the same actions in the same order with the same arguments, fired through the facade's lifecycle functions so the facade's own writes say the same (contracts/runtime.md "Writes tell plugins"). Without a booted runtime every method does nothing, so a write with no plugins loaded is exactly what it was.
| method | deleteForGood(int $id): mixed | A post deleted for good through the reference's own deletion, so its hooks fire and its children (revisions, autosaves) go with it: the one path every permanent delete takes. Deleting needs the facade, so unlike the events it runs only under a booted runtime, which the engine's write routes are. Answers what the deletion answered. |
| method | trash(int $id): mixed | A post (an attachment too) sent to the trash through the reference's own trashing, as deleteForGood deletes. |
| method | live(): bool | Whether plugins are loaded to be told anything. |
| method | beforeSave(array $columns, ?Minn\Content\PostRecord $existing): void | Before the row is written: pre_post_insert for a new post, pre_post_update for one that exists. @param array<string, mixed> $columns @param array<string, mixed> $columns |
| method | saved(int $id, ?Minn\Content\PostRecord $before): void | Once the row is written: the caches, the status transition, the edit actions of an update, the save actions. |
| method | ensureCategory(Minn\Content\PostWriter $writer, int $id): void | A post keeps a category through every save, the default when it has none: through wp_set_post_categories with plugins loaded (which tells them, set_object_terms included), quietly without. |
| method | applyTerms(Minn\Content\PostWriter $writer, int $id, array $body, string $type = ''): void | The terms a REST body names, set through wp_set_object_terms with plugins loaded, quietly without: a plugin's type takes its own REST taxonomies under their REST bases (probe rest-plugin-types). @param array<string, mixed> $body |
| method | applyMeta(Minn\Content\PostWriter $writer, int $id, array $body, string $type): void | The meta a REST body names: through the registered keys with plugins loaded (probe rest-meta; a plugin's type only when it supports custom fields), core's own written directly without. @param array<string, mixed> $body |
| method | attachmentGenerated(int $id, string $file): void | A new attachment's metadata made by the runtime, as the reference's REST upload makes it: wp_generate_attachment_metadata over the stored file (the sizes cut with the attachment's id, the metadata stored as each one lands, so an optimiser hooked there finds it), then wp_update_attachment_metadata. Every upload has some: an image its sizes, a PDF its previews, any file its size (probe hook-inputs-2). |
| method | restInserted(int $id, Minn\Http\Request $request, ?Minn\Content\PostRecord $before): void | rest_insert_{type}, before the request's own terms and fields are applied; a post with no $before is a new one. |
| method | restAfterInsert(int $id, Minn\Http\Request $request, ?Minn\Content\PostRecord $before): void | rest_after_insert_{type}, once the request's terms and fields are in; a post with no $before is a new one. |
| method | afterInsert(int $id, ?Minn\Content\PostRecord $before): void | wp_after_insert_post, last; the revision of an update is saved from it. |
| method | restDeleted(Minn\Content\PostRecord $post, array $data, Minn\Http\Request $request): void | rest_delete_{type}, after a trash or a delete, with the post as it was answered and the response. @param array<string, mixed> $data @param array<string, mixed> $data |
| inside | rest(), wpPost() |
PostInsert final readonly class
public/minn/src/Minn/Runtime/PostInsert.php · 168 lines · Open the class →
The decisions behind wp_insert_post: which columns a postarr fills, when the post counts as empty, the status a publish request lands in, the dates and the slug, the categories a new post gets. The rows are written by Content\PostWriter; the facade fires the hooks around each step.
| new | __construct(Minn\Content\PostWriter $writer, int $userId, Closure $discussion, Closure $supports, Closure $canPublish, Closure $gmtFromDate, Closure $now) | @param Closure(string, string): string $discussion a new post's comment or ping status by type, as get_default_comment_status has it@param Closure(string, string): bool $supports whether a post type supports a feature@param Closure(string): bool $canPublish whether the current user may publish the type@param Closure(string): string $gmtFromDate the site-local date as GMT@param Closure(bool): string $now the current local (or GMT) MySQL time |
| method | columns(array $postarr, ?array $existing): array | The columns the posts table takes, filled from a postarr, with a new post's defaults. @param array<string, mixed>|null $existing @return array<string, string> @param array<string, mixed>|null $existing @return array<string, string> |
| method | isEmpty(array $columns, ?array $existing): bool | A post with nothing in title, content, and excerpt is empty when its type supports all three. @param array<string, mixed>|null $existing @param array<string, mixed>|null $existing |
| method | resolve(array $columns, ?array $existing): array | The columns as they will be written: the status a request lands in (attachments inherit, unprivileged publishes pend, a future date schedules), the dates, the slug (sanitized; not yet free), and the integer columns. @param array<string, mixed>|null $existing@return array<string, string> |
| method | persist(array $columns, ?int $existingId, Closure $guid): int | Writes the resolved columns; a new post without a guid gets the ?p= form. @return int the post id |
| static | categories(array $postarr, string $type, string $status, bool $update, array $taxonomies, int $default): ?array | The categories a saved post should carry: the ones given, or the default for a new post of a type that has categories; null when nothing should change. @param list<string> $taxonomies the type's taxonomies@return list<int>|null |
| inside | type() |
PostLinks final class
public/minn/src/Minn/Runtime/PostLinks.php · 151 lines · Open the class →
Post addresses as the reference's link functions build them (probe permalinks): a post through pre_post_link (the structure) and post_link; a page from get_page_uri through _get_page_link and page_link; an attachment under its parent's address, or its own; a plugin's type by its address pattern (the permastruct its rewrite registered) through post_type_link, by its query var without one. Plain (?p=, ?page_id=, ?post_type=) unless its status is public, or private and readable by whoever asks, or it is a sample: a draft, a scheduled or trashed post, a revision, a private one a visitor asks for. "Leaving the name" keeps the slug as its placeholder (%postname%, %pagename%, %{type}%) for an editor to fill in, and get_sample_permalink shows a draft as it would be published.
| const | LEAVE_NAME = 1 | The slug kept as its placeholder. |
| const | SAMPLE = 2 | The address an editor previews: a draft as if published. |
| static | post(WP_Post $post, int $flags): string | A post's address (not a page, attachment or plugin type). |
| static | page(WP_Post $post, int $flags): string | A page's own address, before page_link: ?page_id= while unpublished, its tag when the name is left, else its path. |
| static | attachment(WP_Post $post, int $flags): string | An attachment's address: under a parent it belongs to, else its own slug, else ?attachment_id=. |
| static | custom(WP_Post $post, int $flags): string | A plugin type's address by its pattern, by its query var, or by the type and id plainly. |
| static | sample(WP_Post $post, ?string $title, ?string $name): array | The address an editor shows with the slug to edit: the post as if published, its name made unique, the address with the name left, a page's parents written in. @return array{0: string, 1: string} |
| inside | tokens(), category(), plain() |
PostLookup final readonly class
public/minn/src/Minn/Runtime/PostLookup.php · 134 lines · Open the class →
The post reads plugin code asks for by shape: a page by title, revisions, counts.
| new | __construct(Minn\Db $db) | |
| method | idByTitle(string $title, array $types): ?int | The id of a post with this title among the types, or null. @param list<string> $types |
| method | revisionsOf(int $postId): array | Revision rows newest first. @return list<array<string, mixed>> @return list<array<string, mixed>> |
| method | countByStatus(string $type): array | How many posts of a type there are per status. @return array<string, int> status => count |
| method | countAttachments(): array | How many attachments there are per mime type. @return array<string, int> mime type => count, plus 'trash' |
| method | countByAuthor(int $userId, array $types, array $statuses): int | How many posts an author has among the types and statuses. @param list<string> $types @param list<string> $statuses |
| method | countsByAuthors(array $userIds, array $types, array $statuses): array | Post counts for several authors at once, as stored (strings), for those that have any. @param list<int> $userIds@param list<string> $types@param list<string> $statuses@return array<int, string> |
| method | publishingAuthors(string $type, int $limit): int | How many authors (up to a limit) have published posts of a type, for is_multi_author. |
| method | countsByAuthor(array $types, array $statuses, int $viewer): array | Post counts by author for wp_list_authors: the types' posts in the statuses given, and the private ones of the viewer (an id, or -1 for none) beside them. @param list<string> $types @param list<string> $statuses@return array<int, int> |
| method | idsByAuthor(int $userId): array | Every post id of an author. @return list<int> |
| method | attachmentIdByFile(string $path): ?int | The attachment whose stored file path is the given one. |
PostQuery final class
public/minn/src/Minn/Runtime/PostQuery.php · 394 lines · Open the class →
WP_Query::get_posts as the reference runs it (probe wp-query-sql): the variables parsed and handed to pre_get_posts, the clauses written piece by piece, every filter a plugin may change them through in the reference's order (suppress_filters keeps the ones it keeps), the request split into an id query when that is cheaper, the count, a single post's status check and preview, sticky posts on the home listing, and the results filters.
| new | __construct(Minn\Db $db) | |
| method | run(WP_Query $query, object $wpdb, mixed $urlSearch): mixed | Runs a query object's variables and fills it in: the posts (or ids, or id => parent), the request, the counts. Returns what get_posts returns. |
| method | search(WP_Query $query, array $q, mixed $urlSearch): string | A search's WHERE fragment, settling the search variables (the terms, how many the string split into, the title matches its order ranks by). @param array<string, mixed> $q |
| method | stopwords(): array | The search stopwords, translated and filtered. @return list<string> @return list<string> |
| inside | prepare(), defaults(), pageSize(), clauses(), taxonomies(), searchOrder(), filtered(), through(), paging(), execute(), idsOnly(), select(), foundPosts() |
PostQueryParts final class
public/minn/src/Minn/Runtime/PostQueryParts.php · 56 lines · Open the class →
The pieces of one WP_Query run as the reference builds them and hands them to filters. The seven clause pieces stay untyped: a filter may hand back anything, and the request is written from whatever it handed back.
| const | PIECES = array ( 0 => 'where', 1 => 'groupby', 2 => 'join', 3 => 'orderby', 4 => 'distinct', 5 => 'fields', 6 => 'limits', ) | The pieces posts_clauses and posts_clauses_request see, in their order. |
| new | __construct(string $table) | |
| prop | mixed $where | |
| prop | mixed $groupby | |
| prop | mixed $join | |
| prop | mixed $orderby | |
| prop | mixed $distinct | |
| prop | mixed $fields | |
| prop | mixed $limits | |
| prop | mixed $search | |
| prop | string $whichauthor | |
| prop | string $whichmimetype | |
| prop | bool $statusJoin | Whether an attachment's status follows its parent's (a taxonomy archive's attachments). |
| prop | int $page | |
| prop | mixed $postType | The post type the query settles on: a name, a list, `any`, or empty. |
| prop | ?WP_Post_Type $typeObject | |
| prop | string $typeCap | |
| prop | array $statuses | The statuses asked for by name, which a single post's status check lets through. @var list<string> |
| prop | readonly string $table | |
| method | pieces(): array | The seven pieces by name. @return array<string, mixed> @return array<string, mixed> |
| method | take(array $clauses): void | Takes back the pieces a clauses filter returned; one it dropped becomes empty. @param array<array-key, mixed> $clauses @param array<array-key, mixed> $clauses |
| method | request(string $foundRows, mixed $fields): string | The SELECT as the reference writes it, line breaks and all, for the given field list. |
PostQueryResults final class
public/minn/src/Minn/Runtime/PostQueryResults.php · 113 lines · Open the class →
What WP_Query does with its posts once it has them, as the reference does it (probe wp-query-sql): a single post whose status is not public is dropped unless the reader may see it (a draft its editor may preview, with the_preview); sticky posts move to the front of the home listing's first page, the ones it did not find fetched by a nested query; the_posts; and the post objects, count and current post set.
| new | __construct(WP_Query $query, Minn\Runtime\PostQueryParts $parts) | |
| method | settle(array $q): void | Settles the posts a query found: status check, stickies, the_posts, then the count and the current post. @param array<string, mixed> $q @param array<string, mixed> $q |
| inside | singleStatus(), visible(), stickies() |
PostQueryStatus final class
public/minn/src/Minn/Runtime/PostQueryStatus.php · 165 lines · Open the class →
WP_Query's post type and status clause as the reference writes it (probe wp-query-sql). Statuses asked for by name: the type clause, then the statuses (any, less those excluded from search; private kept apart when the query asks for readable posts; the user's own when they may not read or edit others'; an attachment's parent's status for a taxonomy archive). None asked for, on a listing: each queried type, sorted, with its public statuses, the admin list's protected ones in the admin, and private ones the signed-in user may read. A single post: the type clause alone.
| new | __construct(WP_Query $query, Minn\Runtime\PostQueryParts $parts) | |
| method | settleType(): void | Settles the post type the caps come from: a list of several types has none (its caps are named for multiple_post_type), any other names one. |
| method | append(array $q): void | Appends the type and status clause. @param array<string, mixed> $q @param array<string, mixed> $q |
| inside | typeWhere(), cap(), named(), listing(), privateStatuses() |
PostQueryTax final class
public/minn/src/Minn/Runtime/PostQueryTax.php · 193 lines · Open the class →
The taxonomy side of WP_Query as the reference runs it (probe wp-query-sql): the clauses its variables make (tax_query, taxonomy and term, each taxonomy's query var, cat and the category__ lists, tag and the tag__ lists), settled back into the variables; the post types a taxonomy archive with no type searches; and the cat, category_name, tag_id, taxonomy and term variables set from the terms queried.
| static | clauses(WP_Query $query, array $q): array | The tax query a set of variables makes, settling the variables it reads. @param array<string, mixed> $q@return list<array<string, mixed>>|array<string, mixed> |
| static | postTypes(array $taxonomies): array|string | The post types a taxonomy archive with no type searches: every searchable type the queried taxonomies are registered for, one as a string, several sorted, none as `any`. @param list<string> $taxonomies@return string|list<string> |
| static | compat(WP_Query $query, array $q, array $queried): void | The compatibility variables the queried terms set: taxonomy and term (or term_id) from the first taxonomy other than category and post_tag when none is set, and cat, category_name and tag_id from those two. @param array<string, array{terms?: list<mixed>, field?: string}> $queried |
| inside | queryVar(), cat(), categoryLists(), tag(), tagLists() |
PostQueryWhere final class
public/minn/src/Minn/Runtime/PostQueryWhere.php · 264 lines · Open the class →
The WHERE fragments WP_Query writes from its variables, in the reference's order and spacing (probe wp-query-sql): menu order, the legacy m date and the date variables, a slug or a page path, ids, parents, a page id (which replaces everything before it), authors, comment counts, mime types, passwords, and comment and ping status. Each appends to the parts it is handed and settles the variables it reads, as the reference leaves them.
| new | __construct(WP_Query $query, Minn\Runtime\PostQueryParts $parts, string $table) | |
| method | dates(array $q): void | Menu order, the m date, the date variables and the date_query. @param array<string, mixed> $q @param array<string, mixed> $q |
| method | names(array $q): void | The post a slug, a page path, an attachment slug, a name list, an id or an id list names, and the parent; a post type's query var stands for the name (the page path, for a hierarchical type). @param array<string, mixed> $q |
| method | authors(array $q): void | Authors (an author list's negatives become author__not_in, which wins over author__in), an author slug, a comment count, and mime types. The slug and mime clauses wait to follow the search. @param array<string, mixed> $q |
| method | access(array $q): void | A password, or whether there is one, and comment and ping status. @param array<string, mixed> $q @param array<string, mixed> $q |
| inside | nameList(), typeQueryVar(), pagename(), ids(), authorName(), commentCount() |
PostRevisions final class
public/minn/src/Minn/Runtime/PostRevisions.php · 67 lines · Open the class →
A post's revisions as the reference's wp_save_post_revision keeps them (probe revisions): only for a type that supports revisions and while wp_revisions_to_keep allows any; compared with the latest revision (wp_save_post_revision_check_for_changes, the revision fields with their whitespace evened out, wp_save_post_revision_post_has_changed, whose default also compares the revisioned meta); saved through wp_insert_post and _wp_put_post_revision (whose default copies the revisioned meta); then the oldest dropped past the limit, after wp_save_post_revision_revisions_before_deletion.
| static | save(WP_Post $post): ?int | The new revision's id, or null when none was called for. |
| static | put(array $data): ?int | The revision (or autosave) row _wp_post_revision_data made, written through wp_insert_post, then _wp_put_post_revision; its id. @param array<string, mixed> $data |
| inside | changed(), prune() |
PostSave final class
public/minn/src/Minn/Runtime/PostSave.php · 253 lines · Open the class →
A REST save's columns through the filters the reference's save runs before it writes, in its order (probe post-insert-filters, contracts/runtime.md "Saves plugins can change"): rest_pre_insert_{type} over the prepared post; every column through its db context (pre_post_* and *_save_pre, where kses and the custom-CSS strip sit) on slashed text; wp_insert_post_empty_content, which refuses a post with no content, title or excerpt; wp_insert_post_parent; for a post that is live, the slug, made again from the filtered title when the request gave none, through pre_wp_unique_post_slug, the bad-slug check and wp_unique_post_slug; and wp_insert_post_data over the row. What a filter changes is written. Without plugins loaded the columns are as given.
| static | filter(array $columns, ?Minn\Content\PostRecord $before, array $body, Minn\Http\Request $request, string $type, Minn\Content\PostSlugs $slugs): array | The columns to write after the filters. @param array<string, mixed> $columns what the engine settled: a new post's whole row, or an update's changes@param array<string, mixed> $body the request's fields@return array<string, mixed> |
| static | throughSaveFilters(int $id, array $changes): array | A direct save's changed columns through every column's db context, as an update runs them over the whole post (pre_post_* and *_save_pre, where kses and the global styles strip sit): a global styles save writes its post this way (probe global-styles-unfiltered). Without a booted runtime the changes are as given. @param array<string, string> $changes column => value@return array<string, string> |
| static | sanitized(array $postarr): array | wp_insert_post's array with its defaults filled in (ID 0 for a new post), through sanitize_post's db context: slashed in, slashed out. @param array<string, mixed> $postarr@return array<string, mixed> |
| static | refusesEmpty(array $sanitized, string $type): bool | wp_insert_post_empty_content over whether a type with an editor, a title and an excerpt has none of them. @param array<string, mixed> $sanitized @param array<string, mixed> $sanitized |
| static | parent(array $sanitized, int $id): int | The parent through wp_insert_post_parent (whose default refuses a loop). @param array<string, mixed> $sanitized @param array<string, mixed> $sanitized |
| static | slugFilters(string $slug, int $id, string $status, string $type, int $parent, Minn\Content\PostSlugs $slugs): string | A live post's slug through the reference's three filters: pre_wp_unique_post_slug may settle it; otherwise it is made free in its type's scope, the bad-slug filter judging the slug asked for when nothing else has taken it; and wp_unique_post_slug has the last word. |
| static | guidAndSlug(array $columns, int $postId, string $type, Minn\Content\PostSlugs $slugs): array | The row's guid and slug as wp_insert_post settles them (probe insert-defaults): an update keeps the guid it had, in its display form, ignoring a new one asked for; a live post's slug goes through the slug filters. @param array<string, string> $columns@return array<string, string> |
| static | keepsSlug(string $status, string $type): bool | Whether a save leaves the slug as it is: a draft, a pending post, an auto-draft, a revision, a personal data request. |
| static | data(array $row, array $sanitized, array $unsanitized, int $postId): array | The row through wp_insert_post_data (wp_insert_attachment_data for an attachment), handed slashed as the reference hands it and unslashed back. @param array<string, mixed> $row the row, unslashed@param array<string, mixed> $sanitized@param array<string, mixed> $unsanitized@return array<string, mixed> |
| inside | stored(), prepared(), changed() |
QueriedObject final readonly class
public/minn/src/Minn/Runtime/QueriedObject.php · 70 lines · Open the class →
Which object a query is "about", read from its flags and variables: a term (by id or slug), a post type, the posts page, the current post, or an author. The caller materialises the record; this only decides where to look.
| prop | readonly string $kind | |
| prop | readonly string $field | |
| prop | readonly string|int $value | |
| prop | readonly string $taxonomy | |
| static | locate(array $vars, array $flags, Minn\Runtime\Registry $registry, callable $option): self | The queried object the query vars and flags point at. @param array<string, mixed> $vars@param array<string, bool> $flags the query's is_* flags@param callable(string): mixed $option a filtered option read |
| inside | taxonomyTerm() |
QueryFlags final readonly class
public/minn/src/Minn/Runtime/QueryFlags.php · 197 lines · Open the class →
The conditional flags a set of query variables implies (is_single, is_archive, is_home, ...), derived the way the reference's parse step derives them, plus the variables after the integer casts that step applies.
| prop | readonly array $vars | |
| prop | readonly array $flags | |
| static | fill(array $vars, Minn\Runtime\Registry $registry): array | The variables with every one the reference fills given its empty value: the template's keys up to search_columns (the ones after it are get_posts' own, settled when the query runs). @param array<string, mixed> $vars@return array<string, mixed> |
| static | derive(array $vars, Minn\Runtime\Registry $registry, callable $option, array $asked): self | The is_* flags the query vars amount to. @param array<string, mixed> $vars the filled query variables@param callable(string): mixed $option a filtered option read@param array<string, mixed> $asked the query as it was given, before filling (a search is one that named s) |
| static | customTaxonomyVar(array $vars, Minn\Runtime\Registry $registry): ?array | The first registered taxonomy (other than the two built-in ones) whose query variable carries a value, as [taxonomy name, query var]. @return array{0: string, 1: string}|null |
| inside | sanitize(), archiveFlags() |
Recovery final readonly class
public/minn/src/Minn/Runtime/Recovery.php · 234 lines · Open the class →
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.
| 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. |
| 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. @param array<string, string> $realpaths@param list<string>|null $active the active plugin files; read from the site when omitted@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. @param array{kind: string, name: string} $blamed@param array{type: int, file: string, line: int, message: string} $error |
| 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. @param array{kind: string, name: string} $blamed |
| 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. |
| inside | pluginFile(), activePlugins(), strikes(), forgetStrikes(), read(), write() |
Refusal final readonly class
public/minn/src/Minn/Runtime/Refusal.php · 6 lines · Open the class →
A refused operation, the way plugin code expects to read it: a code, a message, optional data. The facade turns it into WP_Error.
| new | __construct(string $code, string $message, mixed $data = NULL) | |
| prop | readonly string $code | |
| prop | readonly string $message | |
| prop | readonly mixed $data |
RegisteredSettings final class
public/minn/src/Minn/Runtime/RegisteredSettings.php · 102 lines · Open the class →
Settings as register_setting keeps them (probe rest-settings): the arguments a plugin passes, filtered through register_setting_args and laid over the defaults (a string, in its group, unlabelled, undescribed, unsanitized, not shown in REST); the sanitize callback put on sanitize_option_{$name} with the value alone; a registered default answering get_option through filter_default_option; register_setting fired once kept. unregister_setting takes all of it back. Core's own settings (the site's title, tagline, addresses, formats, reading and discussion defaults) are registered as the REST server starts.
| static | all(): array | Every registered setting by option name, in registration order. @return array<string, array<string, mixed>> @return array<string, array<string, mixed>> |
| static | register(string $group, string $name, mixed $args): void | register_setting: $args an array, or (as it once was) the sanitize callback alone. |
| static | unregister(string $group, string $name, mixed $deprecated = ''): void | unregister_setting: the setting, its sanitize callback and its default gone, unregister_setting fired. |
| static | defaultOf(string $option, mixed $default): mixed | A setting's registered default, or $default when it registered none. |
| static | registerCore(): void | register_initial_settings: core's settings, each through register_setting. |
Registry final class
public/minn/src/Minn/Runtime/Registry.php · 513 lines · Open the class →
Post types, taxonomies, and statuses as plugin code registers and reads them. The built-in set is data/registry.json, captured from the reference; registrations derive their defaults the way the content probe observed (contracts/fixtures/api/content.json).
| new | __construct(string $engineDir) | |
| prop | readonly array $queryVars | |
| prop | readonly array $publicQueryVars | |
| method | postTypes(): array | Every post type. @return array<string, array<string, mixed>> |
| method | postType(string $name): ?array | One post type, or null. |
| method | taxonomies(): array | Every taxonomy. @return array<string, array<string, mixed>> |
| method | taxonomy(string $name): ?array | One taxonomy, or null. |
| method | shareObjects(): void | Makes the one object for each registered type and taxonomy and binds $wp_post_types and $wp_taxonomies to them, as the reference shares them: a plugin changing one (relabelling Posts) is heard by every reader (probe type-object-sharing). Called once the facade classes exist; later registrations make their own. |
| method | postTypeObject(string $name): ?WP_Post_Type | The one object for a registered post type, or null. |
| method | postTypeObjects(): array | The objects of every registered post type, in registration order. @return array<string, \WP_Post_Type> |
| method | taxonomyObject(string $name): ?WP_Taxonomy | The one object for a registered taxonomy, or null. |
| method | taxonomyObjects(): array | The objects of every registered taxonomy, in registration order. @return array<string, \WP_Taxonomy> |
| method | statuses(): array | Every post status. @return array<string, array<string, mixed>> |
| method | status(string $name): ?array | One post status, or null. |
| method | translateBuiltIns(): void | The built-in types', taxonomies' and statuses' labels in the site's language, as the reference registers them again on init: each label asks for its own message (data/registry-labels.json, captured from the reference), and a type's single-item template and a taxonomy's archive template are named after the singular name. Rows and the shared objects both take them; what a plugin set before init gives way, as it does there. |
| method | adoptPostType(WP_Post_Type $object): array | Takes a built post type as the registered one: the object register_post_type made is the one every reader shares, and its properties are the type's row. Registering again puts a new object in the old one's place, as on the reference. @return array<string, mixed> |
| method | labels(array $labels, ?string $label, string $base, string $name): array | A type's labels from its own (probe post-remaining): the labels of its base type (page for a hierarchical type, post for any other) under the type's own. A missing name comes from the type's label, a missing singular name from the name, the menu name from the name, all items from the menu name, archives from all items, and the admin bar name from the singular name or else the type's name; a type with a singular name also names its single-item template. @param array<string, mixed> $labels@return array<string, mixed> |
| method | taxonomyLabels(array $labels, ?string $label, string $base, string $name): array | A taxonomy's labels from its own (probe taxonomy-remaining): derived as a type's are, under the category labels for a hierarchical taxonomy or the tag labels for any other; one that names no template takes "%s Archives" with the singular name it ends up with. @param array<string, mixed> $labels@return array<string, mixed> |
| static | capabilities(array|string $capabilityType, array $overrides, bool $mapMetaCap): array | A type's capability names (probe post-remaining): the eight primitive ones, the six more a mapped type uses, the type's own names merged over them, and create_posts last, the edit capability unless named. @param array<string, string> $overrides@return array<string, string> |
| method | unregisterPostType(string $name): bool | Forgets a non-builtin post type. |
| method | addSupport(string $type, string $feature, array $args): void | Adds a feature to a type, registered or not: the reference keeps features apart from the type objects, so declaring one does not make the type exist. A feature with arguments keeps all of them. @param list<mixed> $args |
| method | removeSupport(string $type, string $feature): void | Removes a feature from a post type. |
| method | removeSupports(string $type): void | Removes every feature of a post type. |
| method | supports(string $type): array | The features a post type supports. @return array<string, mixed> feature => true or the arguments given |
| method | defaultLabels(string $plain, string $hierarchical): array | The default labels of a family as label => [the plain one's, the hierarchical one's]: post and page for types, tag and category for taxonomies, from the built-in data (WP_Post_Type::get_default_labels). @return array<string, array{0: mixed, 1: mixed}> |
| method | adoptTaxonomy(WP_Taxonomy $object): array | Takes a built taxonomy as the registered one, the object every reader shares, its properties the taxonomy's row. @return array<string, mixed> |
| static | settlesRewrites(): bool | Whether a registered rewrite settles into its full form: with pretty permalinks or in the admin; otherwise it stays as given. |
| method | unregisterTaxonomy(string $name): bool | Forgets a non-builtin taxonomy. |
| method | addObjectType(string $taxonomy, string $type): bool | Attaches a taxonomy to a post type. |
| method | removeObjectType(string $taxonomy, string $type): bool | Detaches a taxonomy from a post type. |
| method | registerStatus(string $name, array $args): array | Registers a post status. @param array<string, mixed> $args |
| inside | rowOf(), objectFrom(), derivedLabels() |
RewriteRules final class
public/minn/src/Minn/Runtime/RewriteRules.php · 334 lines · Open the class →
The rewrite rules WordPress makes from its structures, as the reference makes them (probes rewrite-generate, rewrite-rules), so plugins that read, filter or add to them see the same list.
A structure is walked a directory at a time (one at a time only when asked), each level that names something getting its rules: its feeds and embed, its pages, its comment pages (for posts and pages), a static front page's comment pages at the root, the endpoints placed there, and the level itself; deepest level first. The level that names a post (a name, an id, a page path, or a plugin post type's own tag) is the post's: its embed and trackback, its feeds, pages and comment pages, its endpoints and its attachments', then the post with its page number, between the rules for attachments under it (by /attachment/ and, but for a hierarchical type, directly). A tag nobody registered stays as written.
The full list is the plugins' top rules (each permastruct's rules join them as they are made, and stay), the fixed files, then root, comments, search, author and dates, then pages and posts (posts first unless the structure begins ambiguously), then the plugins' bottom rules; each section through its filter, the whole through generate_rewrite_rules and rewrite_rules_array.
| static | generate(WP_Rewrite $rewrite, array $args): array | generate_rewrite_rules: the rules for one structure. @param array{struct: string, ep_mask?: int, paged?: bool, feed?: bool, forcomments?: bool, walk_dirs?: bool, endpoints?: bool} $args@return array<string, string> |
| static | all(WP_Rewrite $rewrite): array | rewrite_rules: every rule, assembled and filtered. @return array<string, string> |
| static | unfiltered(WP_Rewrite $rewrite): array | The rules as the engine itself would make them, in their stored form and with no plugin's filter or action on them: what the stored rules are when no plugin changed them. The site's rewrite is left as it is. @return array<string, string> |
| static | builtins(WP_Rewrite $rewrite): void | The built-in taxonomies' tags and patterns, set once on the site's rewrite under the front of the structure then; a structure set later leaves them, and a plugin's, as they were (probe registry-rewrites). |
| static | typeArchive(WP_Rewrite $rewrite, string $name, array $type): array | A post type's archive rules, which join the top rules as it registers (probe cpt-rules): its archive slug (the rewrite slug when the archive is just true) under the front of the structure, or the root when the type opts out of the front; the archive, its two feed rules when the type has feeds, its pages when it has those. @param array<string, mixed> $type the registered type@return array<string, string> |
| static | verbosePages(string $structure): bool | Whether a structure begins with a tag that could be a page's path too (use_verbose_page_rules). |
| inside | assemble(), queries(), namesPost(), dirMask(), plainLevel(), postLevel(), attachments(), feeds(), pagesAndComments(), endpoints() |
Runtime final class
public/minn/src/Minn/Runtime/Runtime.php · 410 lines · Open the class →
The WordPress runtime the engine offers plugin code: the procedural facade under minn/wp-api/ plus the services it delegates to. One per request; the facade reaches it through these statics.
| new | __construct(Minn\Context $context, bool $isAdmin = false) | The runtime for one request. Everything about the request itself comes from the context; the fields below it are the same values, kept as properties because plugin code reaches for them by name. |
| prop | readonly Minn\Db $db | The database door this request answers through. |
| prop | readonly Minn\Content\Site $site | The site's options. |
| prop | readonly ?Minn\Http\Request $request | The request being answered, absent on the command line. |
| prop | Minn\Content\Reader $reader | Who reads this request; settled again (identify()) once plugins have said who the user is. |
| prop | readonly Minn\Auth\Capabilities $capabilities | The capability engine. |
| prop | readonly string $engineDir | The minn/ folder: the engine's own files. |
| prop | readonly string $absPath | The site root with a trailing slash. |
| prop | readonly string $version | The WordPress release whose contracts the runtime speaks. |
| prop | readonly Minn\Context $context | |
| prop | readonly bool $isAdmin | |
| method | identify(Minn\Content\Reader $reader): void | The request's reader from now on: the user plugin code named through determine_current_user (Runtime\CurrentUser). |
| method | useSeams(Minn\Extension\SeamRunner $seams): void | Holds the extension seams this request registered, so nothing static has to. |
| method | seams(): ?Minn\Extension\SeamRunner | The extension seams, or null before the front has registered any (REST and the CLI never do). |
| method | renderState(): Minn\Blocks\RenderState | The render state for this request, made on first use: one set of counters for everything rendered. |
| method | useRenderState(Minn\Blocks\RenderState $state): void | Makes a render state this request's, so a renderer that brought its own is the one the leaves read. |
| method | blockRenderer(): Minn\Blocks\Renderer | The block renderer for this request, made on first use over this request's own database door. |
| static | boot(self $runtime): self | Makes this request's runtime the one the facade sees and defines the facade. Deliberately does NOT start the facade's registries empty. A process answering a second request would then have a fresh hook table that no plugin can fill again: plugin files register their hooks as they are included, and an include happens once per process. Until a plugin's registrations can be replayed, a second boot inherits the first's registries on purpose, which is why a worker runtime is not yet something the engine claims (see contracts/runtime.md). |
| static | current(): self | The booted runtime; throws when there is none. |
| static | booted(): bool | Whether the runtime is up. |
| static | hooks(): Minn\Runtime\Hooks | The hook registry. |
| static | options(): Minn\Runtime\Options | The options store. |
| static | storeOptions(Minn\Db $db): void | The options store for a connection, made as soon as the request has one (Engine::answer) and kept by the boot that follows: the reads made while routing come from the same autoloaded list as everything after, and Db::option() reads through it from then on. |
| static | installing(): bool | Whether this request is installing (wp_installing()); false before a runtime boots. |
| static | optionWritten(string $name): void | Tells the option store about a row the engine wrote behind it; nothing to do when no store was made. |
| static | capture(string $action, array $args = array ( )): string | Runs an action and returns what it printed. Plugin callbacks may open output buffers of their own during the action (a page post-processor started in wp_head) or close one they think is theirs (the same plugin in wp_footer); a sentinel buffer under the capture keeps the output either way: extra buffers are flushed through their handlers into the capture, and a capture closed early lands in the sentinel. |
| static | contentFilter(string $content): string | Content the engine rendered itself, through the_content for everything else hooked there. |
| static | cache(): Minn\Runtime\ObjectCache | The object cache. |
| static | textDomains(): Minn\I18n\TextDomains | The text domains loaded for this request. |
| static | locales(): Minn\I18n\LocaleStack | The locales this request switched into. |
| static | shortcodes(): Minn\Runtime\Shortcodes | The shortcode registry. |
| static | scriptModules(): Minn\Runtime\ScriptModules | The script modules registry. |
| static | interactivity(): Minn\Runtime\Interactivity | The interactivity API. |
| static | blockTemplates(): Minn\Runtime\BlockTemplates | The registered block templates. |
| static | registry(): Minn\Runtime\Registry | The post types, taxonomies, and statuses. |
| static | postQuery(): Minn\Runtime\PostQuery | A fresh post query over the runtime's registry. |
| method | get(string $key, mixed $default = NULL): mixed | A per-request state value. |
| method | set(string $key, mixed $value): void | Sets a per-request state value. |
| method | isSecure(): bool | Whether the request is over HTTPS. |
| method | contentDir(): string | wp-content under the site root. |
| static | loadFacade(string $engineDir): void | Defines the facade functions once; safe to call again. |
| static | loadPluggables(): void | The pluggable functions, once the plugins have had their chance to define their own: each is defined only where no plugin did. The plugin loader calls this before plugins_loaded; a boot that loads no plugins calls it straight away. |
| static | reset(): void | Fresh per-request state, for suites. |
| inside | loadObjectCacheDropin() |
ScriptLoading final class
public/minn/src/Minn/Runtime/ScriptLoading.php · 82 lines · Open the class →
How a script may load, as the reference decides it (probe dependencies-classes). A script keeps its own strategy (defer or async) unless it has inline code after it, which makes it blocking, or an enqueued dependent (however deep, through dependents that are themselves enqueued) loads more strictly: blocking before defer before async. Its fetch priority rises to the highest among those dependents: low, then auto, then high. A delayed script that cannot be delayed (inline code after it) waits for the footer when nothing enqueued depends on it. The values a script may be given are checked here too.
| new | __construct(Minn\Runtime\Assets $assets) | |
| static | refusal(string $handle, string $key, mixed $value): ?array | Why a strategy or fetch priority value is refused, as the notice the reference gives; null when the value is fine (empty is fine). @return array{0: string, 1: string}|null the message and the version |
| method | strategy(string $handle, array $checked = array ( )): string | The strategy a script loads with: 'defer', 'async', or '' for blocking. @param array<string, true> $checked @param array<string, true> $checked |
| method | heldForFooter(string $handle): bool | Whether the head pass holds a script for the footer: one that asked to be delayed but has inline code after it, with nothing enqueued that depends on it. |
| method | priority(string $handle): array | A script's own fetch priority and the one it loads with. @return array{0: string, 1: string} @return array{0: string, 1: string} |
| inside | raised(), ownPriority(), enqueuedDependents() |
ScriptModules final class
public/minn/src/Minn/Runtime/ScriptModules.php · 303 lines · Open the class →
The script modules registry: registrations with typed dependencies, the queue, and the four things a page prints from it (the import map, module preloads, the module tags split head/footer, and per-module JSON data). Behaviour pinned by the script-modules probe fixture.
| new | __construct(Closure $url, Closure $data) | @param Closure(string, string|false|null): string $url turns a src and version into the printed URL@param Closure(string, array<string, mixed>): array<string, mixed> $data applies the module data filter |
| method | register(string $id, string $src, array $deps, string|false|null $version, array $args): void | Registers a module by id. @param list<string|array{id: string, import?: string}> $deps |
| method | enqueue(string $id, string $src, array $deps, string|false|null $version, array $args): void | Queues a module, registering it when a source is given. |
| method | dequeue(string $id): void | Removes a module from the queue. |
| method | deregister(string $id): void | Forgets a module. |
| method | setFetchpriority(string $id, string $priority): bool | Sets a module's fetch priority. |
| method | moveToFooter(string $id): bool | Moves a module to the footer or the head. |
| method | moveToHead(string $id): bool | Prints a module in the head; false when it is not registered. |
| method | queue(): array | The module ids queued. @return list<string> |
| method | registered(string $id): ?array | One registered module, or null. @return array{src: string, version: string|false|null, dependencies: list<array{id: string, import: string}>, in_footer: bool, fetchpriority: string}|null |
| method | printImportMap(): string | The import map script tag. |
| method | printPreloads(): string | The modulepreload links. |
| method | printHead(): string | The head's module tags. |
| method | printFooter(): string | The footer's module tags. |
| method | printData(): string | The script-module-data tags. |
| method | printA11y(): string | The a11y module's tag, once. |
| inside | placeIn(), printTags(), marked(), complete(), dependencies(), urlOf(), attr(), wrong() |
ScriptPack final class
public/minn/src/Minn/Runtime/ScriptPack.php · 146 lines · Open the class →
The site-supplied script pack: the `wp-*` JavaScript packages the engine does not reimplement, which a few plugins' front ends need (WooCommerce's block cart and checkout are the reason it exists).
These packages are GPL, so the engine never ships them: the SITE installs them into its own `wp-content`, the way it installs a language pack, and the engine registers the handles only when they are actually there. A site owner adding GPL files to a site that already runs GPL plugins is not the engine distributing GPL; nothing under `minn/` changes.
The engine's own MIT packages under `minn/assets/wp` always win: the pack fills the gaps around them, it does not replace them.
| const | RELATIVE_DIR = 'minn-packages/wp-scripts' | Under the site's wp-content, so backups and migrations carry it. |
| const | MANIFEST = 'script-loader-packages.php' | |
| static | dir(string $contentDir): string | Where the script pack lives under wp-content. |
| static | installed(string $contentDir): bool | Whether the script pack is on disk. |
| static | handles(string $contentDir): array | Every handle the pack can register: the packages manifest keyed by `name.js` becomes `wp-name`, plus the three vendor handles. A handle whose file is missing from the pack is skipped rather than registered against a 404. @return array<string, array{file: string, deps: list<string>, ver: string}> |
| static | installFromTree(string $contentDir, string $tree): array | Copies the packages out of a WordPress tree (or an unpacked core download) into the site's pack directory. Only the built JavaScript and the manifest are taken, and only from the paths they live at, so a wrong source folder copies nothing rather than something odd. @return array{files: int, dir: string} |
| static | remove(string $contentDir): bool | Removes the pack; the engine's own packages keep working without it. |
| inside | stamp() |
Shortcodes final class
public/minn/src/Minn/Runtime/Shortcodes.php · 274 lines · Open the class →
The shortcode registry plugin code fills with add_shortcode, and the expansion do_shortcode performs: [tag attrs], [tag attrs/], [tag]…[/tag] (the first closing tag wins; content is not expanded again), [[tag]] as the literal, unregistered tags left as written.
| const | ATTRIBUTES = '/([\\w-]+)\\s*=\\s*"([^"]*)"(?:\\s|$)|([\\w-]+)\\s*=\\s*\'([^\']*)\'(?:\\s|$)|([\\w-]+)\\s*=\\s*([^\\s\'"]+)(?:\\s|$)|"([^"]*)"(?:\\s|$)|\'([^\']*)\'(?:\\s|$)|(\\S+)(?:\\s|$)/' | The attribute pattern shortcode_parse_atts reads by (get_shortcode_atts_regex). |
| method | tags(): array | The registry itself, by reference, so the $shortcode_tags global plugin code reads and copies is this array and not a snapshot of it. @return array<string, callable> |
| method | add(string $tag, callable $callback): void | Registers a shortcode. |
| method | remove(string $tag): void | Forgets a shortcode. |
| method | removeAll(): void | Forgets every shortcode. |
| method | has(string $tag): bool | Whether a shortcode is registered. |
| method | all(): array | Every shortcode with its callback. @return array<string, callable> every registered tag and its handler, to restore after a narrowed run |
| method | restore(array $tags): void | Replaces the registry, after a save-and-restore. @param array<string, callable> $tags |
| method | names(): array | Every shortcode name. @return list<string> |
| method | pattern(?array $tags = NULL): ?string | The regex matching the registered shortcodes, or null for none. |
| method | apply(string $content, string $tags = 'run'): string | Content with its shortcodes run (do_shortcode, probe shortcode-run): only the registered names the text holds; inside HTML tags first (inTags: run, or with 'escape' as with ignore_html, left alone), then in the text, each through run(); the brackets set aside as entities come back at the end. Images rendered meanwhile see the do_shortcode context. |
| method | run(array $m): string | One matched shortcode (do_shortcode_tag): [[tag]] gives the tag as written; otherwise pre_do_shortcode_tag may answer, or the callback runs with the attributes, the content ("" when it closes itself) and the name, and do_shortcode_tag sees the output in its brackets. @param array<int, string> $m the pattern's match |
| method | inTags(string $content, string $tags, array $names): string | Shortcodes inside HTML tags (do_shortcodes_in_html_tags), run before the text: a quoted attribute value's shortcode runs and the attribute is judged again by kses, kept when anything is left; an unquoted one (or one standing as a name or as the tag) runs as written. Comments, CDATA and, with 'escape', every tag are left alone. Brackets left in a tag (and entities already written for them) are set aside so the text pass cannot reach them. @param list<string> $names |
| method | strip(string $content): string | Content with its shortcodes taken out (strip_shortcodes): the names strip_shortcodes_tagnames leaves, never inside a tag; [[tag]] gives the tag as written. |
| static | stripped(array $m): string | One matched shortcode taken out (strip_shortcode_tag): its outer brackets stay, [[tag]] gives the tag. @param array<int, string> $m @param array<int, string> $m |
| static | parse(string $text): array | Shortcode attribute text as an array (shortcode_parse_atts): no-break and zero-width spaces read as spaces; name=value pairs (names lower case) and bare values in order, each a token followed by whitespace or the end, backslash escapes undone; a value holding "<" that is not whole tags becomes "". @return array<int|string, string> |
| inside | present(), attributesRun() |
StoredObjects final class
public/minn/src/Minn/Runtime/StoredObjects.php · 64 lines · Open the class →
The classes a stored blob may name and come back as. The serialized reader never instantiates; the facade registers one factory per value class it owns (WP_Post, WP_Term, WP_Comment) and a record naming any other class stays a stdClass of its properties. That is what lets a transient the reference wrote, holding post objects, read back typed.
| static | register(string $class, Closure $factory): void | Registers what a record naming this class becomes. |
| static | reviver(): Closure | The reviver the serialized reader takes: PHP's array wrappers rebuilt, a factory's object, or the properties as they are. |
| static | knows(string $class): bool | Whether a factory is registered for the class. |
| static | reset(): void | Forgets every factory, for suites. |
| inside | arrayWrapper() |
SymbolGap final readonly class
public/minn/src/Minn/Runtime/SymbolGap.php · 196 lines · Open the class →
The part of the reference's interface the runtime does not answer: names in data/api-names.json that no facade file defines. It is the whole input the symbol gate needs, so it can be exported to JSON and carried to a machine that holds plugin source but no engine, which is how the catalogue-wide compatibility scan runs.
| prop | readonly array $functions | |
| prop | readonly array $classes | |
| prop | readonly array $known | |
| prop | readonly array $pluggable | |
| static | ofLoadedFacade(string $engineDir): self | Reads the reference's interface and subtracts everything the loaded facade defines. A plugin may define a name as it loads, so each call asks again, but only about the names still lacking: a name once defined stays defined, and asking about all four thousand for every plugin was a cost of its own. |
| static | ofShippedFacade(string $engineDir): self | The gap read from the engine's own files without loading them, for a machine that has the engine but runs no site with it yet (preflight): a name counts as defined when a facade file declares it. |
| static | fromFile(string $path): self | The gap read from its JSON file. |
| method | lacksFunction(string $name): bool | Whether the runtime lacks a function. |
| method | defines(string $name): bool | Whether the runtime already defines a function of the reference's interface, so a plugin declaring it again without a guard would fail to compile. The pluggable functions are not counted: the runtime defines those after the plugins load, each only where no plugin did. |
| method | lacksClass(string $name): bool | Whether the runtime lacks a class. |
| method | json(): string | The gap as JSON. |
| inside | typeExists(), declaredIn() |
SymbolTable final class
public/minn/src/Minn/Runtime/SymbolTable.php · 69 lines · Open the class →
What a folder's PHP names, collected while its tokens are read: the functions it calls, the classes it references, and what it declares or guards itself, so the gate can subtract those before judging it.
| method | call(string $name): void | Notes a function called. |
| method | classRef(string $name): void | Notes a class referenced. |
| method | declare(string $function): void | Notes a function the folder declares, a method included: a call by that name is not a need. |
| method | declareGlobal(string $function): void | Notes a function declared in the global scope, which the runtime must not already define. |
| method | declareClass(string $class): void | Notes a class the folder declares. |
| method | guard(string $name): void | A name an existence check protects: function_exists, class_exists, defined, and the rest. |
| method | toArray(bool $truncated): array | The table as the gate reads it. @return array{calls: list<string>, classes: list<string>, declared: array<string, true>, declaredGlobal: array<string, true>, declaredClasses: array<string, true>, guarded: array<string, true>, truncated: bool} |
Symbols final class
public/minn/src/Minn/Runtime/Symbols.php · 391 lines · Open the class →
A static read of what a plugin's PHP calls: global functions and classes it uses but does not itself declare, minus the ones it guards with function_exists() or class_exists(). Checked against what the runtime provides, this decides whether a plugin loads at all.
The read is cached in the minn_runtime_symbols option, keeping only the reference's own names (all a verdict reads). An entry is trusted while the folder's fingerprint (its own time and its top-level files' newest) holds, for up to FULL_CHECK_EVERY seconds; then the whole folder is walked again, as an update replaces it. Installing, updating or activating a plugin walks its folder at once (recheck()). Walking every folder on every request cost hundreds of milliseconds on a site with many plugins.
| static | missing(string $dir, Minn\Runtime\Options $options): array | What a plugin folder needs that the runtime lacks, cached by mtime. @return array{functions: list<string>, classes: list<string>, redeclares: list<string>, files: int, truncated: bool} |
| static | recheck(string $dir, Minn\Runtime\Options $options): void | The folder is walked again at its next check instead of trusted on its fingerprint: an install, an update or an activation never waits out the window. The walk still keeps the cached read when nothing changed. |
| static | redeclaresIn(string $file): array | The functions one file declares in the global scope, unguarded, that the loaded runtime already defines: including that file would not compile. @return list<string> |
| static | missingAgainst(string $dir, Minn\Runtime\SymbolGap $gap): array | The same read against an exported gap instead of the running engine, so a folder can be judged with no database, no options, and no facade loaded. @return array{functions: list<string>, classes: list<string>, redeclares: list<string>, files: int, truncated: bool} |
| static | survey(string $dir, Minn\Runtime\SymbolGap $gap, array $deadEnds): array | What a folder needs and what it reaches that Minn answers with nothing, in one read, for a machine judging a site before the engine runs it (preflight): the gate's verdict against the gap given, and the dead ends it calls (the wp-admin screens, the editors, the Customizer, XML-RPC, what was left out), by area. A call the folder guards or answers itself is not counted, as the gate counts it. @param array{functions?: array<string, string>, classes?: array<string, string>} $deadEnds name => area, as data/deadend-symbols.json holds them@return array{functions: list<string>, classes: list<string>, redeclares: list<string>, files: int, truncated: bool, deadEnds: array<string, list<string>>} |
| inside | keep(), fingerprint(), namesStamp(), reduced(), verdict(), phpFiles(), scan(), scanTokens(), noteName(), significant() |
TagEditor final class
public/minn/src/Minn/Runtime/TagEditor.php · 152 lines · Open the class →
Edits one start tag's attributes in place the way the reference's tag processor does: a replaced value keeps its position, a new attribute goes right after the tag name (after any earlier insertion), and a removal takes only the attribute's own text.
| new | __construct(string $tag, array $attrs) | @param list<array{name: string, value: ?string, start: int, end: int}> $attrs offsets relative to the tag |
| method | get(string $name): ?string | An attribute's value, or null. |
| method | has(string $name): bool | Whether the tag has an attribute. |
| method | set(string $name, string|bool $value): void | true sets a bare boolean attribute; a URL attribute (href, src, ...) keeps only a URL whose scheme is allowed, as the reference's set_attribute keeps it. |
| method | remove(string $name): void | Removes an attribute. |
| method | addClass(string $class): void | Adds or removes a class. |
| method | removeClass(string $class): void | Removes a class, and the attribute when it was the last one. |
| method | setStyle(string $property, ?string $value): void | Sets or removes one inline style property. |
| method | html(): string | The tag as edited. |
| inside | classes(), setClasses(), splice() |
TermEvents final readonly class
public/minn/src/Minn/Runtime/TermEvents.php · 114 lines · Open the class →
What the reference's REST terms controller tells plugins, for the engine's own: with plugins loaded a term is saved as that controller saves one (rest_pre_insert_{taxonomy}, then the runtime's wp_insert_term or wp_update_term, and wp_delete_term), and the REST actions follow. Without a booted runtime each write is the engine's own.
| method | live(): bool | Whether plugins are loaded to be told anything. |
| method | restCreate(string $taxonomy, array $body, Minn\Http\Request $request): int | A term created over REST with plugins loaded, as the reference's controller creates one (probe rest-term-save): the request's fields as the prepared term, through rest_pre_insert_{taxonomy}, into wp_insert_term; a refusal is its REST error. @param array<string, mixed> $body |
| method | restUpdate(int $termId, string $taxonomy, array $body, Minn\Http\Request $request): void | A term changed over REST with plugins loaded: the fields the request sends, through rest_pre_insert_{taxonomy}, into wp_update_term (when any are left); a refusal is its REST error. @param array<string, mixed> $body |
| method | delete(int $termId, string $taxonomy, array $data, Minn\Http\Request $request, Closure $quietly): void | Deletes a term, then tells plugins over REST with the term as it was and the response. @param array<string, mixed> $data the response@param Closure(): void $quietly the engine's own delete |
| method | restSaved(int $termId, string $taxonomy, Minn\Http\Request $request, string $verb): void | rest_insert_{taxonomy}, then rest_after_insert_{taxonomy}, with the term as it stands and the request. |
| inside | prepared(), refusal() |
TermFields final class
public/minn/src/Minn/Runtime/TermFields.php · 65 lines · Open the class →
A term's fields in a context, as the reference's sanitize_term_field treats them (probe term-sanitize): the numeric fields are whole numbers, never below zero, in every context, and raw stops there. edit runs edit_term_{field} and edit_{taxonomy}_{field}; db runs pre_term_{field} and pre_{taxonomy}_{field}, where the saving defaults live, and a slug also pre_category_nicename; rss runs term_{field}_rss and {taxonomy}_{field}_rss; any other context runs term_{field} and {taxonomy}_{field} with the context named. Then a text field is escaped for where it goes: a form (edit), an attribute, a script (js).
| const | FIELDS = array ( 0 => 'term_id', 1 => 'name', 2 => 'description', 3 => 'slug', 4 => 'count', 5 => 'parent', 6 => 'term_group', 7 => 'term_taxonomy_id', 8 => 'object_id', ) | The fields sanitize_term runs, in its order. |
| static | field(string $field, mixed $value, int $termId, string|false $taxonomy, string $context): mixed | One field's value in a context; a lookup with no taxonomy names false, and its filters get false. |
| static | term(object|array $term, string $taxonomy, string $context): object|array | A term (object or array) with each of its fields in the context, and its filter set to the context; the term's own id goes to the filters. |
| inside | filtered(), saving() |
TermOrder final class
public/minn/src/Minn/Runtime/TermOrder.php · 61 lines · Open the class →
A term query's ORDER BY as the reference writes it (probe wp-term-query-sql): term and taxonomy columns, the relationship's order, the order include or slug lists give, none, or the name; then get_terms_orderby, and after it a meta key or meta_value[_num] when the query has meta clauses.
| static | direction(mixed $order): string | ASC when asked for, DESC for anything else. |
| static | clause(WP_Term_Query $query, string $raw): string | The ORDER BY body for an orderby value, filtered as the reference filters it. |
| static | meta(WP_Term_Query $query, string $orderby): string | A meta key, meta_value[_num] or a named meta clause as SQL (parse_orderby_meta), or '' when the query has no such clause. |
TermQuery final readonly class
public/minn/src/Minn/Runtime/TermQuery.php · 376 lines · Open the class →
Term reads in the shapes plugin code asks for: get_terms() arguments to rows, the tree filters (child_of, exclude_tree), the fields shapes, and the single-term lookups. Behaviour pinned by contracts/fixtures/api/content.json.
| const | DEFAULTS = array ( 'taxonomy' => NULL, 'object_ids' => NULL, 'orderby' => 'name', 'order' => 'ASC', 'hide_empty' => true, 'include' => array ( ), 'exclude' => array ( ), 'exclude_tree' => array ( ), 'number' => '', 'offset' => '', 'fields' => 'all', 'count' => false, 'name' => '', 'slug' => '', 'term_taxonomy_id' => '', 'hierarchical' => true, 'search' => '', 'name__like' => '', 'description__like' => '', 'pad_counts' => false, 'get' => '', 'child_of' => 0, 'parent' => '', 'childless' => false, 'cache_domain' => 'core', 'update_term_meta_cache' => true, 'meta_query' => '', 'meta_key' => '', 'meta_value' => '', ) | |
| new | __construct(Minn\Db $db, Closure $slug) | @param Closure(string): string $slug the slug sanitiser, so the reference's filters apply |
| method | row(int $termId, ?string $taxonomy): ?array | A term row joined with its taxonomy row, by id (and taxonomy when known). @return array<string, mixed>|null @return array<string, mixed>|null |
| method | find(string $field, mixed $value, ?string $taxonomy): ?array | The term id and taxonomy a field value names; null when nothing matches or the field is not one a term can be found by. @return array{term_id: int, taxonomy: string}|null |
| method | exists(string|int $term, ?string $taxonomy, ?int $parent): ?array | Whether a term (by id, or by slug or name) exists, optionally under a taxonomy and parent; the ids when it does. @return array{term_id: int, term_taxonomy_id: int}|null |
| method | lookup(string $field, string $value, ?string $taxonomy, ?int $parent): ?array | The term whose slug or name (as stored) is exactly the value, optionally in a taxonomy and under a parent (0 for the top level). @return array{term_id: int, term_taxonomy_id: int}|null |
| method | hasChildren(int $parent, ?array $taxonomies): bool | Whether any term in the taxonomies (any taxonomy for null) sits under the parent: the reference answers a lookup under a childless parent with nothing before it looks. @param list<string>|null $taxonomies |
| method | slugTaken(string $slug, int $exceptTermId): bool | Whether a term other than the one named holds the slug, in any taxonomy. |
| method | normalise(array $args): array | get_terms() arguments normalised: the "get all" shortcut, integer lists, sanitised slugs. |
| method | rows(array $args, ?array $taxonomies): array | The term rows the arguments match, ordered and paged, with the tree filters applied. @param list<string>|null $taxonomies@return list<array<string, mixed>> |
| method | hierarchy(string $taxonomy): array | Every parent in a taxonomy that has children, mapped to its children's ids. A taxonomy nobody nested reads as an empty map. @return array<int, list<int>> |
| method | children(int $termId, string $taxonomy): array | Every term id under a term, however deep. @return list<int> @return list<int> |
| method | objectsIn(array $termIds, array $taxonomies, string $order): array | The object ids attached to any of the terms in any of the taxonomies. @param list<int> $termIds @param list<string> $taxonomies @return list<int> @param list<int> $termIds @param list<string> $taxonomies @return list<int> |
| inside | readRow(), idList(), descendants(), where(), metaClauses(), ids(), like() |
TermQueryRunner final class
public/minn/src/Minn/Runtime/TermQueryRunner.php · 255 lines · Open the class →
WP_Term_Query::get_terms as the reference runs it (probe wp-term-query-sql): the variables parsed and handed to pre_get_terms, get_terms_args, the WHERE pieces (taxonomies, inclusions, exclusions with excluded trees and childless terms through list_terms_exclusions, names, slugs, term taxonomy ids, likes, objects, parent, counts, search, meta), the order through get_terms_orderby, the fields through get_terms_fields, all of it through terms_clauses, terms_pre_query, and the results shaped by TermQueryTree (children, padded counts, the empty hidden, the page).
| method | run(WP_Term_Query $query, object $wpdb): mixed | Runs a term query object's variables: its terms in the shape the fields ask for, a count, or what a plugin answered first. |
| inside | lists(), hierarchical(), settle(), inHierarchy(), order(), clauses(), exclusions(), names(), likes(), select(), limits(), meta() |
TermQueryTree final class
public/minn/src/Minn/Runtime/TermQueryTree.php · 146 lines · Open the class →
What a term query does with its rows, as the reference does it (probe wp-term-query-sql): each row a term through get_term (with the object id a relationship query carries); only child_of's descendants; counts padded with the children's posts; an empty term kept only while one of its children has posts (the rest dropped where they stood, keys and all); a tree's page cut once the tree is walked; then the shape the fields ask for.
| new | __construct(array $taxonomies) | @param list<string> $taxonomies |
| static | populate(array $rows): array | The terms the rows name, keyed as the rows were. @param array<array-key, object> $rows@return array<array-key, \WP_Term> |
| method | settle(array $terms, array $args): array | The terms after the tree has had its say: descendants only, padded counts, empty branches dropped, the page cut. @param array<array-key, \WP_Term> $terms@param array<string, mixed> $args@return array<array-key, \WP_Term> |
| static | pad(array $terms, string $taxonomy): void | Each term's count raised to the published posts in it or under it, as _pad_term_counts does for a hierarchical taxonomy. @param array<array-key, \WP_Term> $terms |
| static | format(array $terms, string $fields): array | The terms in the shape the fields ask for. @param array<array-key, \WP_Term> $terms@return array<array-key, mixed> |
| inside | hasPosts() |
TermSave final class
public/minn/src/Minn/Runtime/TermSave.php · 199 lines · Open the class →
wp_insert_term and wp_update_term in the reference's order (probe term-insert-filters, contracts/runtime.md "Terms plugins can change"). An insert asks pre_insert_term, refuses an empty name or a missing parent, runs every field through its db filters (sanitize_term), refuses a name the parent (or, for tags, the taxonomy) already has, makes the slug unique, hands the row to wp_insert_term_data, writes it (a slug left empty becomes the term's id), puts it in the taxonomy, lets a plugin name a duplicate to keep instead, and tells plugins. An update merges the stored term (slashed, so a backslash in it survives) under the changes, runs the same filters, asks wp_update_term_parent (where a parent that would put the term under itself becomes 0), and refuses a slug a sibling holds.
| static | insert(mixed $term, string $taxonomy, array $args): WP_Error|array | A new term's ids, or why not. @param array<string, mixed> $args@return array{term_id: int, term_taxonomy_id: int}|\WP_Error |
| static | update(int $termId, string $taxonomy, array $changes): WP_Error|array | A changed term's ids, or why not. @param array<string, mixed> $changes@return array{term_id: int, term_taxonomy_id: int}|\WP_Error |
| inside | write(), nameTaken(), updatedSlug(), slugFromId(), row() |
TermWriter final readonly class
public/minn/src/Minn/Runtime/TermWriter.php · 157 lines · Open the class →
The decisions behind wp_delete_term and the object-term relationships: which relationships to add and remove (saves are Runtime\TermSave). The rows themselves come from Content\Terms; the lifecycle actions fire from here in the reference's order. Behaviour pinned by contracts/fixtures/api/content.json.
| new | __construct(Minn\Db $db, Minn\Content\Terms $terms, Minn\Runtime\TermQuery $query, Minn\Content\PostWriter $posts) | |
| method | delete(array $row, string $taxonomy, bool $hierarchical, int $default): array | Deletes a term; objects left without a category fall back to the default one. Returns the object ids the term was attached to. @param array<string, mixed> $row@return list<int> |
| method | relate(int $objectId, array $keep, array $old, string $taxonomy, ?Closure $count = NULL): void | Makes an object's relationships in a taxonomy exactly $keep (or $old plus $keep when appending), in the reference's order: each new relationship between add_term_relationship and added_term_relationship, the counts of those terms, then the ones that went, between delete_term_relationships and deleted_term_relationships, and their counts. $count recounts a list of term_taxonomy ids and tells plugins (wp_update_term_count); without it the taxonomy is recounted quietly. @param list<int> $keep term_taxonomy ids@param list<int> $old the object's current term_taxonomy ids in the taxonomy@param (Closure(list<int>): void)|null $count |
| method | unrelate(int $objectId, array $ttIds, string $taxonomy, ?Closure $count = NULL): bool | Removes the given relationships between delete_term_relationships and deleted_term_relationships, then recounts those terms; true when any row went. @param list<int> $ttIds@param (Closure(list<int>): void)|null $count |
| method | publishedCount(int $ttId, array $statuses = array ( 0 => 'publish', )): int | How many objects in the counted statuses a term holds, as the stored count keeps it (published ones unless a plugin widens the list). @param list<string> $statuses |
| method | split(int $termId, int $ttId): ?array | Gives a taxonomy row that shares its term with another taxonomy a term of its own (the same name, slug and group), and moves its children under it. Null when no other taxonomy holds the term. @return array{0: int, 1: string, 2: list<int>}|null the new term id, the taxonomy, the children moved |
| method | relationshipCount(int $ttId): int | Every object filed under a term, whatever it is: the count a taxonomy of links or other non-posts keeps. |
| method | storeCount(int $ttId, int $count): void | Stores a term's count. |
| method | termIdsOf(array $ttIds): array | The term ids behind term_taxonomy ids, as stored (strings), in the order given. @param list<int> $ttIds @return list<string> @param list<int> $ttIds @return list<string> |
| inside | ttIdOf() |
ThemeSupports final class
public/minn/src/Minn/Runtime/ThemeSupports.php · 183 lines · Open the class →
What a theme supports, as add_theme_support keeps it (probe rest-themes): a bare feature is true, anything else its arguments. html5 lists add up (repeats and all), post thumbnails stay on for every type once on, post formats keep only real formats, the custom logo fills in its defaults at once (flexible both ways when asked for bare), and the custom header and background get theirs as WordPress finishes loading, a header with no width or height made flexible that way. title-tag declared after loading is refused. A block theme supports thumbnails, responsive embeds, editor styles, HTML5 markup and feed links before its own setup runs.
The features themselves (data/theme-features.json, and any a theme registers) say how each is shown in REST: its schema, or the default when the theme does not support it.
| static | all(): array | Every feature the theme has declared, as stored. @return array<string, mixed> @return array<string, mixed> |
| static | add(string $feature, array $args): bool | add_theme_support: false when refused. @param list<mixed> $args @param list<mixed> $args |
| static | remove(string $feature): bool | remove_theme_support: false when the theme did not support it. |
| static | blockThemeDefaults(): void | _add_default_theme_supports: what every block theme supports before its own setup. |
| static | editorDefaults(string $which): void | wp_enable_block_templates and wp_setup_widgets_block_editor: a block theme's templates, and the block widget editor for any theme. |
| static | justInTime(): void | _custom_header_background_just_in_time: the header's and background's defaults filled in once loaded. |
| static | register(string $feature, array $args): WP_Error|true | register_theme_feature: true, or why the feature cannot be registered. |
| static | features(): array | Core's features, then those registered since. @return array<string, array<string, mixed>> @return array<string, array<string, mixed>> |
| static | forRest(WP_REST_Request $request): array | The active theme's supports as wp/v2/themes shows them: each feature shown in REST, shaped by its schema. @return array<string, mixed> @return array<string, mixed> |
| static | prepared(mixed $support, array $config, string $feature, WP_REST_Request $request): mixed | One feature's support as the REST API shows it (the runtime's WP_REST_Themes_Controller::prepare_theme_support): a feature that is not variadic shown by its first value, through its prepare_callback or else cut to its schema. @param array<string, mixed> $config the feature as it was registered |
| inside | unwrapped() |
ThemeSwitch final class
public/minn/src/Minn/Runtime/ThemeSwitch.php · 145 lines · Open the class →
Switching the theme as the reference does it, in two halves. The switch (switch_theme): the leaving theme keeps its widgets and its menu locations are remembered, the three theme options move, the switch is recorded (theme_switched), the arriving theme's mods start with the classic sidebars, only its mods stay autoloaded, then switch_theme. The next load, on init (check_theme_switched): after_switch_theme with the theme that left, whose defaults map the menu locations across (_wp_menus_changed) and look the widgets over, then the rules flushed and the record cleared. Deleting a theme fires delete_theme and deleted_theme around its folder going, and forgets the update check.
| static | switchTo(string $stylesheet): void | switch_theme. |
| static | checkSwitched(): void | check_theme_switched: on the load after a switch, after_switch_theme with the theme that left. |
| static | menusChanged(): void | _wp_menus_changed: the leaving theme's menu locations mapped onto the arriving theme's. |
| static | mapLocations(array $new, mixed $old): array | wp_map_nav_menu_locations: with one location on each side, it is kept; otherwise a location of the same name keeps its menu, then by role, each name of a role in turn hands the next unused location of that role (with a menu) to the arriving locations that name it. @param array<string, int> $new |
| static | delete(string $stylesheet, ?callable $remove = NULL): bool | delete_theme: the folder removed between delete_theme and deleted_theme (by $remove when the caller removes it its own way); false for no name. @param (callable(string): bool)|null $remove |
| inside | removeFolder(), nextOfGroup(), names() |
TreeWalk final class
public/minn/src/Minn/Runtime/TreeWalk.php · 182 lines · Open the class →
The Walker contract's traversal: elements keyed by the walker's db_fields parent/id pair, displayed depth-first through the walker's four element methods, whole or a page of top-level elements at a time (probe comment-walker). The facade Walker delegates here; subclasses override the element methods (or display_element itself, as Walker_Comment does to keep replies past the depth limit at their parent's level) and the dispatch stays virtual.
| static | walk(object $walker, array $elements, int $maxDepth, array $args): string | Walks a tree with a Walker the way the reference does. @param list<object> $elements |
| static | page(object $walker, array $elements, int $maxDepth, int $page, int $perPage, array $args): string | One page of top-level elements, each with its children (paged_walk); no paging when the page is below one or the size negative. Sets the walker's max_pages. Elements whose parent is not in the list are orphans, printed at the top level after the last page's elements; the replies of earlier pages' elements are not orphans. @param list<object> $elements |
| static | roots(object $walker, array $elements): int | How many of the elements have no parent. @param list<object> $elements @param list<object> $elements |
| static | forget(object $walker, mixed $element, array $children): void | An element's children, and theirs through the walker's unset_children, taken out of the children list. @param array<int|string, list<object>> $children |
| static | element(object $walker, mixed $element, array $children, int $maxDepth, int $depth, array $args, string $output): void | Walks one element and its children; replies past the depth limit stay in the list for the walker (or the orphans) to place. @param array<int|string, mixed> $children |
| inside | buckets(), alone(), orphans() |
TypeProps final class
public/minn/src/Minn/Runtime/TypeProps.php · 150 lines · Open the class →
A post type's or taxonomy's properties from the arguments it was built with, as WP_Post_Type::set_props and WP_Taxonomy::set_props fill them (probe type-object-methods): the reference's defaults where an argument is missing, a given value kept as given.
| static | postType(string $name, array $args): array | A post type's properties from its arguments (probe type-object-methods): the reference's defaults where an argument is missing, a given value kept as given. The labels are the type's own; get_post_type_labels makes them whole on the object. @param array<string, mixed> $args@return array<string, mixed> |
| static | taxonomy(string $name, array $objectTypes, array $args): array | A taxonomy's properties from the types it belongs to and its arguments (probe type-object-methods): the reference's defaults where an argument is missing, a given value kept as given. The meta box is the category checklist for a hierarchical taxonomy and the tag box for any other; its input is read as checkboxes for the checklist and as text otherwise. A default term gets a name, slug and description. @param list<mixed> $objectTypes@param array<string, mixed> $args@return array<string, mixed> |
| inside | typeRewrite(), taxonomyRewrite() |
UpdateCounts final class
public/minn/src/Minn/Runtime/UpdateCounts.php · 31 lines · Open the class →
The updates waiting, as wp_get_update_data counts them for the user (probe admin-bar): plugins and themes from their update transients, WordPress itself unless it is current, translations; each only for a user who may update it. WordPress counts its own update only where wp-admin's update functions are loaded; the engine always has them, so it counts it everywhere.
| static | forCurrentUser(): array | The counts and their title (before the wp_get_update_data filter), and the titles one by one. @return array{0: array{counts: array<string, int>, title: string}, 1: array<string, string>} |
Upgrade final class
public/minn/src/Minn/Runtime/Upgrade.php · 349 lines · Open the class →
What WP_Upgrader does with a package (probe upgrader), for the facade's upgraders and so for everything that installs or updates a plugin or a theme. run() is the whole pass: the options through upgrader_package_options, the package downloaded and unpacked into wp-content/upgrade/, put in place by install(), the result through upgrader_install_package_result and told to the skin, and, for a single item, upgrader_process_complete. The steps a subclass may replace (fs_connect, download_package, unpack_package, install_package, clear_destination) are called on the upgrader itself, as the reference calls them, so a plugin's own upgrader class is honoured.
An update names the folder it replaces (hook_extra temp_backup); that copy is moved to wp-content/upgrade-temp-backup/<dir>/<slug> before the destination is cleared, moved back when the new one fails, and removed once the pass is over. The reference keeps it for a weekly event to clear and puts it back at shutdown; Minn keeps nothing and restores at once.
| static | run(WP_Upgrader $upgrader, array $options): mixed | WP_Upgrader::run; false when the filesystem cannot be reached. |
| static | bulk(WP_Upgrader $upgrader, array $items, array $offers, array $how): array|false | The bulk pass of Plugin_Upgrader and Theme_Upgrader: the skin opened, maintenance mode on when an active item is replaced, each item with an offer run as one of many (one without is up to date, answered true), the pass stopped when the filesystem refuses, then maintenance off, the update cache cleaned, upgrader_process_complete told the whole list, and the skin closed. False when the filesystem cannot be reached. @param list<string> $items plugin files or theme folders@param array<string, mixed> $offers item => the offer (its package)@param array{directories: list<string>, maintenance: bool, prepare: \Closure(string): array{0: string, 1: array}, clean: \Closure(): void, complete: array} $how@return array<string, mixed>|false |
| static | install(WP_Upgrader $upgrader, array $args): WP_Error|array | WP_Upgrader::install_package: the unpacked folder chosen (its one folder, or the working folder when it holds several things), offered to upgrader_pre_install and upgrader_source_selection, the destination cleared or refused when something is there, the folder moved in (and walked by the symbol gate at its next check), and the result offered to upgrader_post_install. |
| static | unpack(WP_Upgrader $upgrader, string $package): WP_Error|string | WP_Upgrader::unpack_package, without the package's deletion (the caller's): the upgrade folder emptied of what earlier passes left, the package unzipped into a folder named for it, and an archive that will not open refused in the upgrader's words with the reader's reason. |
| static | clear(WP_Upgrader $upgrader, string $remote): WP_Error|bool | WP_Upgrader::clear_destination: whatever is at the destination removed; true when nothing is left there. |
| static | maintenanceOn(WP_Upgrader $upgrader): void | Maintenance mode on, as a bulk update of active plugins turns it on: the .maintenance file the front door reads. |
| static | maintenanceOff(WP_Upgrader $upgrader): void | Maintenance mode off; said only when it was on. |
| inside | destinationFor(), clearOrRefuse(), move(), copyInto(), report(), setAside(), putBack(), forget(), backedUp(), listing() |
UserEvents final readonly class
public/minn/src/Minn/Runtime/UserEvents.php · 102 lines · Open the class →
What the reference's REST users controller tells plugins, for the engine's own: with plugins loaded an account is written through the runtime's wp_insert_user, wp_update_user and wp_delete_user (wp_set_password, each profile meta, the role, clean_user_cache, user_register and the count, profile_update, delete_user and deleted_user), and the REST actions follow. Without a booted runtime each write is the engine's own, given as a closure.
| method | live(): bool | Whether plugins are loaded to be told anything. |
| method | create(array $userdata, string $role, Minn\Http\Request $request, Closure $quietly): int | Creates an account the way the REST controller does: wp_insert_user with no role, rest_insert_user, then the role added, then rest_after_insert_user. Returns the new id. @param array<string, mixed> $userdata wp_insert_user's fields@param Closure(): int $quietly the engine's own write |
| method | update(int $id, array $userdata, ?string $role, Minn\Http\Request $request, Closure $quietly): void | Updates an account: wp_update_user with the changed fields, the role when one is given, then the two REST actions. @param array<string, mixed> $userdata the fields the request changes@param Closure(): void $quietly the engine's own write |
| method | delete(int $id, ?int $reassign, array $data, Minn\Http\Request $request, Closure $quietly): void | Deletes an account, its posts going to $reassign (or with it when none), then rest_delete_user with the account as it was. @param array<string, mixed> $data the response@param Closure(): void $quietly the engine's own delete |
| inside | prepared() |
UserInsert final readonly class
public/minn/src/Minn/Runtime/UserInsert.php · 122 lines · Open the class →
The decisions behind wp_insert_user: what a new account needs, which email and login collide, the resolved profile fields, and which columns an update changes. The sanitisers and lookups arrive as closures so the reference's filters apply. Behaviour pinned by contracts/fixtures/api/functions.json.
| const | META_KEYS = array ( 0 => 'nickname', 1 => 'first_name', 2 => 'last_name', 3 => 'description', 4 => 'rich_editing', 5 => 'syntax_highlighting', 6 => 'comment_shortcuts', 7 => 'admin_color', 8 => 'use_ssl', 9 => 'show_admin_bar_front', 10 => 'locale', ) | |
| new | __construct(Closure $sanitizeLogin, Closure $sanitizeSlug, Closure $isEmail, Closure $loginTaken, Closure $emailOwner, Closure $roleExists) | @param Closure(string): string $sanitizeLogin@param Closure(string): string $sanitizeSlug@param Closure(string): bool $isEmail@param Closure(string): bool $loginTaken@param Closure(string): int $emailOwner 0 when nobody has it@param Closure(string): bool $roleExists |
| method | resolve(array $userdata, ?array $existing): Minn\Runtime\Refusal|array | The validated, resolved fields, or the refusal the reference gives. @param array<string, mixed> $userdata@param array<string, mixed>|null $existing the current row on an update@return array{login: string, email: string, nicename: ?string, display_name: string, role: ?string}|Refusal |
| static | account(array $userdata, array $resolved, string $defaultRole): array | The fields a new account is created from. @param array{login: string, email: string, nicename: ?string, display_name: string, role: ?string} $resolved @return array<string, mixed> @param array{login: string, email: string, nicename: ?string, display_name: string, role: ?string} $resolved @return array<string, mixed> |
| static | changes(array $userdata, array $existing, array $resolved, Closure $url, Closure $hash): array | The columns an update actually changes. @param array<string, mixed> $existing@param array{login: string, email: string, nicename: ?string, display_name: string, role: ?string} $resolved@param Closure(string): string $url the URL sanitiser@param Closure(string): string $hash the password hasher@return array<string, string> |
UserOrder final class
public/minn/src/Minn/Runtime/UserOrder.php · 43 lines · Open the class →
A user query's ORDER BY keys as the reference writes them (probe wp-user-query-sql): the user columns by their short or full names, the display name, a post count (its subquery joined in), the id, the meta key or meta value, the order include or a name list gives, or a named meta clause; '' for a key it does not know.
| static | direction(mixed $order): string | ASC when asked for, DESC for anything else. |
| static | clause(WP_User_Query $query, string $orderby, object $wpdb): string | The column or expression one orderby key stands for. |
| inside | postCount() |
UserQueryRoles final class
public/minn/src/Minn/Runtime/UserQueryRoles.php · 91 lines · Open the class →
A user query's roles and capabilities as the meta clauses the reference writes for them (probe wp-user-query-sql): a capability matches its own name or any role granting it, capability__in and __not_in widen the role lists, and each role is a LIKE on the site's capabilities meta (NOT LIKE to leave it out).
| new | __construct(string $capabilitiesKey) | |
| method | clauses(array $qv, array $queries, array $roles): array | The meta query's clauses once the roles and capabilities join them. @param array<string, mixed> $qv@param array<array-key, mixed> $queries the meta query's own clauses@param array<string, array{capabilities: array<string, bool>}> $roles the site's roles@return array<array-key, mixed> |
| inside | lists(), firstOf(), like() |
UserQueryRunner final class
public/minn/src/Minn/Runtime/UserQueryRunner.php · 218 lines · Open the class →
WP_User_Query as the reference runs it (probe wp-user-query-sql): the variables filled and handed to pre_get_users; the fields (a column list, ids, the count), published authors, nicenames and logins, the meta query with the roles and capabilities joined in, the order (post counts joined in, include and list orders kept), the limit, a search over the columns its shape suggests (through user_search_columns), ids in or out, the date query, then pre_user_query; the query (users_pre_query may answer it), found_users_query, and the results shaped as the fields ask.
| new | __construct(object $wpdb) | |
| method | prepare(WP_User_Query $query): void | Builds the query's pieces from its variables, as prepare_query does. |
| static | searchSql(string $search, array $columns, string|false $wild): string | A search over columns (get_search_sql): ID matched exactly, the rest by LIKE with wildcards at the leading or trailing end, or both. @param list<string> $columns |
| method | query(WP_User_Query $query): void | Runs the query: users_pre_query may answer it; the count follows; the results take the fields' shape. |
| inside | fields(), published(), names(), meta(), order(), search() |
UserSave final class
public/minn/src/Minn/Runtime/UserSave.php · 74 lines · Open the class →
An account's fields through the filters the reference's wp_insert_user runs, in its order (probe user-insert-filters, contracts/runtime.md "Accounts plugins can change"): the login (pre_user_login), refused when illegal_user_logins names it; the nicename, made from the login for a new account (pre_user_nicename); the email and the URL; the nickname (the login when none), first and last names; the display name, made as the reference makes it when none is given; the description. Each pre_user_* filter carries the reference's sanitisers. Then wp_pre_insert_user_data over the row and insert_user_meta over the meta.
| static | fields(array $userdata, ?array $existing): Minn\Runtime\Refusal|array | The fields after their filters, or the refusal for an illegal login. @param array<string, mixed> $userdata@param array<string, mixed>|null $existing the stored account on an update@return array<string, mixed>|Refusal |
| static | data(array $row, int $userId, array $userdata): array | wp_pre_insert_user_data over the row an insert or update writes: the row, whether it updates, the account's id (null for a new one), what the caller gave. @param array<string, mixed> $row@param array<string, mixed> $userdata@return array<string, mixed> |
| static | meta(array $meta, int $userId, array $userdata): array | insert_user_meta over the profile meta, then insert_custom_user_meta over the meta_input the caller gave beyond it; the two merged. An update is one whose userdata names an ID. @param array<string, mixed> $meta@param array<string, mixed> $userdata@return array<string, mixed> |
WidgetAreas final class
public/minn/src/Minn/Runtime/WidgetAreas.php · 184 lines · Open the class →
The widgets helper functions as the reference answers them (probe widget-helpers): a widget id split into its base and number, a sidebar by id (the inactive widgets are a sidebar too, with a name and nothing else), finding and moving a widget between sidebars, one widget rendered in its sidebar as dynamic_sidebar renders each (an unregistered widget renders nothing), and the sidebars with every registered one present.
| const | INACTIVE = 'wp_inactive_widgets' | |
| static | parse(string $id): array | wp_parse_widget_id: ['id_base' => ..., 'number' => n], or the id alone as its base. @return array{id_base: string, number?: int} @return array{id_base: string, number?: int} |
| static | sidebar(string $id): ?array | wp_get_sidebar: a registered sidebar, the inactive widgets, or null. @return array<string, mixed>|null @return array<string, mixed>|null |
| static | find(string $widgetId): ?string | wp_find_widgets_sidebar: the sidebar holding a widget, or null. |
| static | assign(string $widgetId, string $sidebarId): void | wp_assign_widget_to_sidebar: out of every sidebar, then onto the end of one ('' leaves it in none). |
| static | render(string $widgetId, string $sidebarId): string | wp_render_widget: a registered widget as its sidebar wraps it, through dynamic_sidebar_params; '' otherwise. |
| static | control(string $widgetId): ?string | wp_render_widget_control: a registered widget's settings form, or null. |
| static | retrieve(array $stored, array $areas, array $registered): array | retrieve_widgets (probe widgets-remaining): the inactive list, then every registered sidebar, holding only widgets that are registered. The inactive list takes, in order, the registered widgets no list mentions (the -1 placeholders aside, in the order they were registered), the widgets already inactive, and the widgets of sidebars no longer registered, which go. @param array<string, mixed> $stored the stored lists, sidebar => widget ids@param list<string> $areas the registered sidebars@param list<string> $registered the registered widgets@return array<string, list<string>> |
| static | map(array $existing, array $areas, array $stored, array $registered): array | The old theme's widget lists fitted to the new theme's sidebars (probe sidebars-mapping). One old sidebar and one new map straight across. Otherwise a sidebar of the same id keeps its widgets; then, group by group, the new sidebars whose ids hold one of the group's fragments take, in order, the old ones that do (a new sidebar may take from several groups); what is left goes after the inactive widgets. When nothing is inactive then, the stored inactive widgets that are registered and placed nowhere come back, keys kept. @param array<string, mixed> $existing the old theme's lists@param list<string> $areas the new theme's sidebars@param array<array-key, mixed> $stored the theme's stored inactive widgets@param list<string> $registered the registered widgets@return array<string, array<array-key, mixed>> |
| inside | inGroup(), unmentioned() |
Nothing matches.