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
A zero-dependency parser: scalars, quoted strings + escapes, block scalars (|, >, with chomping), multi-line plain scalars, nested maps/sequences, inline [a, b] / {k: v}, comments, multi-doc ---. Mappings are stdClass unless $assoc = true. loadFile() throws on a circular reference.
SCHEMA
A pure-PHP JSON Schema validator — no ajv, same as every other "stay lite" call in the toolchain.
$v = new SCHEMA(array $schema);
$v->isValid(mixed $data): bool; // alias: validate()
$v->getErrors(): string[]; // "path: message" from the last run
Draft-7-ish: type, required, properties, patternProperties, additionalProperties, items, min/maxItems, uniqueItems, min/maxLength, pattern, minimum/maximum (+ exclusive*), min/maxProperties, enum, const, anyOf/allOf/oneOf/not, format, local $ref.
LD
A schema.org JSON-LD graph builder — the engine behind the seo.jsonld sub-block.
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', …]
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
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.
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()
Bundled polyfill
ext-intl isn't in the WASM build, so a Normalizer polyfill is autoloaded (Normalizer::normalize() / isNormalized() + NFC/NFD/NFKC/NFKD constants) — prefer STR::normalize() in your own code; the polyfill exists for third-party snippets that expect the real extension.