Minn\Http
Request, response, routing, and the outgoing client. 37 classes, 122 public methods and 34 internals. This page is the public surface; each class name opens the whole class.
Access enum
public/minn/src/Minn/Http/Access.php · 17 lines · Open the class →
Who a route is for. The six answers every route gives, so the authorization surface of the engine reads as a list of these.
Cases: Public, SignedIn, Cap, Floor, Own, Type, Taxonomy
Args final class
public/minn/src/Minn/Http/Args.php · 149 lines · Open the class →
The parameters a route accepts, as the reference describes them in the REST index: name => {description, type, ...}. The descriptions, types, enums and bounds were captured from the reference, so a client that reads either index is told the same thing, and the router refuses a value the way the reference refuses it, before it judges the caller.
A route declares only what it really reads. An argument published here that the handler ignores would be worse than none at all, because a client reading the index would build a request around it, so each set below names the code that consumes it. The sets keep the reference's order, because a refusal lists the invalid parameters in that order. `_fields` and `_embed` are read everywhere and declared nowhere, as the reference lists neither and validates neither.
| const | SHARED = array ( 0 => 'context', 1 => 'page', 2 => 'per_page', 3 => 'search', ) | The shared collection parameters, judged first: the reference answers a refusal among these alone, and reads the route's own parameters only once these pass. |
| const | HANDLER_VALIDATES = 'handler_validates' | The key an argument carries when its handler judges it (a status the caller may not read is refused before the enum is). |
| const | CONTEXT = array ( 'context' => array ( 'description' => 'Scope under which the request is made; determines fields present in response.', 'type' => 'string', 'enum' => array ( 0 => 'view', 1 => 'embed', 2 => 'edit', ), 'default' => 'view', 'required' => false, ), ) | Read by Rest\Context::of(): which view of a resource is wanted. |
| const | FORCE = array ( 'force' => array ( 'description' => 'Whether to bypass Trash and force deletion.', 'type' => 'boolean', 'default' => false, 'required' => false, ), ) | Read by Content\PostWriter: whether a delete bypasses the trash. |
| const | USERS = array ( 'page' => array ( 'description' => 'Current page of the collection.', 'type' => 'integer', 'default' => 1, 'minimum' => 1, 'required' => false, ), 'per_page' => array ( 'description' => 'Maximum number of items to be returned in result set.', 'type' => 'integer', 'default' => 10, 'minimum' => 1, 'maximum' => 100, 'required' => false, ), 'search' => array ( 'description' => 'Limit results to those matching a string.', 'type' => 'string', 'required' => false, ), 'exclude' => array ( 'description' => 'Ensure result set excludes specific IDs.', 'type' => 'array', 'items' => array ( 'type' => 'integer', ), 'default' => array ( ), 'required' => false, ), 'include' => array ( 'description' => 'Limit result set to specific IDs.', 'type' => 'array', 'items' => array ( 'type' => 'integer', ), 'default' => array ( ), 'required' => false, ), 'offset' => array ( 'description' => 'Offset the result set by a specific number of items.', 'type' => 'integer', 'required' => false, ), 'order' => array ( 'default' => 'asc', 'description' => 'Order sort attribute ascending or descending.', 'enum' => array ( 0 => 'asc', 1 => 'desc', ), 'type' => 'string', 'required' => false, ), 'orderby' => array ( 'default' => 'name', 'description' => 'Sort collection by user attribute.', 'enum' => array ( 0 => 'id', 1 => 'include', 2 => 'name', 3 => 'registered_date', 4 => 'slug', 5 => 'include_slugs', 6 => 'email', 7 => 'url', ), 'type' => 'string', 'required' => false, ), 'slug' => array ( 'description' => 'Limit result set to users with one or more specific slugs.', 'type' => 'array', 'items' => array ( 'type' => 'string', ), 'required' => false, ), 'roles' => array ( 'description' => 'Limit result set to users matching at least one specific role provided. Accepts csv list or single role.', 'type' => 'array', 'items' => array ( 'type' => 'string', ), 'required' => false, ), 'capabilities' => array ( 'description' => 'Limit result set to users matching at least one specific capability provided. Accepts csv list or single capability.', 'type' => 'array', 'items' => array ( 'type' => 'string', ), 'required' => false, ), 'who' => array ( 'description' => 'Limit result set to users who are considered authors.', 'type' => 'string', 'enum' => array ( 0 => 'authors', ), 'required' => false, ), 'has_published_posts' => array ( 'description' => 'Limit result set to users who have published posts.', 'type' => array ( 0 => 'boolean', 1 => 'array', ), 'items' => array ( 'type' => 'string', 'enum' => array ( ), ), 'required' => false, ), 'search_columns' => array ( 'default' => array ( ), 'description' => 'Array of column names to be searched.', 'type' => 'array', 'items' => array ( 'enum' => array ( 0 => 'email', 1 => 'name', 2 => 'id', 3 => 'username', 4 => 'slug', ), 'type' => 'string', ), 'required' => false, ), ) | Read by Rest\UserCollectionParams: the user collection. |
| const | TERMS = array ( 'page' => array ( 'description' => 'Current page of the collection.', 'type' => 'integer', 'default' => 1, 'minimum' => 1, 'required' => false, ), 'per_page' => array ( 'description' => 'Maximum number of items to be returned in result set.', 'type' => 'integer', 'default' => 10, 'minimum' => 1, 'maximum' => 100, 'required' => false, ), 'search' => array ( 'description' => 'Limit results to those matching a string.', 'type' => 'string', 'required' => false, ), 'exclude' => array ( 'description' => 'Ensure result set excludes specific IDs.', 'type' => 'array', 'items' => array ( 'type' => 'integer', ), 'default' => array ( ), 'required' => false, ), 'include' => array ( 'description' => 'Limit result set to specific IDs.', 'type' => 'array', 'items' => array ( 'type' => 'integer', ), 'default' => array ( ), 'required' => false, ), 'offset' => array ( 'description' => 'Offset the result set by a specific number of items.', 'type' => 'integer', 'required' => false, ), 'order' => array ( 'description' => 'Order sort attribute ascending or descending.', 'type' => 'string', 'default' => 'asc', 'enum' => array ( 0 => 'asc', 1 => 'desc', ), 'required' => false, ), 'orderby' => array ( 'description' => 'Sort collection by term attribute.', 'type' => 'string', 'default' => 'name', 'enum' => array ( 0 => 'id', 1 => 'include', 2 => 'name', 3 => 'slug', 4 => 'include_slugs', 5 => 'term_group', 6 => 'description', 7 => 'count', ), 'required' => false, ), 'hide_empty' => array ( 'description' => 'Whether to hide terms not assigned to any posts.', 'type' => 'boolean', 'default' => false, 'required' => false, ), 'parent' => array ( 'description' => 'Limit result set to terms assigned to a specific parent.', 'type' => 'integer', 'required' => false, ), 'post' => array ( 'description' => 'Limit result set to terms assigned to a specific post.', 'type' => 'integer', 'default' => NULL, 'required' => false, ), 'slug' => array ( 'description' => 'Limit result set to terms with one or more specific slugs.', 'type' => 'array', 'items' => array ( 'type' => 'string', ), 'required' => false, ), ) | Read by Rest\TermCollectionParams: a term collection, either shape. |
| const | COMMENTS = array ( 'page' => array ( 'description' => 'Current page of the collection.', 'type' => 'integer', 'default' => 1, 'minimum' => 1, 'required' => false, ), 'per_page' => array ( 'description' => 'Maximum number of items to be returned in result set.', 'type' => 'integer', 'default' => 10, 'minimum' => 1, 'maximum' => 100, 'required' => false, ), 'search' => array ( 'description' => 'Limit results to those matching a string.', 'type' => 'string', 'required' => false, ), 'after' => array ( 'description' => 'Limit response to comments published after a given ISO8601 compliant date.', 'type' => 'string', 'format' => 'date-time', 'required' => false, ), 'author' => array ( 'description' => 'Limit result set to comments assigned to specific user IDs. Requires authorization.', 'type' => 'array', 'items' => array ( 'type' => 'integer', ), 'required' => false, ), 'author_exclude' => array ( 'description' => 'Ensure result set excludes comments assigned to specific user IDs. Requires authorization.', 'type' => 'array', 'items' => array ( 'type' => 'integer', ), 'required' => false, ), 'author_email' => array ( 'default' => NULL, 'description' => 'Limit result set to that from a specific author email. Requires authorization.', 'format' => 'email', 'type' => 'string', 'required' => false, ), 'before' => array ( 'description' => 'Limit response to comments published before a given ISO8601 compliant date.', 'type' => 'string', 'format' => 'date-time', 'required' => false, ), 'exclude' => array ( 'description' => 'Ensure result set excludes specific IDs.', 'type' => 'array', 'items' => array ( 'type' => 'integer', ), 'default' => array ( ), 'required' => false, ), 'include' => array ( 'description' => 'Limit result set to specific IDs.', 'type' => 'array', 'items' => array ( 'type' => 'integer', ), 'default' => array ( ), 'required' => false, ), 'offset' => array ( 'description' => 'Offset the result set by a specific number of items.', 'type' => 'integer', 'required' => false, ), 'order' => array ( 'description' => 'Order sort attribute ascending or descending.', 'type' => 'string', 'default' => 'desc', 'enum' => array ( 0 => 'asc', 1 => 'desc', ), 'required' => false, ), 'orderby' => array ( 'description' => 'Sort collection by comment attribute.', 'type' => 'string', 'default' => 'date_gmt', 'enum' => array ( 0 => 'date', 1 => 'date_gmt', 2 => 'id', 3 => 'include', 4 => 'post', 5 => 'parent', 6 => 'type', ), 'required' => false, ), 'parent' => array ( 'default' => array ( ), 'description' => 'Limit result set to comments of specific parent IDs.', 'type' => 'array', 'items' => array ( 'type' => 'integer', ), 'required' => false, ), 'parent_exclude' => array ( 'default' => array ( ), 'description' => 'Ensure result set excludes specific parent IDs.', 'type' => 'array', 'items' => array ( 'type' => 'integer', ), 'required' => false, ), 'post' => array ( 'default' => array ( ), 'description' => 'Limit result set to comments assigned to specific post IDs.', 'type' => 'array', 'items' => array ( 'type' => 'integer', ), 'required' => false, ), 'status' => array ( 'default' => 'approve', 'description' => 'Limit result set to comments assigned a specific status. Requires authorization.', 'type' => 'string', 'required' => false, ), 'type' => array ( 'default' => 'comment', 'description' => 'Limit result set to comments assigned a specific type. Requires authorization.', 'type' => 'string', 'required' => false, ), 'password' => array ( 'description' => 'The password for the post if it is password protected.', 'type' => 'string', 'required' => false, ), ) | Read by Rest\CommentsController::list(): the comment collection. |
| const | USER_CREATE = array ( 'username' => array ( 'description' => 'Login name for the user.', 'type' => 'string', 'required' => true, ), 'name' => array ( 'description' => 'Display name for the user.', 'type' => 'string', 'required' => false, ), 'first_name' => array ( 'description' => 'First name for the user.', 'type' => 'string', 'required' => false, ), 'last_name' => array ( 'description' => 'Last name for the user.', 'type' => 'string', 'required' => false, ), 'email' => array ( 'description' => 'The email address for the user.', 'type' => 'string', 'format' => 'email', 'required' => true, ), 'url' => array ( 'description' => 'URL of the user.', 'type' => 'string', 'format' => 'uri', 'required' => false, ), 'description' => array ( 'description' => 'Description of the user.', 'type' => 'string', 'required' => false, ), 'locale' => array ( 'description' => 'Locale for the user.', 'type' => 'string', 'required' => false, ), 'nickname' => array ( 'description' => 'The nickname for the user.', 'type' => 'string', 'required' => false, ), 'roles' => array ( 'description' => 'Roles assigned to the user.', 'type' => 'array', 'items' => array ( 'type' => 'string', ), 'required' => false, ), 'password' => array ( 'description' => 'Password for the user (never included).', 'type' => 'string', 'required' => true, ), ) | Read by Rest\UsersController::create(): the body of a user create. The locale takes any string here; the reference's enum is the site's installed languages, which is not a constant. |
| const | USER_EDIT = array ( 'name' => array ( 'description' => 'Display name for the user.', 'type' => 'string', 'required' => false, ), 'first_name' => array ( 'description' => 'First name for the user.', 'type' => 'string', 'required' => false, ), 'last_name' => array ( 'description' => 'Last name for the user.', 'type' => 'string', 'required' => false, ), 'email' => array ( 'description' => 'The email address for the user.', 'type' => 'string', 'format' => 'email', 'required' => false, ), 'url' => array ( 'description' => 'URL of the user.', 'type' => 'string', 'format' => 'uri', 'required' => false, ), 'description' => array ( 'description' => 'Description of the user.', 'type' => 'string', 'required' => false, ), 'locale' => array ( 'description' => 'Locale for the user.', 'type' => 'string', 'required' => false, ), 'nickname' => array ( 'description' => 'The nickname for the user.', 'type' => 'string', 'required' => false, ), 'slug' => array ( 'description' => 'An alphanumeric identifier for the user.', 'type' => 'string', 'required' => false, ), 'roles' => array ( 'description' => 'Roles assigned to the user.', 'type' => 'array', 'items' => array ( 'type' => 'string', ), 'required' => false, ), 'password' => array ( 'description' => 'Password for the user (never included).', 'type' => 'string', 'required' => false, ), 'meta' => array ( 'description' => 'Meta fields.', 'type' => 'object', 'properties' => array ( 'show_admin_bar_front' => array ( 'type' => 'string', 'default' => 'true', ), ), 'required' => false, ), ) | Read by Rest\UsersController::update(): the body of a user edit; nothing is required. |
| const | USER_DELETE = array ( 'force' => array ( 'type' => 'boolean', 'default' => false, 'description' => 'Required to be true, as users do not support trashing.', 'required' => false, ), 'reassign' => array ( 'type' => 'integer', 'description' => 'Reassign the deleted user\'s posts and links to this user ID.', 'required' => true, 'handler_validates' => true, ), ) | Read by Rest\UsersController::delete(): the reassignment a user delete requires, and the force it insists on. |
| const | APPLICATION_PASSWORD = array ( 'app_id' => array ( 'description' => 'A UUID provided by the application to uniquely identify it. It is recommended to use an UUID v5 with the URL or DNS namespace.', 'type' => 'string', 'oneOf' => array ( 0 => array ( 'type' => 'string', 'format' => 'uuid', ), 1 => array ( 'type' => 'string', 'enum' => array ( 0 => '', ), ), ), 'required' => false, ), 'name' => array ( 'description' => 'The name of the application password.', 'type' => 'string', 'minLength' => 1, 'pattern' => '.*\\S.*', 'required' => true, ), ) | Read by Rest\ApplicationPasswordsController::create(): the body of a new application password. |
| const | SEARCH = array ( 'context' => array ( 'description' => 'Scope under which the request is made; determines fields present in response.', 'type' => 'string', 'enum' => array ( 0 => 'view', 1 => 'embed', ), 'default' => 'view', 'required' => false, ), 'page' => array ( 'description' => 'Current page of the collection.', 'type' => 'integer', 'default' => 1, 'minimum' => 1, 'required' => false, ), 'per_page' => array ( 'description' => 'Maximum number of items to be returned in result set.', 'type' => 'integer', 'default' => 10, 'minimum' => 1, 'maximum' => 100, 'required' => false, ), 'search' => array ( 'description' => 'Limit results to those matching a string.', 'type' => 'string', 'required' => false, ), 'type' => array ( 'default' => 'post', 'description' => 'Limit results to items of an object type.', 'type' => 'string', 'enum' => array ( 0 => 'post', 1 => 'term', 2 => 'post-format', ), 'required' => false, 'handler_validates' => true, ), 'subtype' => array ( 'default' => 'any', 'description' => 'Limit results to items of one or more object subtypes.', 'type' => 'array', 'items' => array ( 'enum' => array ( 0 => 'post', 1 => 'page', 2 => 'category', 3 => 'post_tag', 4 => 'any', ), 'type' => 'string', ), 'required' => false, 'handler_validates' => true, ), ) | Read by Rest\SearchController::list(): the search collection, whose context has no edit view. |
| static | merge(array $sets): array | The sets merged into one map, as a route's endpoint publishes them. @param list<array<string, array<string, mixed>>> $sets@return array<string, array<string, mixed>> |
CertificateName final class
public/minn/src/Minn/Http/CertificateName.php · 56 lines · Open the class →
Whether a TLS certificate names a host: a wildcard only as a whole first label with at least two labels after it, matching exactly one label; an IP address never matches by name; the subjectAltName DNS entries win over the common name when present.
| static | valid(string $reference): bool | Whether a certificate's reference name is one a host can match. |
| static | matches(string $host, string $reference): bool | Whether the host matches the reference name. |
| static | certificateMatches(string $host, array $certificate): bool | Whether a parsed certificate (openssl_x509_parse) names the host. @param array<string, mixed> $certificate |
CookieText final class
public/minn/src/Minn/Http/CookieText.php · 94 lines · Open the class →
Set-Cookie text and the matching rules a cookie jar applies: parsing a header into name, value and attributes; normalizing attributes (expires and max-age as timestamps, the domain without its leading dot); whether a cookie belongs to a domain and a path (RFC 6265).
| static | parse(string $header, string $name = ''): array | A Set-Cookie value split up. With a name given, the first part is all value; a first part without "=" is a value with an empty name. @return array{name: string, value: string, attributes: array<string, string|true>} |
| static | normalizeAttribute(string $name, mixed $value, int $referenceTime): mixed | One attribute normalized, or null when it should go: dates become timestamps, the domain loses its leading dot. |
| static | hostMatches(?string $cookieDomain, string $domain): bool | Whether a host-only cookie with this domain attribute (none means any) belongs to the domain: the same text only. |
| static | domainMatches(?string $cookieDomain, string $domain): bool | Whether a cookie with this domain attribute (none means any) belongs to the domain or one of its subdomains (never an IP address). |
| static | pathMatches(?string $cookiePath, string $requestPath): bool | Whether a cookie with this path attribute (none means any) is sent for the request path. |
| static | defaultPath(string $requestPath): string | The path a cookie without one gets: the request path up to its last slash, or "/". |
Destination final readonly class
public/minn/src/Minn/Http/Destination.php · 102 lines · Open the class →
Where Minn\Http lets a request go. Only http and https; when hosts are named, every hop must start with one of them (and with one of the client's own, when it is bound); and an address outside the public internet (loopback, private and shared ranges, link-local, cloud metadata, reserved) is refused unless its host is listed in $private. A name is resolved here and curl is pinned to the addresses that were checked, so DNS cannot answer one way to the check and another to the connection.
| new | __construct(array $hosts = array ( ), array $private = array ( ), ?Closure $lookup = NULL, array $within = array ( )) | @param list<string> $hosts URL prefixes every hop must start with; none means any public host@param list<string> $private host names or addresses allowed to reach a private address@param (Closure(string): list<string>)|null $lookup a host's addresses; DNS when null@param list<string> $within URL prefixes the client itself is bound to (an extension's needs.http); none means unbound |
| method | refusal(string $url): ?string | Why $url may not be requested, or null when nothing about the URL itself refuses it (a name is judged again once resolved). |
| method | pinned(Minn\Http\Outbound $request): Minn\Http\Outbound|Minn\Http\Exchange | The request with curl pinned to the addresses its host resolves to, or the failed exchange when the name does not resolve or resolves somewhere private it was not allowed. |
| static | host(string $url): string | A URL's host, lower-cased, without the brackets around an IPv6 address. |
| inside | startsWithOne(), allowed(), dns() |
DiePage final class
public/minn/src/Minn/Http/DiePage.php · 126 lines · Open the class →
The page wp_die shows a browser, as the reference draws it (probe functions-remaining): its stylesheet word for word, the html element's language (or dir='rtl' for a right-to-left message, which also sets a Tahoma font), the charset, the robots tag, the title and the message as given, then any links. The caller escapes what needs escaping. The stylesheet is kept here with four spaces for each of its tabs.
| static | html(string $languageAttributes, string $direction, string $charset, string $robots, string $title, string $message, string $links): string | The whole page: $languageAttributes go inside <html> unless the direction is 'rtl'; $robots is the printed robots tag. |
| inside | styles() |
Download final readonly class
public/minn/src/Minn/Http/Download.php · 36 lines · Open the class →
A file the engine fetches for itself (a package, a language pack), over its Minn\Http client with the rules a download needs: https at every hop, one of the caller's host prefixes when given, no private address, and a body over the cap fails rather than being truncated. The messages are written for the person who asked for the download.
| new | __construct(Minn\Http $http = \Minn\Http::__set_state(array( 'within' => array ( ), 'fake' => NULL, 'refusal' => NULL, ))) | |
| method | https(string $url, int $maxBytes, array $hostPrefixes = array ( ), string $userAgent = 'Minn Engine'): string | The body at $url, following at most five redirects. @param list<string> $hostPrefixes URL prefixes every hop must start with (none: any https host) |
Envelope interface
public/minn/src/Minn/Http/Envelope.php · 9 lines · Open the class →
What runs around a matched route once the router knows the route takes the request: the place a host lets other code refuse, answer or edit it (the runtime's REST server filters). The router hands over the argument check's refusal and the policy's, unthrown, because that code may clear the one and must never run the handler past the other. Without an envelope the router throws them itself.
| method | around(Minn\Http\Matched $matched, Minn\Http\Request $request, ?Minn\RestError $invalid, ?Minn\RestError $refusal, Closure $invoke): Minn\Http\Response | The answer to a matched request, refusals included. @param Closure(): Response $invoke the route's handler; it may throw RestError |
Exchange final readonly class
public/minn/src/Minn/Http/Exchange.php · 74 lines · Open the class →
What came back: the final response's status, headers (repeats as lists), Set-Cookie values, and body, or the transport error.
| new | __construct(int $code, array $headers, array $cookies, string $body, ?string $error = NULL, array $head = array ( ), int $errno = 0, string $url = '') | @param array<string, string|list<string>> $headers names lower-cased@param list<string> $cookies raw Set-Cookie header values@param list<string> $head the final response's status line and header lines as they came |
| prop | readonly int $code | |
| prop | readonly array $headers | |
| prop | readonly array $cookies | |
| prop | readonly string $body | |
| prop | readonly ?string $error | |
| prop | readonly array $head | |
| prop | readonly int $errno | |
| prop | readonly string $url | |
| static | failure(string $error, int $errno, string $url): self | An exchange in which no response arrived. |
| method | failed(): bool | Whether the transport failed before any status came back. |
| method | ok(): bool | A response arrived and it was a 2xx. |
| method | json(): mixed | The body decoded as JSON, or null when it is not JSON. |
| method | header(string $name): ?string | One header by any spelling of its name; a repeated header's values joined with ", ". |
| method | cookie(string $name): ?string | One cookie's value from the Set-Cookie headers, percent-decoded; the last one set wins. |
| method | throw(): self | This exchange when it is ok(), otherwise a RequestFailed exception (a RuntimeException) carrying it, for callers who would rather catch than check. |
Failure final class
public/minn/src/Minn/Http/Failure.php · 207 lines · Open the class →
What the public sees when the engine cannot answer: a plain page with no detail, while the detail goes to the log. Installed once per request, it sets PHP's error settings from the debug constants (display stays off unless WP_DEBUG and WP_DEBUG_DISPLAY ask for it) and catches the fatal errors PHP would otherwise print.
| static | armRecovery(): void | Opens the window in which a failure is an extension's fault: while the plugins and the theme's functions file load and the boot actions fire, code that fails fails for every visitor, so pausing it heals the site. A failure after that (a route, a shortcode, a template) answers to one request's input and is never grounds for a pause; it is logged and the request ends in the error page. |
| static | disarmRecovery(): void | Closes the window armRecovery() opened. |
| static | onFatal(callable $recorder): void | What to do with a fatal beyond showing the page: the engine records the extension it came from so the next request loads without it. Installed once the runtime is up, since it needs the database. @param callable(array{type:int,file:string,line:int,message:string}): void $recorder |
| static | install(array $constants, array $server): void | Sets PHP's error settings from the debug constants as the reference's boot does (Runtime\DebugMode), and installs the error handlers. With WP_DEBUG off, or for a request that answers a machine, errors never display. In the first case the reference leaves php.ini's setting; the engine never shows a visitor PHP's errors. Detail is shown only while display_errors is on. @param array<string, mixed> $constants the debug and request constants, by name@param array<string, mixed> $server the request's server variables |
| static | report(Throwable $error): Minn\Http\Response | Logs the cause; the page says only that something went wrong. |
| static | reportJson(Throwable $error): Minn\Http\Response | The same, for a request that asked for JSON: a client that speaks REST is answered in REST, never handed the HTML error page. |
| static | internal(): Minn\Http\Response | The 500 page. |
| static | databaseUnavailable(): Minn\Http\Response | The 503 page the reference shows when the database cannot be reached. |
| static | maintenance(): Minn\Http\Response | The 503 page while maintenance mode holds; monitors read the status, the retry hint, and the title. |
| static | detailed(string $class, string $message, string $file, int $line): Minn\Http\Response | The same page with the cause on it, for a site that asked to see errors. Only ever reached when WP_DEBUG_DISPLAY (or WP_DEBUG) is on: a site that has not asked never learns this much from a response. |
| inside | discardOutput(), note(), record(), dropin(), page() |
Fake final class
public/minn/src/Minn/Http/Fake.php · 94 lines · Open the class →
Answers requests in place of the network, and keeps what was sent. A test makes one and hands its client to the code it tests:
$fake = new Fake(['api.example.com/*' => ['ok' => true]]); $result = (new StatusCheck($fake->http()))->run(); $fake->sent('api.example.com/status'); // the requests that went there
Only that client is answered, so there is nothing to put back and nothing else in the process sees the fake.
| new | __construct(array $answers = array ( )) | Keys are URL patterns (* matches anything; the scheme and query may be left off); values are an array (sent as JSON), a string (the body), a status code, an Exchange from reply(), or a closure taking the Outbound and returning one of those. A request no pattern matches fails as if nothing were listening. @param array<string, mixed> $answers |
| method | http(array $within = array ( )): Minn\Http | A client this fake answers, bound to $within as an extension's is by its needs.http. The destination rules still run, but names are not resolved. @param list<string> $within |
| method | answer(Minn\Http\Outbound $request): Minn\Http\Exchange | The answer for one request, which is recorded as sent. |
| method | sent(string $pattern = '*'): array | The requests sent so far, oldest first; with a pattern, only those whose URL matches it. @return list<Outbound> |
| static | reply(array|string $body = '', int $status = 200, array $headers = array ( )): Minn\Http\Exchange | A response made by hand, for a fake to answer with. An array body is sent as JSON. @param array<string, string|list<string>> $headers name => value; Set-Cookie values become cookies |
| static | matches(string $pattern, string $url): bool | Whether a URL matches a pattern in which * stands for anything; the scheme and the query string may be left off. |
| inside | exchange() |
Ipv6 final class
public/minn/src/Minn/Http/Ipv6.php · 62 lines · Open the class →
IPv6 addresses as text: written out in full (eight groups, or six and an IPv4 tail), compressed ("::" for the longest run of zero groups, the first when two tie), and checked. Zone suffixes ("%eth0") do not pass.
| static | expand(string $ip): string | The address with "::" expanded into zero groups. |
| static | compress(string $ip): string | The address with leading zeros dropped from all-digit groups and the longest zero run as "::". |
| static | valid(string $ip): bool | Whether the text is an IPv6 address (with an optional IPv4 tail). |
IriParts final readonly class
public/minn/src/Minn/Http/IriParts.php · 138 lines · Open the class →
An IRI (a URL that may carry non-ASCII text) split into its parts and written back normalized: scheme and host in lower case, a scheme's default port dropped, dot segments removed, and every character a URL may not hold percent-encoded. The IRI form keeps non-ASCII characters; the URI form encodes them too.
| new | __construct(?string $scheme, ?string $userinfo, ?string $host, ?int $port, string $path, ?string $query, ?string $fragment) | |
| prop | readonly ?string $scheme | |
| prop | readonly ?string $userinfo | |
| prop | readonly ?string $host | |
| prop | readonly ?int $port | |
| prop | readonly string $path | |
| prop | readonly ?string $query | |
| prop | readonly ?string $fragment | |
| static | parse(string $iri): self | The parts of an IRI (RFC 3986 appendix B). |
| method | valid(): bool | Whether the parts make a well-formed IRI: a valid scheme when there is one, no "//" path without an authority. |
| method | resolve(self $ref): ?self | The reference resolved against this base (RFC 3986 section 5.2), or null when the base has no scheme. |
| method | withHost(?string $host): self | The same IRI with another host. |
| method | withPath(string $path): self | The same IRI with another path. |
| method | toIri(): string | The normalized IRI: non-ASCII characters stay as they are. |
| method | toUri(): string | The normalized URI: non-ASCII characters percent-encoded too. |
| inside | write(), mergePath(), removeDots(), encode() |
Kernel final readonly class
public/minn/src/Minn/Http/Kernel.php · 38 lines · Open the class →
The edge. Turns a request into a response through the router and turns a RestError thrown anywhere underneath into the WordPress error shape.
| new | __construct(Minn\Http\Router $router) | |
| method | handle(Minn\Http\Request $request): ?Minn\Http\Response | Routes the request; a thrown failure becomes its response, and null means no route matched. |
| inside | harden() |
LegacyHeaders final class
public/minn/src/Minn/Http/LegacyHeaders.php · 60 lines · Open the class →
WP_Http's legacy readers, kept for plugins that still call them (probe http-classes). They read a header block the old way, and decode a chunked body with the reference's quirk. RawResponse does both properly for the engine's own requests.
| static | read(array|string $headers): array | A header block (a string, its folded lines joined, or a list of lines) read into the last status line's code and message, lower-cased headers (a repeated one as a list) and the Set-Cookie values. Each status line starts the headers over. @param string|list<string> $headers@return array{response: array{code: int, message: string}, headers: array<string, string|list<string>>, cookies: list<string>} |
| static | dechunk(string $body): string | A chunked body decoded as the reference's WP_Http::chunkTransferDecode decodes one. The CRLF after a chunk is not stepped over, so it decodes only when chunks run into each other and a final 0 ends the body; any other shape comes back as it was. |
Location final class
public/minn/src/Minn/Http/Location.php · 85 lines · Open the class →
Where a redirect leads: a Location header made absolute against the URL that sent it.
| static | sanitize(string $location): string | A redirect's address made safe, as wp_sanitize_redirect() makes it (probe security-parity): spaces as %20, whole UTF-8 sequences URL-encoded, any other character outside the URL set dropped, and an encoded CR or LF taken out until none is left. Other encoded control characters stay, as they do on the reference. |
| static | resolve(string $base, string $location): string | The absolute URL $location names, read against $base as RFC 3986 reads a reference: a scheme keeps it as written, "//" takes the base's scheme, a fragment or query alone keeps the base's path, and a relative path joins the base's directory with "." and ".." resolved. |
| static | sameOrigin(string $one, string $two): bool | Whether two URLs share scheme, host, and port, so credentials may follow from one to the other. |
| inside | withoutDotSegments() |
Matched final readonly class
public/minn/src/Minn/Http/Matched.php · 15 lines · Open the class →
A route the router matched to a request and whose policy it judged: the route, the object and method that answer it, the pattern's captures, and every method the same handler answers on that pattern (the reference lists POST, PUT and PATCH together for one edit handler).
| new | __construct(Minn\Http\Route $route, object $handler, string $method, array $captures, array $methods) | @param array<string, string> $captures@param list<string> $methods |
| prop | readonly Minn\Http\Route $route | |
| prop | readonly object $handler | |
| prop | readonly string $method | |
| prop | readonly array $captures | |
| prop | readonly array $methods |
MediaType final class
public/minn/src/Minn/Http/MediaType.php · 18 lines · Open the class →
Reading the media types a request names in its Accept and Content-Type headers.
| static | json(string $types): bool | Whether a list of media types names JSON: application/json, or any application/…+json vendor type, parameters and case ignored (wp_is_json_media_type(), probe load-remaining). |
Method enum
public/minn/src/Minn/Http/Method.php · 33 lines · Open the class →
Cases: Get = 'GET', Head = 'HEAD', Post = 'POST', Put = 'PUT', Patch = 'PATCH', Delete = 'DELETE', Options = 'OPTIONS', Any = '*'
| static | fromName(string $name): self | The verb for a name, GET when the name is unknown. |
| method | matches(self $declared): bool | HEAD is served by GET handlers; the kernel drops the body. |
| method | canonicalRedirects(): bool | Canonical redirects (trailing slash, pretty-URL mapping, 404 guessing) run only for reads; every other method renders the URL as typed. |
Outbound final readonly class
public/minn/src/Minn/Http/Outbound.php · 46 lines · Open the class →
One outgoing HTTP request, normalised: the transport needs nothing else.
| new | __construct(string $method, string $url, array $headers = array ( ), ?string $body = NULL, float $timeout = 5.0, int $redirects = 5, bool $verifySsl = true, string $userAgent = '', ?string $caInfo = NULL, bool $blocking = true, ?Closure $prepare = NULL, ?float $connectTimeout = NULL, ?int $maxBytes = NULL) | @param list<string> $headers "Name: value" lines@param (Closure(\CurlHandle): void)|null $prepare a last word on the curl handle before it is sent |
| prop | readonly string $method | |
| prop | readonly string $url | |
| prop | readonly array $headers | |
| prop | readonly ?string $body | |
| prop | readonly float $timeout | |
| prop | readonly int $redirects | |
| prop | readonly bool $verifySsl | |
| prop | readonly string $userAgent | |
| prop | readonly ?string $caInfo | |
| prop | readonly bool $blocking | |
| prop | readonly ?Closure $prepare | |
| prop | readonly ?float $connectTimeout | |
| prop | readonly ?int $maxBytes | |
| method | to(string $url, string $method, ?string $body, array $headers): self | The same request sent somewhere else, as the next hop of a redirect. @param list<string> $headers "Name: value" lines |
| method | preparing(Closure $prepare): self | The same request with a last word on the curl handle, run after any it already has. |
Policy final readonly class
public/minn/src/Minn/Http/Policy.php · 115 lines · Open the class →
What a route requires of its caller, as data on the route: the router enforces it before the handler runs, so a grep over the attributes is the authorization surface. The two refusals are the reference's: a caller who is not signed in gets the sign-in code (401), a signed-in caller who lacks the capability gets the refusal code (403); how a bad nonce is answered belongs to whoever judges the policy. A policy that names a subject has the record looked up first, and answers the record's 404 before either refusal, which is the reference's order.
Written inline in the attribute with `new`, which is what an attribute argument allows: `policy: new Policy(Access::Cap, 'upload_files', refuse: 'rest_cannot_create', message: '...')`.
| new | __construct(Minn\Http\Access $access = Minn\Http\Access::Public, ?string $cap = NULL, array $caps = array ( ), ?string $param = NULL, string $signIn = 'rest_not_logged_in', string $signInMessage = 'You are not currently logged in.', string $refuse = 'rest_forbidden', string $message = 'Sorry, you are not allowed to do that.', ?Minn\Http\Policy $edit = NULL, ?Minn\Http\Subject $subject = NULL, ?string $missing = NULL, ?string $missingMessage = NULL, int $signInStatus = 401, ?string $verb = NULL, ?array $opener = NULL) | @param list<string> $caps further capabilities every one of which the caller must hold |
| prop | readonly Minn\Http\Access $access | |
| prop | readonly ?string $cap | |
| prop | readonly array $caps | |
| prop | readonly ?string $param | |
| prop | readonly string $signIn | |
| prop | readonly string $signInMessage | |
| prop | readonly string $refuse | |
| prop | readonly string $message | |
| prop | readonly ?Minn\Http\Policy $edit | |
| prop | readonly ?Minn\Http\Subject $subject | |
| prop | readonly ?string $missing | |
| prop | readonly ?string $missingMessage | |
| prop | readonly int $signInStatus | |
| prop | readonly ?string $verb | |
| prop | readonly ?array $opener | |
| method | isPublic(): bool | Whether the route is open to anyone with nothing to look up, so the gate need not run at all. |
| method | missingCode(): string | The 404 code a missing subject earns. |
| method | missingText(): string | The 404 message a missing subject earns. |
| method | capabilities(): array | Every capability the policy asks for by name, the one on the object included, so a listing can show what a route takes. @return list<string> |
| method | toArray(): array | The policy as data, for the route catalogue. @return array<string, mixed> @return array<string, mixed> |
| method | describe(): string | The policy in one line, for the route index and the docs. |
Punycode final class
public/minn/src/Minn/Http/Punycode.php · 103 lines · Open the class →
Internationalized host names in ASCII: each label that is not ASCII is written as "xn--" plus its Punycode (RFC 3492) and must stay under 64 bytes. Labels keep their case: there is no Nameprep step, as on the reference ("Bücher" becomes "xn--Bcher-kva").
| const | PREFIX = 'xn--' | |
| const | MAX_LENGTH = 64 | |
| static | host(string $hostname): string | A whole host name, label by label. |
| static | label(string $text): string | One label in ASCII. |
| static | encode(string $input): string | The Punycode of a UTF-8 string (without the prefix). |
| inside | digits(), digit(), adapt() |
RawResponse final class
public/minn/src/Minn/Http/RawResponse.php · 105 lines · Open the class →
An HTTP response as text, the way the Requests library hands it from transport to parser: the status line and headers, a blank line, the body. Built from an exchange, split back up, chunked bodies joined and compressed ones inflated.
| static | fromExchange(Minn\Http\Exchange $exchange): string | The exchange as raw response text: its status line, header lines, a blank line, the body. |
| static | parse(string $raw): array | The text split into protocol, status, header pairs (folded lines joined) and body. @return array{protocol: float, status: int, headers: list<array{0: string, 1: string}>, body: string} |
| static | parseHead(string $head): array | A head alone (the body went to a file): protocol, status, header pairs, and an empty body. @return array{protocol: float, status: int, headers: list<array{0: string, 1: string}>, body: string} |
| static | unchunk(string $body): string | A chunked body joined; text that is not chunked comes back as it was. |
| static | inflateLoose(string $data): string|false | A gzip body inflated past its header fields (extra, name, comment, header CRC) and trailer, or a zlib one past its two-byte header, as WP_Http_Encoding::compatible_gzinflate reads them; false when neither inflates. |
| static | inflate(string $data): string | A gzip or zlib body inflated; anything else (raw deflate included) comes back as it was. |
Refusal interface
public/minn/src/Minn/Http/Refusal.php · 5 lines · Open the class →
A refusal a route judges after its arguments and before its policy: the reference turns some requests away at the head of its permission check, before it looks at the caller at all (a post created with an id).
| static | refuse(Minn\Http\Request $request): void | Throws the refusal when the request earns it; returns quietly otherwise. |
Request final readonly class
public/minn/src/Minn/Http/Request.php · 145 lines · Open the class →
An immutable picture of the incoming request. Built once from the PHP globals at the edge; handlers only ever see this object.
| new | __construct(Minn\Http\Method $method, string $path, array $query, array $headers, array $cookies, string $body, bool $secure, string $host, array $form = array ( ), array $files = array ( ), string $remoteAddress = '', array $server = array ( )) | @param array<string, string|array> $query@param array<string, string> $headers lower-cased names@param array<string, string> $cookies@param array<string, mixed> $form decoded form fields, for bodies that are not JSON@param array<string, array> $files uploaded files, keyed by field |
| prop | readonly Minn\Http\Method $method | |
| prop | readonly string $path | |
| prop | readonly array $query | |
| prop | readonly array $headers | |
| prop | readonly array $cookies | |
| prop | readonly string $body | |
| prop | readonly bool $secure | |
| prop | readonly string $host | |
| prop | readonly array $form | |
| prop | readonly array $files | |
| prop | readonly string $remoteAddress | |
| prop | readonly array $server | what the server says about itself: software, protocol, and address; diagnostics only |
| static | fromGlobals(): self | The request PHP received, read once from the superglobals. |
| method | json(): array | The JSON body as an array, or the form fields when the body is empty or a form. |
| method | withPath(string $path): self | The same request addressed to another path (a REST route carried in ?rest_route=). |
| method | query(string $key, ?string $default = NULL): ?string | One query value as a string, or the default when it is absent or not a string. |
| method | flag(string $key): bool | A query value read as the reference reads a boolean argument: true, 1, yes, on; anything else is false. |
| method | flagged(string $key): bool | A boolean the query or the body sets, as a DELETE may carry force in either (probes rest-posts-write, rest-terms-controller). |
| method | has(string $key): bool | Whether the query carries this key at all, even empty. |
| method | header(string $name): ?string | A request header by case-insensitive name, or null. |
| method | cookie(string $name): ?string | A cookie's value, or null. |
| method | segments(): array | The path split into non-empty segments: "/a/b/" becomes ["a", "b"]. |
| method | queryStringWithout(string ...$keys): string | The query string with the given keys removed, ready to append to a redirect. |
RequestFailed final class
public/minn/src/Minn/Http/RequestFailed.php · 7 lines · implements Stringable, Throwable · extends RuntimeException · Open the class →
Thrown by Exchange::throw() when no response arrived or it was not a 2xx; the exchange rides along.
| new | __construct(Minn\Http\Exchange $reply, string $message) | |
| prop | readonly Minn\Http\Exchange $reply |
RequestsNames final class
public/minn/src/Minn/Http/RequestsNames.php · 18 lines · Open the class →
The Requests library's PSR-0 class names (Requests_Exception_HTTP_404, Requests_IDNAEncoder, ...) mapped to the PSR-4 names they stand for.
| static | modern(string $legacy): ?string | The namespaced name for a Requests_* name, or null when it is not one. |
Response final readonly class
public/minn/src/Minn/Http/Response.php · 133 lines · Open the class →
What a handler returns. Nothing is written to the client until the kernel calls send(), so a response can be inspected, wrapped, or replaced on the way out. Work that belongs after the client has its answer (a cron run a page found due) rides along as afterSend closures.
| new | __construct(int $status = 200, array $headers = array ( ), string $body = '', array $cookies = array ( ), array $afterSend = array ( )) | @param array<string, string> $headers@param list<array{0: string, 1: string, 2: array}> $cookies name, value, setcookie options@param list<Closure(): void> $afterSend run once the response has been handed to the client |
| prop | readonly int $status | |
| prop | readonly array $headers | |
| prop | readonly string $body | |
| prop | readonly array $cookies | |
| prop | readonly array $afterSend | |
| static | html(string $body, int $status = 200): self | An HTML response. |
| static | json(mixed $payload, int $status = 200): self | A JSON response with the payload encoded. |
| static | redirect(string $location, int $status = 301): self | A redirect to a location. |
| method | withHeader(string $name, string $value): self | The same response with one header set, after those already set (one already there keeps its place). |
| method | withoutHeader(string $name): self | The same response without one header. |
| method | withCookie(string $name, string $value, array $options): self | The same response with a cookie to set. @param array<string, mixed> $options setcookie options: expires, path, secure, httponly, samesite |
| method | withoutBody(): self | The same response with an empty body, the HEAD answer. |
| method | afterSend(Closure $work): self | The same response with work to run once the client has been answered. @param Closure(): void $work @param Closure(): void $work |
| static | emitHeader(string $line): void | One header line sent now, as plugin code sends one, ahead of the response's own (which win on a shared name). Nothing outside a web request, or once the head is out. |
| static | write(string $bytes): void | Bytes written to PHP's output now, so into whatever buffers are open: for a page passed through buffers plugin code opened before it (Theme\TemplateBuffer), whose handlers then see it. |
| method | sendHead(): void | Writes the status, the headers, and the cookies now, and nothing else: for an answer that code outside the engine may finish on its own (an ajax handler that ends the request), which must find them already said. |
| method | send(): void | Writes the status, the headers, the cookies, and the body, ends the request for the client, then runs the after-send work in the same process (the server that can close the connection first does). |
Route final readonly class
public/minn/src/Minn/Http/Route.php · 75 lines · Open the class →
Declares a handler method as a route. The policy lives here, as metadata the router enforces before the handler runs, so the authorization surface of the engine is a grep away; a route with no policy is one the ratchet in the style suite counts down. The parameter sets it reads live here too, judged before the policy is (the reference refuses a bad argument before it refuses a caller), so the REST index can tell a client what a route takes as well as what it requires of them.
Patterns: "/wp/v2/posts/{id}" captures one segment, "{id:\d+}" constrains it, and "/{path*}" captures the rest of the path (slashes included).
| new | __construct(Minn\Http\Method $method, string $pattern, ?Minn\Http\Policy $policy = NULL, array $args = array ( ), bool $index = true, array $body = array ( ), ?string $name = NULL, ?string $params = NULL, ?string $refuse = NULL) | @param list<array<string, array<string, mixed>>> $args the parameter sets this route reads, from Args@param list<array<string, array<string, mixed>>> $body the parameter sets this route reads from the JSON body, from Args or the shape that owns them |
| prop | readonly Minn\Http\Method $method | |
| prop | readonly string $pattern | |
| prop | readonly ?Minn\Http\Policy $policy | |
| prop | readonly array $args | |
| prop | readonly bool $index | |
| prop | readonly array $body | |
| prop | readonly ?string $name | |
| prop | readonly ?string $params | |
| prop | readonly ?string $refuse | |
| method | arguments(array $captures = array ( )): array | The parameters this route accepts, as the REST index publishes them: from its params source for these captures (a capture the pattern fixes, such as {base:posts}, counts when none is given), or its fixed sets. @param array<string, string> $captures@return array<string, array<string, mixed>> |
| method | bodyArguments(): array | The parameters this route reads from the JSON body, validated before the caller is judged. @return array<string, array<string, mixed>> |
| method | regex(): string | The pattern as a regular expression with named captures. |
RouteMiss final class
public/minn/src/Minn/Http/RouteMiss.php · 3 lines · implements Stringable, Throwable · extends RuntimeException · Open the class →
A handler declining a request its pattern matched: the router swallows it and goes on to the next route, so a catch-all pattern can leave a path it does not own to whatever registers after it (a plugin's route under the same namespace, say) instead of answering "no route" itself.
RouteParams interface
public/minn/src/Minn/Http/RouteParams.php · 10 lines · Open the class →
The parameters a route takes when they depend on what its captures name (a post type's collection takes its own taxonomies, its own orderby values, whatever plugins add to it), rather than on a fixed set.
| static | for(array $captures): array | The route's parameters for these captures, as the index publishes them; none when the captures name nothing. @param array<string, string> $captures@return array<string, array<string, mixed>> |
RouteRow final readonly class
public/minn/src/Minn/Http/RouteRow.php · 81 lines · Open the class →
One line of the route table: what a route is, who it is for, and what it takes, read from the attribute and the handler method alone, so the table can be built from the classes without a request, a database, or a site. This is the row the REST index, the docs, and contracts/api/routes.json are written from.
| new | __construct(string $method, string $pattern, string $name, string $handler, string $summary, ?Minn\Http\Policy $policy, array $args, array $body, bool $index, ?string $params = NULL, string $regex = '') | @param array<string, array<string, mixed>> $args the query parameters the route reads@param array<string, array<string, mixed>> $body the JSON body parameters it reads |
| prop | readonly string $method | |
| prop | readonly string $pattern | |
| prop | readonly string $name | |
| prop | readonly string $handler | |
| prop | readonly string $summary | |
| prop | readonly ?Minn\Http\Policy $policy | |
| prop | readonly array $args | |
| prop | readonly array $body | |
| prop | readonly bool $index | |
| prop | readonly ?string $params | |
| prop | readonly string $regex | |
| static | of(Minn\Http\Route $route, ReflectionMethod $method): self | The row for one attribute on one handler method; the name defaults to the handler. |
| method | argsFor(string $route): array | The parameters as the index publishes them for one concrete route the pattern takes (a {base} route's parameters are that base's). @return array<string, array<string, mixed>> |
| method | toArray(): array | The row as data, for the JSON catalogue. @return array<string, mixed> @return array<string, mixed> |
| inside | summary() |
Router final class
public/minn/src/Minn/Http/Router.php · 260 lines · Open the class →
Matches a request to a #[Route] on one of the registered handler objects, has the check refuse a bad argument, has the gate judge the route's policy, and invokes the method with the request plus the named pattern captures. That order is the reference's: an invalid parameter is answered before the caller is looked at. The gate is the one thing a router cannot be built without: a policy nobody judges is a route nobody may call.
| new | __construct(Closure $gate, ?Closure $check = NULL, ?Minn\Http\Envelope $envelope = NULL) | @param Closure(Policy $policy, Request $request, array<string, string> $captures): void $gate throws when the policy refuses the caller@param Closure(Route $route, Request $request): void|null $check throws when a declared argument is missing or invalid; judged before the gate |
| method | register(object ...$handlers): self | Registers every #[Route] method of the given handlers; returns the router for chaining. |
| method | routes(): array | The routes that list in an index: pattern => methods. @return array<string, list<string>> @return array<string, list<string>> |
| method | table(): array | Every registered route as a row: what it is, who it is for, what it takes. The same rows Rest\Catalogue reads from the classes alone. @return list<RouteRow> |
| method | allowed(Minn\Http\Request $request): array | The methods the caller may use on this path, for the Allow header: every route matching the path (a route with no captures that names the path exactly claims it alone), its policy judged for the caller without dispatching. A route that states no policy counts for GET only, until it states one. Empty when nothing matched or nothing is allowed, and the header is then left out, as the reference does. @return list<string> in the reference's order |
| method | matching(Minn\Http\Request $request): array | The routes a request's method and path match, policies unjudged, in the order dispatch tries them, with their captures: what a batch reads to tell a route that does not take part from no route at all. @return list<array{0: Route, 1: array<string, string>}> |
| method | dispatch(Minn\Http\Request $request): ?Minn\Http\Response | Null when nothing matched, so the caller can fall through. |
| inside | admits(), arguments(), answer(), enveloped(), methodsOf(), literal(), spells() |
StatusText final class
public/minn/src/Minn/Http/StatusText.php · 16 lines · Open the class →
The reason phrase a status line carries for each code, as the reference's table has them (probe functions-remaining): its own wording where it differs from today's RFCs ("Request Entity Too Large", "Reserved" for 306).
| static | of(int $code): string | The phrase for a code, or empty for one the table does not know. |
Subject enum
public/minn/src/Minn/Http/Subject.php · 46 lines · Open the class →
The record a route capture names, so a policy can have it looked up before the caller is judged: the reference answers "no such post" ahead of "you may not", and the 404 it sends is the record's own. A kind that reads its post type or taxonomy from the {base} capture says so.
Cases: Post, PostParent, Attachment, Block, Navigation, MenuItem, GlobalStyles, GlobalStylesParent, Term, Menu, User, Comment
| method | missingCode(): string | The reference's error code for a record that does not exist. |
| method | missingMessage(): string | The reference's message for a record that does not exist. |
Transport final class
public/minn/src/Minn/Http/Transport.php · 138 lines · Open the class →
The engine's outgoing HTTP transport over curl: it sends exactly what an Outbound says and nothing else decides. Redirects are followed by curl, so the header lines of every hop arrive in order; only the last response's block is kept, the way plugin code expects to read it. Code calling out goes through Minn\Http, which owns the rules about where a request may go.
| static | send(Minn\Http\Outbound $request): Minn\Http\Exchange | Performs one outgoing request over curl and returns the exchange, a transport error included. |
| inside | options(), limit(), lastHead(), lastBlock() |
TrustedProxies final readonly class
public/minn/src/Minn/Http/TrustedProxies.php · 115 lines · Open the class →
Which addresses in front of the engine may speak for the client.
By default none do: a forwarded header is a request header, and a request header is the client's word. Behind a proxy or CDN that terminates TLS, `REMOTE_ADDR` is then the proxy's, which makes every visitor look like one address (the sign-in throttle would lock them all out together) and every request look insecure (the auth cookie would lose its Secure flag).
A site says who to believe in `wp-config.php`:
define('MINN_TRUSTED_PROXIES', '10.0.0.0/8, 2400:cb00::/32'); define('MINN_TRUSTED_PROXIES', '*'); // any: only when nothing but the proxy can reach PHP
The client is then the right-most address in `X-Forwarded-For` that is not itself trusted, which is what a proxy chain appends, so a client that sends its own `X-Forwarded-For` cannot put an address the engine believes to the right of the proxy's. `*` trusts whoever connects, not every forwarded hop: the client is the address that proxy appended (with it every hop, the client's own included, would be skipped as trusted, and every visitor would be the proxy again).
| static | none(): self | Nobody in front of the engine speaks for the client. |
| static | configured(): self | What the site declared in wp-config.php, or nobody. |
| method | trusts(string $address): bool | Whether the engine believes what this address says about the client. |
| method | clientAddress(string $remoteAddress, string $forwardedFor): string | The client's address, given who connected and what was forwarded: the right-most forwarded address that is not itself a trusted proxy. |
| method | forwardedSecure(string $remoteAddress, string $forwardedProto): bool | Whether a forwarded scheme may be believed, and says https. |
| inside | coversHop(), bare(), covers() |
Nothing matches.