API / Minn\Extension

Minn\Extension

The extension contract and its seams. 8 classes, 32 public methods and 2 internals. This page is the public surface; each class name opens the whole class.

Extension interface

public/minn/src/Minn/Extension/Extension.php · 5 lines · Open the class →

What a Minn extension is: a class the engine constructs once per request and asks to register what it provides. Everything it can touch is on the Seams object; there is no global state to reach for.

methodregister(Minn\Extension\Seams $minn): void

Registers the extension's seams.

Extensions final class

public/minn/src/Minn/Extension/Extensions.php · 14 lines · Open the class →

Where the engine looks for the seams an extension registered. The request's runtime holds them, so nothing here keeps state of its own and a request cannot inherit the extensions of the one before it. Before the front has loaded any (REST and the command line never do) there are none.

staticset(Minn\Extension\Seams $seams): void

Installs the seams for this request.

staticrunner(): ?Minn\Extension\SeamRunner

The engine side of the seams, if any.

Loader final class

public/minn/src/Minn/Extension/Loader.php · 129 lines · Open the class →

Finds extensions under wp-content/plugins and wp-content/mu-plugins, decides which are active, autoloads them, and asks each to register. An extension that throws is logged and skipped; the page still renders.

new__construct(string $contentDir, Minn\Content\Site $site)
methodfound(): array

Every extension manifest on disk.

@return list<Manifest> every extension on disk
methodrefresh(): void

Forgets the cached activation list after an option write.

methodactive(): array

The manifests that are active.

@return list<Manifest> the extensions this site has switched on
methoddeclaredTypes(): array

Extra post types declared by active extensions, keyed by slug.

@return array<string, array<string, mixed>>
methodreplacements(): array

Which active WordPress plugin files an extension stands in for. @return array<string, string> plugin file => extension slug

@return array<string, string> plugin file => extension slug
methodregister(Minn\Extension\Seams $seams): void

Registers every active extension, each on seams of its own; one that throws adds nothing.

insideautoload()

Manifest final readonly class

public/minn/src/Minn/Extension/Manifest.php · 78 lines · Open the class →

minn.json, read as data. A folder under wp-content/plugins (or mu-plugins) with this file is an extension; the same folder may also be a WordPress plugin, in which case the two share a name and a version.

{ "name": "Block Visibility for Minn", "version": "1.0.0", "license": "MIT", "replaces": ["block-visibility/block-visibility.php"], "autoload": {"Minn\\Ext\\BlockVisibility\\": "src/"}, "extension": "Minn\\Ext\\BlockVisibility\\Extension", "shortcodes": ["example_tag"], "blocks": ["vendor/block"], "types": [{"slug": "zz_note", "name": "Notes", "rest_base": "zz-note"}], "needs": {"http": ["https://api.example.com/"]} }

"replaces" names the WordPress plugin files whose behaviour this extension stands in for; the extension is active whenever one of them is in active_plugins, or when its own folder is listed there, or when it is named in the minn_active_extensions option. "shortcodes" and "blocks" are the content tokens preflight treats as provided instead of missing. "types" are extra post types the engine should serve on wp/v2. "needs" says what the extension reaches outside the process: "http" is the URL prefixes its $minn->http may request (true for any public host).

new__construct(string $slug, string $dir, string $name, string $version, string $license, array $replaces, array $autoload, string $extension, string $covers = '', array $shortcodes = array ( ), array $blocks = array ( ), array $types = array ( ), ?array $http = NULL)
@param list<string> $replaces
@param array<string, string> $autoload namespace prefix => directory
@param list<string> $shortcodes
@param list<string> $blocks
@param list<array<string, mixed>> $types
propreadonly string $slug
propreadonly string $dir
propreadonly string $name
propreadonly string $version
propreadonly string $license
propreadonly array $replaces
propreadonly array $autoload
propreadonly string $extension
propreadonly string $covers

what of the replaced plugin this provides, when not all of it

propreadonly array $shortcodes
propreadonly array $blocks
propreadonly array $types
propreadonly ?array $http
staticread(string $dir): ?self

A folder's minn.json, or null when there is none.

insidehttp()

Registrations final readonly class

public/minn/src/Minn/Extension/Registrations.php · 25 lines · Open the class →

Everything the extensions registered for this request, handed from Seams to the runner once.

new__construct(array $blockGates, array $blockFilters, array $shortcodes, array $contentFilters, array $head, array $footer, array $bodyClasses, array $documentFilters, ?Closure $title)
@param list<Closure> $blockGates
@param list<Closure> $blockFilters
@param array<string, Closure> $shortcodes
@param list<Closure> $contentFilters
@param list<Closure> $head
@param list<Closure> $footer
@param list<string> $bodyClasses
@param list<Closure> $documentFilters
propreadonly array $blockGates
propreadonly array $blockFilters
propreadonly array $shortcodes
propreadonly array $contentFilters
propreadonly array $head
propreadonly array $footer
propreadonly array $bodyClasses
propreadonly array $documentFilters
propreadonly ?Closure $title

SeamRunner final readonly class

public/minn/src/Minn/Extension/SeamRunner.php · 79 lines · Open the class →

The engine's side of the seams: what the loader collected, called at the right moment and in registration order. An extension never sees this class; it sees Seams, which is only the eight registrations.

new__construct(Minn\Extension\Seams $seams, Minn\Extension\Registrations $registered)
methodallowsBlock(Minn\Blocks\Block $block): bool

Whether every block gate lets a block render.

methodfilterBlock(Minn\Blocks\Block $block, string $html): string

A block's HTML through every block filter.

methodfilterContent(string $html, Minn\Content\PostRecord $post): string

Shortcodes, then the content filters, over rendered post content. An extension's filter is promised the post as a row (contracts/extensions.md), so a record is handed over as one.

methodtitle(string $title): string

The document title through the title filter.

methodhead(): string

What the extensions add to the head.

methodfooter(): string

What the extensions add to the footer.

methodbodyClasses(): array

The body classes the extensions add.

@return list<string>
methodfilterDocument(string $html): string

The whole document through every document filter.

Seams final class

public/minn/src/Minn/Extension/Seams.php · 145 lines · Open the class →

Where an extension can take part in a request: eight typed registrations, the request they run in, and the outgoing HTTP its minn.json asks for. This is the whole surface an extension sees. Each extension registers on its own (forExtension), and what it registered joins the request's once its register() returns (adopt). The engine calls what was registered through SeamRunner, at the right moment and in registration order.

new__construct(Minn\Db $db, Minn\Content\Site $site, Minn\Http\Request $request, Minn\Content\Reader $reader, ?Minn\Http $http = NULL)
propreadonly Minn\Http $http

Outgoing HTTP, bound to the hosts the extension's needs.http lists; a client that throws when it asked for none.

propreadonly Minn\Db $db
propreadonly Minn\Content\Site $site
propreadonly Minn\Http\Request $request
propreadonly Minn\Content\Reader $reader
methodforExtension(Minn\Extension\Manifest $manifest): self

The seams one extension registers on: this request, and the outgoing HTTP its manifest grants. A manifest without needs.http gets a client that throws at its first request, naming the key to add.

methodadopt(self $extension): void

Takes on what one extension registered, after its register() returned, so an extension that throws half-way adds nothing. A closure the engine hands seams to is handed that extension's own.

methodgateBlocks(Closure $gate): void

Decides whether a block renders at all; the first gate to answer false wins.

methodfilterBlocks(Closure $filter): void

Rewrites a block's rendered markup.

methodshortcode(string $tag, Closure $render): void

Renders [tag attr="…"]content[/tag] wherever it appears in post content.

methodfilterContent(Closure $filter): void

Rewrites rendered post content (after blocks and shortcodes).

methodhead(Closure $render): void

Markup for the document head (a style, a meta tag, a script).

methodfooter(Closure $render): void

Markup before </body>.

methodbodyClass(string $class): void

Adds a body class.

methodtitle(Closure $filter): void

Replaces the document title; receives the engine's own.

methodfilterDocument(Closure $filter): void

Rewrites the whole themed document before it is sent (what an output buffer did on the reference).

methodregistrations(): Minn\Extension\Registrations

What was registered, for the engine's runner. Extensions have no reason to call this.

Shortcodes final class

public/minn/src/Minn/Extension/Shortcodes.php · 54 lines · Open the class →

The shortcode syntax in rendered content: [tag], [tag attr="v" flag], [tag]inner[/tag], and [[tag]] as the escaped literal. Unregistered tags stay as written, which is what the reference shows for a plugin that is not installed.

staticapply(string $html, array $registry, Minn\Extension\Seams $seams): string

Content with the registered shortcodes run.

@param array<string, Closure> $registry
staticattributes(string $text): array

A shortcode's attributes parsed, curly quotes included.

@return array<string, string> named attributes; bare words keyed by position