PHP class library
Every autoloaded class available in pages, before/after includes, prepros.includes, and kiri run scripts.
All classes below are autoloaded — no require anywhere. Every static method also has a plain-function alias (aliases at the bottom) — the two are the same API, pick whichever reads better in a given page.
PREPROS
The render engine itself.
PREPROS::$config // stdClass: ->data = kirigami: block, ->image, ->before/after/format/network
PREPROS::registerTag(string $tag, callable $cb) // $cb($fullTag, array $attrs, string $body): string
PREPROS::registerHook(string $hook, callable $cb) // hooks: page_info, pre_render, post_render
PREPROS::mount(string|array $globPatterns) // mount extra local files (any extension) into the WASM FS
PREPROS::exportFile(string|array $absPath) // mark a file as build output
PREPROS::getExportedFiles(): string[]
PREPROS::fstat(string $path) // stat a file in the WASM FS, or false
PREPROS::backtraceFile() // path of the page currently rendering
See Writing pages for the render pipeline these hooks fire in.
MD
Markdown → HTML.
$html = MD::toHtml(string $markdown): string;
MD::registerPlugin(string $name, callable $cb); // $cb(array $args, string $body): string
MD::unregisterPlugin(string $name);
MD::getRegisteredPlugins(): string[];
MD::registerEmoji(string $shortcode, string $char);
Supports GFM (tables with alignment, task lists, alerts, strikethrough, autolinks), ATX + Setext headings with an auto id (what every anchor link on this page targets), fenced code with a language class, footnotes, definition lists, emoji shortcodes, hard line breaks, and allowlist-sanitized inline HTML. External links get target="_blank" rel="noopener noreferrer"; images get loading="lazy".
HTML
HTML::format(string $html): string; // PHP 8.4 Dom\HTMLDocument (Lexbor), 4-space indent
Runs automatically when prepros.format: true. Handles inline elements, <script>, <style>; writes boolean HTML5 attributes without a value; keeps SVG/MathML foreign-content casing (viewBox, linearGradient, …) intact.
YAML
YAML::parse(string $yaml, bool $assoc = false): mixed;
YAML::parseFile(string $path, bool $assoc = false): mixed;
YAML::loadFile(string $path, bool $assoc = false): mixed; // parseFile + recursively inline nested .yaml/.yml/.json refs
Backed by the native yaml extension (LibYAML), compiled into the runtime. Mappings are stdClass unless $assoc = true; loadFile() throws on a circular reference. LibYAML follows YAML 1.1: unquoted yes, no, on, off, y and n are booleans, keys included, so quote them when you mean the word ("NO": Norway).
SCHEMA
A JSON Schema validator backed by the native jsonk extension (draft 2020-12).
$v = new SCHEMA(array $schema);
$v->isValid(mixed $data): bool; // alias: validate()
$v->getErrors(): string[]; // "path: message" from the last run
It covers draft 2020-12, including if/then/else, contains, propertyNames, prefixItems, dependentRequired and $ref to local or $id-relative schemas. The previous pure-PHP validator stays available as SCHEMA_LEGACY.
LD
A schema.org JSON-LD graph builder, injected automatically from the seo block (seo.jsonld: false turns it off).
LD::add(string|array $type, array $props = [], ?string $id = null): array // build + register a node
LD::node(string|array $type, array $props = []): array // build only, no register
LD::push(array $node): array // register a ready-made node
LD::ref(string $id): array // ['@id' => …] ('#person' → the Person node)
LD::remove(string $id): void
LD::graph(): array
LD::reset(): void
LD::organization(array $overrides = []): array // config-aware, @id #organization
LD::person(array $overrides = []): array // config-aware, @id #person
LD::website(array $overrides = []): array // config-aware, @id #website
LD::webPage(array $overrides = []): array // current-page-aware, @id …#webpage
LD::breadcrumb(?array $items = null, array $overrides = []): array // items: [['name'=>…,'url'=>…], …]
LD::faqPage(array $qa, array $overrides = []): array // qa: ['Question ?' => 'Answer.', …]
LD::address(array|string $a): array
LD::image(string $url, ?string $id = null, ?int $w = null, ?int $h = null): array
LD::geo(float $lat, float $lng): array
LD::rating(int|float $value, ?int $count = null, $best = 5, $worst = 1): array
LD::offer(array $o): array
LD::contactPoint(array $c): array
LD::searchAction(string $urlTemplate): array
LD::script(bool $pretty = true): string // <script>…</script>, and disables auto-injection
LD::json(bool $pretty = true): string // the document, no wrapper
Any other schema.org type works through LD::typeName([...]) — LD::recipe([...]), LD::event([...]), LD::softwareApplication([...]), and so on — via __callStatic.
LD::article([
'headline' => 'Announcing v2',
'author' => LD::ref('#person'),
'image' => LD::image('images/cover.webp'),
'publisher' => LD::ref('#organization'),
]);
META
A <head> SEO / social metadata generator — the companion to LD, and the engine behind the seo: block. Manual builders are always emitted, whether or not the config block is present, and are de-duplicated against whatever the page already writes by hand:
META::tag(string $name, ?string $content): void // name= , or property= for an og:* key
META::link(string $rel, string $href, array $attrs = []): void
META::raw(string $html): void // a verbatim, already-valid tag line
META::tags(string $html = ''): string // the whole block, joined
META::reset(): void
META::tag('twitter:image', 'https://example.com/card.png');
META::link('icon', './favicon.svg', ['type' => 'image/svg+xml']);
CACHE
Persistent key/value store, SQLite-backed (.cache.db), survives incremental builds. Backs SCRAPER results and CURL cookie persistence — this site's own kirigami_pkg_version() (powering /ecosystem/) uses it directly for a 1-hour TTL on npm registry lookups.
CACHE::get(string $key): mixed; // null if missing/expired
CACHE::set(string $key, mixed $val, int $ttl = 0): bool; // ttl seconds, 0 = forever
CACHE::delete(string $key): bool;
CACHE::purge(): bool; // drop expired entries
IMG
The image autogenerator's engine — GD, with an Imagick fallback for HEIC/TIFF/BMP and vector formats (SVG/EPS/AI/PDF).
$img = new IMG(string $file);
$img->width; $img->height;
$img->resize(int $w, int $h = 0, bool $cover = false): self; // contain by default; cover crops+fills
$img->save(string $dest): self; // format from extension: jpg png gif webp avif
$img->getRepresentativeColors(int $count = 5): string[]; // ['#rrggbb', …] (median cut)
$img->getAuraColors(int $count = 5): string[]; // ['#rrggbb', …] (Aura, max 6)
IMG::asset(string $path, int $w = 0, int $h = 0, bool $cover = false): string; // → generated file URL, relative to caller
IMG::palette(string $path, int $colors = 5): string[]; // CACHE-backed, Aura (max 6)
asset() resolves $path against image.source, writes into image.dest only when missing or stale, and names files <name>-<W>w / -<H>h / -<W>x<H>[-cover].<format>. The same call is reachable from Sass (img-asset()) and from markup (<img asset>) — see config → image.
palette() extracts colours with Aura, an extension built into the PHP runtime. Aura picks up to six named swatches (vibrant and muted, each in a dark and a light variant) instead of clustering an arbitrary number of colours, so it returns at most six, the most present first, and asking for more just returns what it found. Swatches Aura had to invent because the image has no such colour are dropped. The same list feeds Sass's colors().
assets/images/ — the wide crop is <img asset width="480"> (contain), the square is <img asset width="220" height="220" cover>, and the swatches are IMG::palette(). Photo: Giles Laurent, Wikimedia Commons (CC BY-SA).FS
FS::dig(string $glob): iterable; // recursive glob, yields paths
FS::getRelativePath(string $from, string $to): string;
FS::phpFileInfo(string $file): object|false; // parse first PHPDOC block → stdClass
FS::getChildren(string $backtrace = ''): object[]; // render-only: child _index.php pages, sorted by @position then folder name
FS::getBreadcrumb(string $backtrace = ''): object[]; // render-only: ancestor _index.php pages, opt-in via @breadcrumb true
FS::rmdir(string $dir, bool $removeSelf = true): bool;
FS::pathJoin(string ...$parts): string; // URL-aware, resolves ..
getChildren() and getBreadcrumb() are what builds the card grid and the "Docs / …" trail on every page in this section — getChildren() scans the calling page's own sub-folders for _index.php, sorted by an optional @position tag then name; getBreadcrumb() walks upward from the calling page, collecting ancestor _index.php pages, but only while each one — the caller included — carries @breadcrumb true. The first ancestor without it stops the walk and is excluded, so a trail can stop short of Home on purpose.
STR
STR::htmlesc(string $s): string;
STR::replaceTags(string $tag, string $html, callable $cb): string; // engine behind registerTag()
STR::parseHtmlAttributes(string $attrString): array;
STR::trimIndent(string $s): string;
STR::is_url(string $s): bool;
STR::html_entities_decode(string $s): string;
STR::shorthash(string $s): string; // first 12 chars of sha-256
STR::normalize(string $s): string; // Unicode NFD + strip combining marks (é → e)
STR::slug(string $s, string $sep = ''): string; // '' → compact id; '-' → hyphenated slug
ARR
ARR::find_key(mixed $data, string $key): mixed; // depth-first, first match at any depth, or null
CURL
Browser-like headers, cookie jar persisted at .cookie.txt. Needs prepros.network: true.
CURL::urlExists(string $url, ?string $mimeRegex = null): bool; // HEAD, true on 2xx/3xx
CURL::getInfo(string $url): array|false;
CURL::getContents(string $url, ?string $dest = null, ?callable $onProgress = null): string|bool;
SCRAPER
Link-preview metadata — pulls JSON-LD / Open Graph / <meta> tags from a URL, cached indefinitely via CACHE. Needs prepros.network: true.
$m = SCRAPER::get(string $url): object|false; // ->title ->description ->image ->label ->url
OBF
Light obfuscation only — contact data on a page, that kind of thing. Not encryption.
OBF::encode(mixed $obj): string; // JSON → base64 → ROT-13 → gzip
OBF::decode(string $s): mixed;
STD
Internal to the build runner — useful in a kiri run script that needs to end early with a custom result.
STD::succeed(array|string $props = []): void; // exit 0, JSON to stdout
STD::error(array|string $props = []): void; // exit 1, JSON to stderr
Aliases
libraries/aliases.inc.php exposes every static method above as a plain function, named <lowercase class>_<snake_case method>() — each carries the same docblock, so editor hover shows the full signature. A sample, not the full list:
md_to_html($md) // MD::toHtml()
html_format($html) // HTML::format()
yaml_load_file($path) // YAML::loadFile()
schema($schema) // new SCHEMA() — plus schema_validate($schema, $data, $errors)
cache_get() / cache_set() / cache_delete() / cache_purge()
img_asset($path, $w, $h, $cover) // IMG::asset(), URL relative to the calling file
fs_dig() / fs_get_relative_path() / fs_get_children() / fs_get_breadcrumb()
str_htmlesc() str_slug() str_shorthash()
arr_find_key($data, $key)
curl_get_contents() / curl_get_info() / curl_url_exists()
scraper_get($url)
obf_encode() / obf_decode()
std_succeed() / std_error()
meta_tag() / meta_link() / meta_raw() / meta_tags()
ld_add() / ld_organization() / ld_article() / ld_script()
prepros_render() prepros_mount() prepros_backtrace_file()
register_tag() / register_hook() // = prepros_register_tag / prepros_register_hook
md_register_plugin() md_register_emoji()
Normalizer
PHP's Normalizer class (Normalizer::normalize() / isNormalized(), NFC/NFD/NFKC/NFKD) comes from the native norm extension, compiled into the runtime. Installing the optional @kirigami/phpext-intl package replaces it with ICU's. The previous pure-PHP polyfill stays available as NORMALIZER_LEGACY.