Read from the package's own PHP files at the commit 1ba157548bc3: its namespaces, classes, interfaces and enums, their public constants, properties and methods with their PHPDoc, and the options its factories take. Nothing of it is run.
DiagnosticclassOne thing the export found: an error fails the build (a page that did not render, two pages for one file, a link to nothing, a missing asset, a page rendered differently twice), a warning is to be read (a route left out, a page given twice), a notice says what the export did on purpose (a link left to the dynamic host).
6 methods · 4 properties · 15 constants
ExporterclassExports a site to a folder: every page its providers give, rendered by its renderer, written as a file; the files of its asset manifests and the public files it names, copied; every internal link of the pages followed - to a page or file of the export, to a public file (copied), to a file the application serves (rendered and written), to the dynamic host (left, or rewritten to it) - and any other reported as broken; the redirections as small pages, _redirects and redirects.json; 404.html; sitemap.xml; and omnibridge-static.json, the report (files and checksums, pages, redirections, diagnostics).
2 methods · 1 constant
ReportclassWhat an export wrote and found: the files (their path in the export, their SHA-256 and size), the pages and where each went, the redirections, the diagnostics. The same report is written as omnibridge-static.json in the export, without a date: two builds of the same application give the same file, so CI can compare them.
7 methods · 5 properties
SiteclassWhat a site exports, declared once (the file the CLI reads returns it): the public address (an origin, or a folder of one: basePath()), the providers of its addresses, its renderer, its public folder and asset manifests, the page a static host shows for an unknown address, the addresses that stay on the dynamic host, and the normalisers that take out of a page what changes at every request.
29 methods · 1 property
StaticPageclassOne address of the application to export: its path (an absolute URL path, no query string, no fragment), and what is known of it - the route and the parameters it was generated from, the action the route runs, its language and the same page in the other languages, whether the sitemap lists it.
4 methods · 9 properties · 3 constants
StaticRendererInterfaceinterfaceRenders one page at build time, by the application's own code: through its host's request handling (Render\RequestRenderer: Symfony's kernel, Laravel's), by calling the route's action itself (Render\ActionRenderer), or by any callable (Render\CallableRenderer, a plain PHP application).
1 method
StaticResponseclassWhat a page answered at build time: its status, its type, its body - or, for a redirection, where it sends. Nothing of the request's own (cookies, caching headers) is kept: a static host answers every visitor alike.
13 methods · 5 properties · 1 constant
StaticRouteProviderInterfaceinterfaceWhere the addresses to export come from: an explicit list (Route\PageList), a router's routes with the values of their parameters (Route\RouteCollectionProvider), the application's own sitemap (Route\SitemapProvider), or the application's own class.
1 method
AssetManifestclassThe files a front-end build produced, read from its manifest, whichever tool wrote it - the exporter copies them from the public folder, as the bundler fingerprinted them:
2 methods
CliclassThe command line, plain PHP (bin/omnibridge-static), which the Symfony command and the artisan command of omnibridge/symfony and omnibridge/laravel call with their host:
5 methods · 5 constants
LinksclassThe addresses a page or a style sheet refers to, and the rewriting of some of them: what the exporter follows to check that every internal link resolves within the export, to collect the files the pages need, and to send a visitor to the dynamic host for what cannot be static.
5 methods · 4 constants
BasePathclassThe folder of its origin a static site is served under ("/repository" on https://owner.github.io/repository/), and the moving of an export's internal addresses under it. The application renders its pages at the root of the origin; once they are written, every address that names the site is moved under the base path:
9 methods · 2 properties · 4 constants
OutputPathclassWhere an address is written in the export, and the URL rules the exporter follows: "/" and "/blog/hello" are HTML pages written as index.html in their folder (a static host serves /blog/hello from blog/hello/index.html: nginx's try_files $uri $uri/index.html, the "pretty URLs" of the static hosts); a page whose path ends in .html, and any other file, are written at their path. Paths are percent-decoded: the file of /caf%C3%A9 is café/index.html, as a static host decodes the address before it looks.
5 methods
ActionRendererclassRenders a page by calling the action its route runs, with no host's request handling: the action taken from a PSR-11 container, called with the route's parameters by name (and `$query`, an empty query string), its other parameters taken from the container by their type, or left to their default; what it returns handed to $answer, the application's own way of turning an action's result into a page (omnibase's Base\Screen\Negotiation, a template engine...).
2 methods
CallableRendererclassA renderer of any callable: a plain PHP application renders its pages its own way (its templates, its data) and answers a StaticResponse - or a string, an HTML page with status 200, or null, no such page (404).
2 methods
RequestRendererclassRenders a page the way the host serves it: a GET request of its address handled in this process by the host's own request handling - the route matched, the same action run, the same templates rendered, the host's listeners on the way - with symfony/http-foundation's Request and Response, which Symfony's kernel and Laravel's both speak (a Laravel request extends it).
2 methods · 1 constant
PageListclassThe plainest provider: the addresses written out, as paths or as pages.
2 methods
RouteCollectionProviderclassThe pages of a router's routes (symfony/routing's RouteCollection - what Symfony's router holds, what omnibridge/laravel's Routing\ActionRoutes reads from an application's #[Route] actions, what symfony/routing's attribute loader gives a plain PHP application).
3 methods
SitemapProviderclassThe pages an application's own sitemap lists (sitemaps.org's urlset, its xhtml:link alternates): a site that already enumerates its addresses for the crawlers enumerates them for the export too. The sitemap is read from a file, or rendered at build time by the renderer (its path, "/sitemap.xml" by default). A sitemap index (sitemapindex) is followed, one level.
3 methods
SkipReportingInterfaceinterfaceA provider that leaves routes out says which and why (a route of a method other than GET, a parameter nobody gave values for...): the exporter lists them in its diagnostics, so that nothing is left out silently.
1 method
Omnibridge\Static
Diagnostic class
final class Diagnostic implements JsonSerializable Diagnostic.php:12
One thing the export found: an error fails the build (a page that did not render, two pages for one file, a link to nothing, a missing asset, a page rendered differently twice), a warning is to be read (a route left out, a page given twice), a notice says what the export did on purpose (a link left to the dynamic host).
const ERROR = 'error'const WARNING = 'warning'const NOTICE = 'notice'const RENDER_FAILED = 'render-failed'A page answered an error status, or its rendering threw.
const DUPLICATE_OUTPUT = 'duplicate-output'Two pages would be written to one file.
const DUPLICATE_PAGE = 'duplicate-page'The same page was given twice (the first is kept).
const BROKEN_LINK = 'broken-link'An internal link resolves to nothing in the export.
const MISSING_ASSET = 'missing-asset'A file of an asset manifest, or one to copy, is not in the public folder.
const NOT_DETERMINISTIC = 'not-deterministic'A page rendered twice gave two different bodies.
const SKIPPED_ROUTE = 'skipped-route'A provider left a route out.
const NOT_EXPORTABLE = 'not-exportable'A page answered something a static file cannot be (no extension for a type other than HTML...).
const QUERY_IGNORED = 'query-ignored'A link with a query string to an exported page: a static host answers the page whatever the query.
const DYNAMIC = 'dynamic'A link left to the dynamic host (Site::dynamic()).
const OUTSIDE_BASE = 'outside-base'An internal address of a written file that points outside the site's base path (Site::basePath()).
const GITHUB_PAGES = 'github-pages'What GitHub Pages will not serve as the export expects (no 404 page, a file or a site above its limits).
public readonly string $levelpublic readonly string $codepublic readonly string $subjectpublic readonly string $message
Diagnostic::__construct()
public function __construct(public readonly string $level, public readonly string $code, public readonly string $subject, public readonly string $message)Diagnostic::error()
public static function error(string $code, string $subject, string $message): selfDiagnostic::warning()
public static function warning(string $code, string $subject, string $message): selfDiagnostic::notice()
public static function notice(string $code, string $subject, string $message): selfDiagnostic::__toString()
public function __toString(): stringDiagnostic::jsonSerialize()
public function jsonSerialize(): arrayExporter class
final class Exporter Exporter.php:37
Exports a site to a folder: every page its providers give, rendered by its renderer, written as a file; the files of its asset manifests and the public files it names, copied; every internal link of the pages followed - to a page or file of the export, to a public file (copied), to a file the application serves (rendered and written), to the dynamic host (left, or rewritten to it) - and any other reported as broken; the redirections as small pages, _redirects and redirects.json; 404.html; sitemap.xml; and omnibridge-static.json, the report (files and checksums, pages, redirections, diagnostics).
A site served under a folder of its origin (Site::basePath()) is rendered at the root and its written files' internal addresses are then moved under the folder (Output\BasePath); any left outside it is an error. For GitHub Pages (Site::githubPages()): .nojekyll, CNAME for a custom domain, the redirections as pages only.
$report = (new Exporter())->export($site, __DIR__.'/dist');
exit($report->hasErrors() ? 1 : 0);The output is deterministic: pages are rendered in the order of their paths, nothing written carries a date, the report's lists are sorted. With $verify, every page is rendered twice and a page that differs is an error. The folder is emptied first, if it holds an earlier export (its omnibridge-static.json) or nothing.
const REPORT = 'omnibridge-static.json'
Exporter::__construct()
public function __construct(readonly ?\Closure $log = null)$logtold of the progress, a line at a time
Exporter::export()
public function export(Site $site, string $outDir, bool $verify = false): ReportReport class
final class Report Report.php:12
What an export wrote and found: the files (their path in the export, their SHA-256 and size), the pages and where each went, the redirections, the diagnostics. The same report is written as omnibridge-static.json in the export, without a date: two builds of the same application give the same file, so CI can compare them.
public readonly string $baseUrlpublic readonly array $filespath in the export => checksum and size, sorted
public readonly array $pagespage path => what was written, sorted
public readonly array $redirectspath => target, sorted
public readonly array $diagnosticsin the order they were found
Report::__construct()
public function __construct(public readonly string $baseUrl, public readonly array $files, public readonly array $pages, public readonly array $redirects, public readonly array $diagnostics)$filespath in the export => checksum and size, sorted
$pagespage path => what was written, sorted
$redirectspath => target, sorted
$diagnosticsin the order they were found
Report::errors()
public function errors(): arrayReport::warnings()
public function warnings(): arrayReport::hasErrors()
public function hasErrors(): boolReport::level()
public function level(string $level): arrayReport::counts()
public function counts(): array- Returns
code => how many, sorted by code
Report::toJson()
public function toJson(): stringThe report as omnibridge-static.json holds it: stable keys, no date.
Site class
final class Site Site.php:22
What a site exports, declared once (the file the CLI reads returns it): the public address (an origin, or a folder of one: basePath()), the providers of its addresses, its renderer, its public folder and asset manifests, the page a static host shows for an unknown address, the addresses that stay on the dynamic host, and the normalisers that take out of a page what changes at every request.
Asset\AssetManifest reads the manifests.
return (new Site('https://example.org')) ->provide(new PageList(['/', '/about'])) ->renderWith(new CallableRenderer(fn (StaticPage $page) => render($page->path))) ->publicDir(__DIR__.'/public') ->assets(__DIR__.'/public/build/manifest.json', '/build') ->copy('favicon.ico', 'robots.txt') ->notFound('/404');
public readonly string $baseUrlthe public address of the static site: its origin ("https://example.org"), or the folder of an origin it is served under ("https://owner.github.io/repository/") - the sitemap's addresses, the links taken for internal, and the base path every internal address of the export is moved under (Output\BasePath)
Site::__construct()
public function __construct(public readonly string $baseUrl)$baseUrlthe public address of the static site: its origin ("https://example.org"), or the folder of an origin it is served under ("https://owner.github.io/repository/") - the sitemap's addresses, the links taken for internal, and the base path every internal address of the export is moved under (Output\BasePath)
Site::basePath()
public function basePath(): stringThe folder of its origin the site is served under, without its trailing slash ("/repository"), or "" at the root. The application renders its pages at the root; the export moves every internal address under this folder (Output\BasePath).
Site::githubPages()
public function githubPages(bool $githubPages = true): selfExported for GitHub Pages: .nojekyll written; a CNAME file when the base URL's host is a custom domain (not <owner>.github.io); the redirections as pages only (Pages reads no _redirects); 404.html expected; every internal address of the export checked to stay under the base path (an error otherwise); Pages' limits on a file's and the site's size warned of.
Site::isForGitHubPages()
public function isForGitHubPages(): boolSite::customDomain()
public function customDomain(): ?stringThe custom domain GitHub Pages serves the site at (its CNAME file), or null on <owner>.github.io.
Site::origin()
public function origin(): stringThe scheme, host and port of the base URL ("https://example.org:8443").
Site::provide()
public function provide(StaticRouteProviderInterface ...$providers): selfSite::renderWith()
public function renderWith(StaticRendererInterface $renderer): selfSite::publicDir()
public function publicDir(string $publicDir): selfThe application's public folder: the files the pages refer to are copied from it before any is rendered.
Site::assets()
public function assets(string $manifest, string $base = ''): selfA front-end build's manifest (Vite, Webpack, Encore's entrypoints.json): every file it names is copied from the public folder (Asset\AssetManifest).
$basethe public path a Vite manifest's files are relative to ("/build")
Site::copy()
public function copy(string ...$patterns): selfPublic files copied as they are, by pattern relative to the public folder ("favicon.ico", "images/*").
Site::notFound()
public function notFound(string $path = '/404'): selfThe address rendered for 404.html: one the application does not serve (its error page, status 404).
Site::dynamic()
public function dynamic(array $patterns, ?string $host = null): selfThe addresses that cannot be static and stay on the dynamic host: a link or a form to one of them is not a broken link. Given the dynamic host's origin, their links and forms are rewritten to it.
$patternspaths, fnmatch patterns ("/login", "/admin/*": "*" crosses slashes)
Site::normalise()
public function normalise(\Closure $normaliser): selfWhat changes from one request to the next taken out of each HTML page before it is written (a nonce, a token, a timing): the export stays the same from one build to the next.
Site::followResources()
public function followResources(bool $follow = true): selfWhether a file the pages refer to that is not in the public folder (a picture an application route serves) is rendered by the renderer and written too. On by default; off, it is a broken link.
Site::resourceAttributes()
public function resourceAttributes(string ...$attributes): selfMore attributes that name a file the page's scripts load (a gallery's "data-thumb"): followed as a src is, beside the ones Link\Links knows.
Site::getResourceAttributes()
public function getResourceAttributes(): arraySite::sitemap()
public function sitemap(bool $sitemap = true): selfWhether sitemap.xml is written (on by default).
Site::providers()
public function providers(): arraySite::renderer()
public function renderer(): StaticRendererInterfaceSite::getPublicDir()
public function getPublicDir(): ?stringSite::manifests()
public function manifests(): array- Returns
manifest file, base path
Site::copies()
public function copies(): arraySite::notFoundPage()
public function notFoundPage(): ?StaticPageSite::dynamicHost()
public function dynamicHost(): ?stringSite::normalisers()
public function normalisers(): arraySite::followsResources()
public function followsResources(): boolSite::writesSitemap()
public function writesSitemap(): boolSite::isDynamic()
public function isDynamic(string $path): boolStaticPage class
final class StaticPage StaticPage.php:15
One address of the application to export: its path (an absolute URL path, no query string, no fragment), and what is known of it - the route and the parameters it was generated from, the action the route runs, its language and the same page in the other languages, whether the sitemap lists it.
A provider (StaticRouteProviderInterface) gives the pages; a renderer (StaticRendererInterface) answers each with a StaticResponse; the exporter writes what comes back.
const PAGE = 'page'An ordinary page.
const NOT_FOUND = 'not-found'The page a static host shows for an address it does not have (written to 404.html, its status 404 expected).
const RESOURCE = 'resource'A file the pages refer to (a picture, a style sheet...) found by following their links, written as it is.
public readonly string $paththe address' path, as the application serves it ("/", "/blog/hello")
public readonly ?string $routethe route it was generated from, for the diagnostics
public readonly array $parametersthe route's parameters it was generated with
public readonly ?string $actionthe action the route runs ("Class" for an invokable one, "Class::method" otherwise)
public readonly ?string $localeits language, as the application names it
public readonly array $alternatesthe same page in other languages: language => path
public readonly array $headersthe request headers to render it with (Accept...), beside the renderer's own
public readonly bool $sitemapwhether sitemap.xml lists it
public readonly string $roleself::PAGE, self::NOT_FOUND or self::RESOURCE
StaticPage::__construct()
public function __construct(public readonly string $path, public readonly ?string $route = null, public readonly array $parameters = [], public readonly ?string $action = null, public readonly ?string $locale = null, public readonly array $alternates = [], public readonly array $headers = [], public readonly bool $sitemap = true, public readonly string $role = self::PAGE)$paththe address' path, as the application serves it ("/", "/blog/hello")
$routethe route it was generated from, for the diagnostics
$parametersthe route's parameters it was generated with
$actionthe action the route runs ("Class" for an invokable one, "Class::method" otherwise)
$localeits language, as the application names it
$alternatesthe same page in other languages: language => path
$headersthe request headers to render it with (Accept...), beside the renderer's own
$sitemapwhether sitemap.xml lists it
$roleself::PAGE, self::NOT_FOUND or self::RESOURCE
StaticPage::notFound()
public static function notFound(string $path = '/404'): selfThe page a static host shows for an unknown address, rendered from an address the application does not serve.
StaticPage::withAlternates()
public function withAlternates(array $alternates): selfStaticPage::describe()
public function describe(): stringWhat the diagnostics call it: the path, and the route it comes from.
StaticRendererInterface interface
interface StaticRendererInterface StaticRendererInterface.php:14
Renders one page at build time, by the application's own code: through its host's request handling (Render\RequestRenderer: Symfony's kernel, Laravel's), by calling the route's action itself (Render\ActionRenderer), or by any callable (Render\CallableRenderer, a plain PHP application).
A renderer answers as a visitor does: no session, no cookie, nothing a signed-in visitor would see.
StaticRendererInterface::render()
public function render(StaticPage $page): StaticResponseStaticResponse class
final class StaticResponse StaticResponse.php:10
What a page answered at build time: its status, its type, its body - or, for a redirection, where it sends. Nothing of the request's own (cookies, caching headers) is kept: a static host answers every visitor alike.
public readonly int $statusthe HTTP status
public readonly string $bodythe content, as it is to be written
public readonly ?string $contentTypethe Content-Type header ("text/html; charset=UTF-8")
public readonly ?string $locationwhere a redirection sends (its Location header)
public readonly ?string $fragmenta redirection that carries the visitor's own #fragment over: the prefix it gains on the way ("" keeps it as it is; null drops it, as a meta refresh does)
const FRAGMENT_HEADER = 'X-Static-Fragment'The header a host's response names the fragment's prefix with, for RequestRenderer: a page of a manual merged into one page answers
Location: /docs/flowjs#stickyandX-Static-Fragment: sticky-- /docs/flowjs/sticky#keys then lands on /docs/flowjs#sticky-keys.
StaticResponse::__construct()
public function __construct(public readonly int $status, public readonly string $body = '', public readonly ?string $contentType = null, public readonly ?string $location = null, public readonly ?string $fragment = null)$statusthe HTTP status
$bodythe content, as it is to be written
$contentTypethe Content-Type header ("text/html; charset=UTF-8")
$locationwhere a redirection sends (its Location header)
$fragmenta redirection that carries the visitor's own #fragment over: the prefix it gains on the way ("" keeps it as it is; null drops it, as a meta refresh does)
StaticResponse::html()
public static function html(string $body, int $status = 200): selfStaticResponse::json()
public static function json(string $body, int $status = 200): selfStaticResponse::bytes()
public static function bytes(string $body, int $status = 200): selfA file served as application/octet-stream - what a static host answers for a path without a known extension -, for a reader that takes it so: npm's and Unity's registry clients read /<package> and /-/v1/search as JSON whatever the type (omnisense/omnihub's static hub).
Exported at its path, extension or not.
StaticResponse::isBytes()
public function isBytes(): boolWhether it is served as bytes (application/octet-stream): exportable at a path without extension.
StaticResponse::redirect()
public static function redirect(string $location, int $status = 301, ?string $fragment = null): self$fragmentthe visitor's #fragment carried over with this prefix ("" as it is); null: dropped
StaticResponse::notFound()
public static function notFound(string $body = ''): selfStaticResponse::isSuccessful()
public function isSuccessful(): boolStaticResponse::isRedirect()
public function isRedirect(): boolStaticResponse::mediaType()
public function mediaType(): ?stringThe media type alone, lower-cased ("text/html"), or null when none was given.
StaticResponse::isHtml()
public function isHtml(): boolStaticResponse::isCss()
public function isCss(): boolStaticResponse::withBody()
public function withBody(string $body): selfThe same response with another body (a normaliser's, a rewritten link's).
StaticRouteProviderInterface interface
interface StaticRouteProviderInterface StaticRouteProviderInterface.php:14
Where the addresses to export come from: an explicit list (Route\PageList), a router's routes with the values of their parameters (Route\RouteCollectionProvider), the application's own sitemap (Route\SitemapProvider), or the application's own class.
The order the pages come in is kept in the diagnostics; the files written do not depend on it.
StaticRouteProviderInterface::pages()
public function pages(): iterableOmnibridge\Static\Asset
AssetManifest class
final class AssetManifest AssetManifest.php:19
The files a front-end build produced, read from its manifest, whichever tool wrote it - the exporter copies them from the public folder, as the bundler fingerprinted them:
- Vite's manifest.json (build.manifest): entries with "file", "css", "assets", relative to Vite's output folder - $base is that folder's public path ("/build");
- Webpack's manifest.json (webpack-manifest-plugin, Symfony Encore's): logical name => public path ("build/app.js" => "/build/app.3f2a.js");
- Encore's entrypoints.json: {"entrypoints": {"app": {"js": [...], "css": [...]}}}.
No bundler is required, none is run: a manifest is read, nothing else.
AssetManifest::read()
public static function read(string $file, string $base = ''): arrayThe public paths the manifest names, sorted, each once. An address on another host (a CDN) is not one of them.
$basethe public path a Vite manifest's files are relative to
AssetManifest::parse()
public static function parse(string $json, string $base = ''): arrayOmnibridge\Static\Console
Cli class
final class Cli Cli.php:29
The command line, plain PHP (bin/omnibridge-static), which the Symfony command and the artisan command of omnibridge/symfony and omnibridge/laravel call with their host:
omnibridge-static build static.php [--out=dist] [--target=github-pages] [--verify] [--strict] [--quiet] omnibridge-static check dist
build: the site the file declares (it returns a Site, or a closure that takes the host's offer - Cli::HOST_* - and returns one) exported to --out; the exit code is 1 when there is an error (with --strict, a warning too). --target=github-pages exports for GitHub Pages (Site::githubPages()). check: an export's files against its omnibridge-static.json (each there, the same checksum, nothing more) and, for a site under a folder of its origin, no internal address outside that folder - what a deployment step runs before it publishes.
const HOST_RENDERER = 'renderer'What a host hands the site's file: a renderer through its request handling.
const HOST_ROUTES = 'routes'...its routes (a symfony/routing RouteCollection).
const HOST_CONTAINER = 'container'...its container (PSR-11).
const HOST_PUBLIC_DIR = 'publicDir'...its public folder.
const HOST_NAME = 'host'...its name ("symfony", "laravel").
Cli::__construct()
public function __construct(readonly ?\Closure $out = null)$outwhere the lines go (STDERR by default)
Cli::load()
public static function load(string $file, array $host = []): SiteThe site a file declares.
$hostwhat the host offers (Cli::HOST_*), empty from the plain command line
Cli::run()
public function run(array $arguments, array $host = []): int$argumentsthe command line's, without the script's name
$hostwhat the host offers (Cli::HOST_*)
Cli::build()
public function build(string $file, array $options, array $host = []): intCli::check()
public function check(string $folder): intOmnibridge\Static\Link
Links class
final class Links Links.php:20
The addresses a page or a style sheet refers to, and the rewriting of some of them: what the exporter follows to check that every internal link resolves within the export, to collect the files the pages need, and to send a visitor to the dynamic host for what cannot be static.
Read from the markup's tags (not from scripts nor comments): a link's href (PAGE), a form's action (FORM, or POST for a form posted; "" for a form without one, posted to the document's own address), a src, a srcset, a poster (and the data-src, data-srcset, data-poster of the lazy loaders), a <link>'s href, a url() of a style attribute or a <style>, an SVG's <use href> and <image href> (RESOURCE), a <meta http-equiv="refresh">'s address (PAGE); from a style sheet, its url() and @import. rewriteAddresses() and rewriteCss() rewrite the same addresses (Output\BasePath moves them under a site's base path).
const PAGE = 'page'const FORM = 'form'const POST = 'post'A form posted (method="post"): a static host takes no post.
const RESOURCE = 'resource'
Links::html()
public static function html(string $html, array $resources = []): array$resourcesmore attributes that name a file a script loads (a gallery's "data-thumb")
- Returns
[kind, address], in the order they come, each address as written (entities decoded)
Links::css()
public static function css(string $css): array- Returns
[kind, address]
Links::rewrite()
public static function rewrite(string $html, \Closure $rewrite): stringThe markup with the PAGE and FORM addresses $rewrite changes rewritten: it is given the address as written (a form without an action: "", the document's own address), the kind and the tag's attributes, and returns the new address, or null to leave it. Scripts and comments are left untouched.
Links::rewriteAddresses()
public static function rewriteAddresses(string $html, \Closure $rewrite, array $resources = []): stringThe markup with every address html() reads that $rewrite changes rewritten (with a script's src, a <style>'s url()s, each candidate of a srcset, a refresh's address): it is given the address as written (entities decoded) and its kind, and returns the new address or null to leave it. Scripts' bodies and comments are left untouched, and so is every attribute whose address does not change.
$resourcesmore attributes that name a file (Site::resourceAttributes())
Links::rewriteCss()
public static function rewriteCss(string $css, \Closure $rewrite): stringA style sheet with the addresses of its url()s and @imports that $rewrite changes rewritten; its comments left untouched.
Omnibridge\Static\Output
BasePath class
final class BasePath BasePath.php:38
The folder of its origin a static site is served under ("/repository" on https://owner.github.io/repository/), and the moving of an export's internal addresses under it. The application renders its pages at the root of the origin; once they are written, every address that names the site is moved under the base path:
- HTML: every address Link\Links reads that starts with "/" (a link, a form, a src, a srcset, a poster, a <link>, a style's url(), a refresh, the attributes Site::resourceAttributes() adds) gets the base path; in a page that may be served at another address than its own (404.html, served at any depth; on GitHub Pages, every page, which /a/b redirects to /a/b/) a relative address is resolved against the address it was rendered at first.
- CSS: its url()s and @imports.
- JSON (and .webmanifest): a string value that is the root-relative address of a page or file of the export ("/guides/start").
- Everywhere (HTML, CSS, JSON, XML feeds, sitemaps, robots.txt, SVG): an absolute URL of the origin ("https://owner.github.io/x", as written, in JSON's escaped form, or percent-encoded in a share link's query), the canonical and hreflang links among them.
An address already under the base path is left as it is: an application that knows its base path (a bundler's public path) writes them so. Not read: what a script builds at run time (fetch("/api")) - a script reads its addresses from the page (a data- attribute named by Site::resourceAttributes(), document.baseURI) to be moved with it.
escapes() lists the addresses of a written file that still point outside the base path: the exporter's check, and the command line's.
const HTML = 'html'const CSS = 'css'const JSON = 'json'const TEXT = 'text'public readonly string $originthe site's scheme, host and port ("https://owner.github.io")
public readonly string $paththe folder, without its trailing slash ("/repository"), or "" at the root
BasePath::__construct()
public function __construct(public readonly string $origin, public readonly string $path)$originthe site's scheme, host and port ("https://owner.github.io")
$paththe folder, without its trailing slash ("/repository"), or "" at the root
BasePath::isRoot()
public function isRoot(): boolBasePath::contains()
public function contains(string $path): boolWhether an address' path is the base path or under it ("/repository", "/repository/x?y").
BasePath::strip()
public function strip(string $path): stringAn address under the base path as the application serves it ("/repository/x" => "/x").
BasePath::prefix()
public function prefix(string $path): stringA root-relative address moved under the base path ("/x" => "/repository/x"), unless it is already.
BasePath::kind()
public static function kind(string $file): ?stringWhat a file of the export is, by its extension: self::HTML, CSS, JSON, TEXT, or null (a script, a picture...).
BasePath::apply()
public function apply(string $kind, string $body, \Closure $exported, ?string $resolveFrom = null, array $resources = []): stringA written file with its internal addresses moved under the base path.
$kindself::kind() of the file
$exportedwhether a root-relative path names a page or file of the export (for JSON)
$resolveFromthe address a relative address is resolved against first (404.html: the address it was rendered at)
$resourcesSite::resourceAttributes()
BasePath::escapes()
public function escapes(string $kind, string $body, \Closure $exported, array $resources = []): arrayThe addresses of a written file that point outside the base path: root-relative addresses (and, in JSON, the root-relative addresses of the export's pages and files) and absolute URLs of the origin.
- Returns
each once, in the order they come
BasePath::describe()
public function describe(array $escapes): stringWhat escapes() found, in a line.
OutputPath class
final class OutputPath OutputPath.php:14
Where an address is written in the export, and the URL rules the exporter follows: "/" and "/blog/hello" are HTML pages written as index.html in their folder (a static host serves /blog/hello from blog/hello/index.html: nginx's try_files $uri $uri/index.html, the "pretty URLs" of the static hosts); a page whose path ends in .html, and any other file, are written at their path. Paths are percent-decoded: the file of /caf%C3%A9 is café/index.html, as a static host decodes the address before it looks.
OutputPath::page()
public static function page(string $path): stringThe file an HTML page is written to, relative to the export ("blog/hello/index.html").
OutputPath::file()
public static function file(string $path): stringThe file any other address is written to, relative to the export ("build/app.css").
OutputPath::hasExtension()
public static function hasExtension(string $path): boolWhether the last segment of a path has an extension (".css", ".json").
OutputPath::key()
public static function key(string $path): stringAn address' path as the exporter compares it: percent-decoded, without its trailing slash ("/" stays "/").
OutputPath::resolve()
public static function resolve(string $base, string $reference): stringA reference resolved against the address of the document it is in (RFC 3986's merge and dot segments): the path and query of the address it names.
Omnibridge\Static\Render
ActionRenderer class
final class ActionRenderer implements StaticRendererInterface ActionRenderer.php:31
Renders a page by calling the action its route runs, with no host's request
handling: the action taken from a PSR-11 container, called with the route's
parameters by name (and $query, an empty query string), its other
parameters taken from the container by their type, or left to their
default; what it returns handed to $answer, the application's own way of
turning an action's result into a page (omnibase's Base\Screen\Negotiation,
a template engine...).
new ActionRenderer($container, function (mixed $result, StaticPage $page) use ($negotiation): StaticResponse { $answer = $negotiation->answer($result, new Visit($page->path, 'text/html', null, null));
return new StaticResponse($answer->status, $answer->body, $answer->headers['Content-Type'] ?? 'text/html; charset=UTF-8');
});The page names its action (StaticPage::$action, which RouteCollectionProvider fills from the route's _controller). An action that returns null is no such page (404). What a host adds around an action - its layout listeners, its request-scoped state - is not there: the action's result alone is rendered. RequestRenderer renders the page as the host serves it.
ActionRenderer::__construct()
public function __construct(readonly ContainerInterface $container, readonly \Closure $answer)ActionRenderer::render()
public function render(StaticPage $page): StaticResponseCallableRenderer class
final class CallableRenderer implements StaticRendererInterface CallableRenderer.php:16
A renderer of any callable: a plain PHP application renders its pages its own way (its templates, its data) and answers a StaticResponse - or a string, an HTML page with status 200, or null, no such page (404).
new CallableRenderer(fn (StaticPage $page) => $twig->render('page.html.twig', ['path' => $page->path]));
CallableRenderer::__construct()
public function __construct(readonly \Closure $render)CallableRenderer::render()
public function render(StaticPage $page): StaticResponseRequestRenderer class
final class RequestRenderer implements StaticRendererInterface RequestRenderer.php:30
Renders a page the way the host serves it: a GET request of its address handled in this process by the host's own request handling - the route matched, the same action run, the same templates rendered, the host's listeners on the way - with symfony/http-foundation's Request and Response, which Symfony's kernel and Laravel's both speak (a Laravel request extends it).
// Symfony new RequestRenderer(fn (Request $request) => $kernel->handle($request), 'https://example.org'); // Laravel new RequestRenderer(fn (Request $request) => $app->make(Kernel::class)->handle(\Illuminate\Http\Request::createFromBase($request)), 'https://example.org');
The request carries no cookie and no session: the page is rendered for a visitor. The host's terminate step is not run (a host that counts a page view there counts none for the export).
const USER_AGENT = 'omnibridge-static'
RequestRenderer::__construct()
public function __construct(readonly \Closure $handle, readonly string $baseUrl, readonly array $headers = [])$handlethe host's handling of one request
$baseUrlthe public origin the pages are rendered for ("https://example.org"): the host, scheme and port the application sees
$headersthe headers every request carries (Accept-Language...), beside a page's own
RequestRenderer::render()
public function render(StaticPage $page): StaticResponseOmnibridge\Static\Route
PageList class
final class PageList implements StaticRouteProviderInterface PageList.php:13
The plainest provider: the addresses written out, as paths or as pages.
new PageList(['/', '/about', '/blog/hello']);
PageList::__construct()
public function __construct(readonly iterable|\Closure $pages)PageList::pages()
public function pages(): iterableRouteCollectionProvider class
final class RouteCollectionProvider implements StaticRouteProviderInterface, SkipReportingInterface RouteCollectionProvider.php:38
The pages of a router's routes (symfony/routing's RouteCollection - what Symfony's router holds, what omnibridge/laravel's Routing\ActionRoutes reads from an application's #[Route] actions, what symfony/routing's attribute loader gives a plain PHP application).
A route without a variable is one page. A route with variables is exported for the values a source gives, by the route's name (or, for a localized route such as "blog.fr", by its canonical name "blog"); without one it is left out and said so (skipped()): a parameter has no values the exporter could guess.
new RouteCollectionProvider($router->getRouteCollection(),
parameters: ['article' => fn () => [['slug' => 'hello'], ['slug' => 'world']]],
include: ['app_*'],
exclude: ['app_*_comments'],
);Only the routes that answer GET are read. A localized route's language is the page's locale, and the same parameters under the route's other languages its alternates (sitemap.xml lists them). The path is generated by symfony/routing's UrlGenerator: requirements are checked, a parameter the path does not name is not added as a query string.
RouteCollectionProvider::__construct()
public function __construct(readonly RouteCollection $routes, readonly array $parameters = [], readonly ?array $include = null, readonly array $exclude = [], readonly array $actions = [], readonly array $noSitemap = [])$parametersroute name (or canonical name) => the parameter sets
$includethe routes to read, by name (fnmatch patterns); null for all
$excludethe routes to leave out, by name (fnmatch patterns)
$actionsthe actions to read, by class prefix ("App\Action\"); empty for all
$noSitemapthe routes whose pages sitemap.xml does not list (fnmatch patterns)
RouteCollectionProvider::pages()
public function pages(): iterableRouteCollectionProvider::skipped()
public function skipped(): arraySitemapProvider class
final class SitemapProvider implements StaticRouteProviderInterface, SkipReportingInterface SitemapProvider.php:22
The pages an application's own sitemap lists (sitemaps.org's urlset, its xhtml:link alternates): a site that already enumerates its addresses for the crawlers enumerates them for the export too. The sitemap is read from a file, or rendered at build time by the renderer (its path, "/sitemap.xml" by default). A sitemap index (sitemapindex) is followed, one level.
new SitemapProvider(renderer: $renderer); new SitemapProvider(file: 'public/sitemap.xml');
An address with a query string, or on another host than the sitemap's first, is left out and said so (skipped()).
SitemapProvider::__construct()
public function __construct(readonly ?StaticRendererInterface $renderer = null, readonly string $path = '/sitemap.xml', readonly ?string $file = null)SitemapProvider::pages()
public function pages(): iterableSitemapProvider::skipped()
public function skipped(): arraySkipReportingInterface interface
interface SkipReportingInterface SkipReportingInterface.php:11
A provider that leaves routes out says which and why (a route of a method other than GET, a parameter nobody gave values for...): the exporter lists them in its diagnostics, so that nothing is left out silently.
SkipReportingInterface::skipped()
public function skipped(): arrayWhat the last pages() left out.
- Returns
subject (a route's name) => why