Skip to content

omnibase/docs API reference

Floor
Prepared structure · Omnibase
Version
2.0
Verified for real
Its README does not say
Licence
MIT
composer require omnibase/docs:2.0.x-dev

Read from the package's own PHP files at the commit 259f0ef9ecb3: 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.

Base\Wikidoc

  • WikidocBundle class

    The name the sites register (config/bundles.php) and omnibase's bundle scan reads (a class Base\<Name>\<Name>Bundle, its folder src/): the bundle itself is Base\Wikidoc\Bridge\Symfony\WikidocBundle, since step C8 of the plan "omnibase agnostique".

    1 method

Base\Wikidoc\Bridge\Symfony

  • WikidocBundle class

    omnibase/docs under Symfony: the container's services (config/services.php), the `wikidoc:` configuration, the controllers, the commands, the templates as @Wikidoc. The documentation itself - the roots, the manuals, their Markdown, the search - is the bundle's root namespace and loads without the framework (tests/Fondation.php, plan "omnibase agnostique", step C8).

    2 methods

Base\Wikidoc\Bridge\Symfony\Command

  • IndexCommand class

    Fills the manuals' search index: one Typesense collection per manual and version (title, sections, text), rewritten from the files. Without the engine - not installed, not switched on, or stopped - there is nothing to fill: the search reads the pages' own index, and this says so.

    1 method

  • SyncCommand class

    The versions of the manuals: for each repository, the documentation of the branches that are not checked out is copied under the export folder (`wikidoc.export_dir`). The branch checked out needs no copy: it is read where it is.

    1 method

Base\Wikidoc\Bridge\Symfony\Controller\Backend

Base\Wikidoc\Bridge\Symfony\DependencyInjection

Base\Wikidoc\Bridge\Symfony\Search

  • BundleTypesenseClient class

    Typesense through glitchr/typesense-bundle: its manager's connection (the default one, or the one `wikidoc.search.typesense.connection` names) gives the client; the manuals' collections are created here, not from an entity mapping - a page is a file, not a row. The bundle's glue (a Symfony bundle's service): any other client implements TypesenseClientInterface.

    7 methods

Base\Wikidoc\Bridge\Symfony\Site\Controller

  • ManualController class

    The manuals, on the public site, the way symfony.com/doc publishes its own: a manual per package, a version per branch, each page with the manual's contents, a breadcrumb, previous and next, its own headings, and a link to the file it is edited in.

    5 methods

Base\Wikidoc\Controller\Backend

  • ManualController class

    The back office's manual where the sites import it: they read the routes of the folder @WikidocBundle/src/Controller (config/routes.yaml), and get them from this class, which inherits them. The controller itself is Base\Wikidoc\Bridge\Symfony\Controller\Backend\ManualController since step C8 of the plan "omnibase agnostique"; both are services (config/services.php).

Base\Wikidoc\Documentation

  • DocPage class

    One markdown file, resolved to the page the admin actually renders.

    4 methods · 7 properties

  • DocumentationRegistry class

    Builds the documentation tree from markdown on disk, across several roots.

    11 methods

  • MarkdownRenderer class

    Markdown -> HTML for the manual, plus the plain-text extraction the search index is built from.

    5 methods

  • SearchIndexBuilder class

    The payload behind the ⌘K palette: one flat list of searchable records, built once per set of source files and matched entirely in the browser.

    2 methods

Base\Wikidoc\Manual

  • BranchExporter class

    Copies the documentation of a branch that is not checked out - its docs/ folder and its README - out of the repository, under the export folder, where ManualRegistry finds it as a version of the manual.

    5 methods

  • Git class

    What a manual needs to know of a repository, read in its `.git` folder: the branch checked out, the branches there are, where it is published.

    5 methods

  • Manual class

    One manual: the documentation of one package, read in its repository (its `docs/` folder and its README), in one or several versions.

    6 methods · 8 properties

  • ManualLocation class

    What an address under the manuals' prefix names: a manual, one of its versions, a page path inside it ("" is the manual's home).

    1 method · 4 properties

  • ManualMarkdownRenderer class

    The public manuals' Markdown: what the back office's renderer reads, plus

    2 methods · 1 constant

  • ManualRegistry class

    The manuals a site publishes: the ones its configuration names (`wikidoc.manuals`) and the ones found on disk (`wikidoc.discover`: every folder a pattern matches that holds a composer.json or a package.json).

    10 methods

  • ManualVersion class

    One version of a manual: a branch of the package's repository.

    5 methods · 6 properties

  • PageRenderer class

    Renders one page of a manual for the public site: the Markdown to HTML, its relative links rewritten, its headings listed, its "edit this page" address worked out.

    5 methods · 1 constant

  • RenderedPage class

    A manual's page, ready for the template: its HTML, the headings its "on this page" list is made of, the address its source is edited at.

    1 method · 6 properties

Base\Wikidoc\Search

  • LocalSearch class

    The search without an engine: the manuals' records matched in PHP, the way the back office's palette matches them in the browser. Every word of the query must be found; a title outranks a heading, a heading the text, and the start of a word the middle of one. No typo tolerance: that is what Typesense adds.

    4 methods

  • ManualIndex class

    The searchable records of a manual's version: one per page and one per heading - title, section, text. They are SearchIndexBuilder's, the index the back office's palette has always used; here it is built per manual and version, sent to Typesense by `wikidoc:index`, and searched in PHP when the engine does not answer.

    3 methods

  • ManualSearch class

    The manuals' search: Typesense when it is switched on and answers (`wikidoc.search.typesense`), the pages' own index otherwise. The result says which of the two answered.

    4 methods

  • SearchHit class

    One result: a page of a manual, or a section of it.

    2 methods · 10 properties

  • SearchResult class

    What a search returned, and who answered: "typesense" (the engine) or "local" (the pages' own index, when the engine is off or does not answer).

    2 methods · 4 properties · 2 constants

  • TypesenseClientInterface interface

    The few calls the manuals make to Typesense. BundleTypesenseClient makes them through glitchr/typesense-bundle's connection; a test hands in its own. Every method may throw: the caller falls back on the local index.

    6 methods

  • TypesenseIndex class

    The manuals in Typesense: one collection per manual and version - `<prefix>_<manual>_<version>`, fields title, section, text - filled by `wikidoc:index` from the same records the local search reads.

    6 methods

Base\Wikidoc\Site\Controller

  • ManualController class

    The public manuals where the sites import them: they read the routes of the folder @WikidocBundle/src/Site/Controller (config/routes.yaml), and get them from this class, which inherits them. The controller itself is Base\Wikidoc\Bridge\Symfony\Site\Controller\ManualController since step C8 of the plan "omnibase agnostique"; both are services (config/services.php), both removed unless `wikidoc.public.enabled`.

Base\Wikidoc

WikidocBundle class

class WikidocBundle extends WikidocBundle WikidocBundle.php:13

The name the sites register (config/bundles.php) and omnibase's bundle scan reads (a class Base\<Name>\<Name>Bundle, its folder src/): the bundle itself is Base\Wikidoc\Bridge\Symfony\WikidocBundle, since step C8 of the plan "omnibase agnostique".

WikidocBundle::__construct()

public function __construct()

Base\Wikidoc\Bridge\Symfony

WikidocBundle class

class WikidocBundle extends AbstractBaseBundle WikidocBundle.php:18

omnibase/docs under Symfony: the container's services (config/services.php), the wikidoc: configuration, the controllers, the commands, the templates as @Wikidoc. The documentation itself - the roots, the manuals, their Markdown, the search - is the bundle's root namespace and loads without the framework (tests/Fondation.php, plan "omnibase agnostique", step C8).

Sites register it as Base\Wikidoc\WikidocBundle, the subclass that keeps that name.

WikidocBundle::__construct()

public function __construct()

WikidocBundle::getPath()

public function getPath(): string

Modern bundle layout: the bundle root is the package root - so TwigBundle picks up ./templates as @Wikidoc (see the identical fix on Base\Admin\AdminBundle), and @WikidocBundle/src/Controller is the folder the sites import, wherever the class that registers it lives.

Base\Wikidoc\Bridge\Symfony\Command

IndexCommand class

class IndexCommand extends Command IndexCommand.php:21

Fills the manuals' search index: one Typesense collection per manual and version (title, sections, text), rewritten from the files. Without the engine - not installed, not switched on, or stopped - there is nothing to fill: the search reads the pages' own index, and this says so.

IndexCommand::__construct()

public function __construct(protected readonly ManualRegistry $manuals, protected readonly ManualIndex $index, protected readonly ?TypesenseIndex $typesense = null)

SyncCommand class

class SyncCommand extends Command SyncCommand.php:22

The versions of the manuals: for each repository, the documentation of the branches that are not checked out is copied under the export folder (wikidoc.export_dir). The branch checked out needs no copy: it is read where it is.

SyncCommand::__construct()

public function __construct(protected readonly ManualRegistry $manuals, protected readonly BranchExporter $exporter)

Base\Wikidoc\Bridge\Symfony\Controller\Backend

ManualController class

class ManualController extends AbstractController ManualController.php:23

The manual, rendered from markdown on disk.

There is no database behind this any more: the pages are files in the documentation roots (base-bundle's own manual, then the application's, which overrides it), so they are versioned with the code they describe and reviewed in the same diff.

ManualController::__construct()

public function __construct(protected readonly AdminContext $adminContext, protected readonly MenuBuilder $menuBuilder, protected readonly DocumentationRegistry $registry, protected readonly MarkdownRenderer $renderer, protected readonly SearchIndexBuilder $searchIndex)
public function search(): JsonResponse

Declared BEFORE index() below: "/manuel/{path}" has a .+ requirement so it can match nested paths, which means it would happily swallow "_search" as a page path if it were registered first.

ManualController::index()

public function index(?string $path): Response

One path for every locale, deliberately: "docs" reads the same in French and English, and a documentation URL is the kind of thing that gets pasted into a ticket or a commit message, where a locale-specific variant only creates two spellings of the same page.

Base\Wikidoc\Bridge\Symfony\DependencyInjection

WikidocConfiguration class

class WikidocConfiguration extends AbstractBaseConfiguration WikidocConfiguration.php:9

WikidocConfiguration::getConfigTreeBuilder()

public function getConfigTreeBuilder(): TreeBuilder

WikidocExtension class

class WikidocExtension extends AbstractBaseExtension WikidocExtension.php:24

WikidocExtension::getConfiguration()

public function getConfiguration(array $config, ContainerBuilder $container): WikidocConfiguration

WikidocExtension::load()

public function load(array $configs, ContainerBuilder $container): void

BundleTypesenseClient class

class BundleTypesenseClient implements TypesenseClientInterface BundleTypesenseClient.php:16

Typesense through glitchr/typesense-bundle: its manager's connection (the default one, or the one wikidoc.search.typesense.connection names) gives the client; the manuals' collections are created here, not from an entity mapping - a page is a file, not a row. The bundle's glue (a Symfony bundle's service): any other client implements TypesenseClientInterface.

BundleTypesenseClient::__construct()

public function __construct(protected readonly TypesenseManager $manager, protected readonly ?string $connection = null)

BundleTypesenseClient::isAvailable()

public function isAvailable(): bool

BundleTypesenseClient::collections()

public function collections(): array

BundleTypesenseClient::createCollection()

public function createCollection(array $schema): void

BundleTypesenseClient::dropCollection()

public function dropCollection(string $name): void

BundleTypesenseClient::import()

public function import(string $collection, array $documents): int

BundleTypesenseClient::multiSearch()

public function multiSearch(array $searches): array

Base\Wikidoc\Bridge\Symfony\Site\Controller

ManualController class

class ManualController extends AbstractController ManualController.php:43

The manuals, on the public site, the way symfony.com/doc publishes its own: a manual per package, a version per branch, each page with the manual's contents, a breadcrumb, previous and next, its own headings, and a link to the file it is edited in.

Addresses, under wikidoc.public.path ("docs"):

/docs                                         every manual
/docs/glitchr/omnitrade                       a manual, at its default version
/docs/glitchr/omnitrade/1.x                   a version's home (the README, or docs/index.md)
/docs/glitchr/omnitrade/1.x/webhooks          a page
/docs/glitchr/omnitrade/current/webhooks      the same, whatever the default version is
/docs/_search?q=webhook[&manual=...&version=...][&format=json]

This controller lives outside src/Controller on purpose: the sites that had the bundle before the public manuals import that whole folder for the back office's manual, and must not gain these routes. A site that wants them sets wikidoc.public.enabled and imports @WikidocBundle/src/Site/Controller (Base\Wikidoc\Site\Controller\ManualController, which inherits these routes) or this folder, @WikidocBundle/src/Bridge/Symfony/Site/Controller.

ManualController::__construct()

public function __construct(protected readonly ManualRegistry $manuals, protected readonly PageRenderer $renderer, protected readonly ManualMarkdownRenderer $markdown, protected readonly ManualSearch $search, protected readonly ?TranslatorInterface $translator = null)

ManualController::index()

public function index(): Response
public function search(Request $request): Response

Declared before the page route, whose path takes anything.

ManualController::asset()

public function asset(string $path): Response

A picture a page shows, served from the manual's repository: "glitchr/omnitrade/1.x/docs/img/flow.png".

ManualController::page()

public function page(string $path): Response

Base\Wikidoc\Controller\Backend

ManualController class

class ManualController extends ManualController ManualController.php:12

The back office's manual where the sites import it: they read the routes of the folder @WikidocBundle/src/Controller (config/routes.yaml), and get them from this class, which inherits them. The controller itself is Base\Wikidoc\Bridge\Symfony\Controller\Backend\ManualController since step C8 of the plan "omnibase agnostique"; both are services (config/services.php).

Base\Wikidoc\Documentation

DocPage class

final class DocPage DocPage.php:14

One markdown file, resolved to the page the admin actually renders.

A page knows which ROOT it came from and, when it shadows a page of the same path in a lower-priority root, which file it superseded. That is the whole point of the layered model: base-bundle ships a manual, the application overrides the parts that differ, and a reader can still tell that an override is in play.

public readonly string $path
public readonly string $title
public readonly ?string $file
public readonly string $root
public readonly int $order
public array $children
public readonly ?string $supersedes

DocPage::__construct()

public function __construct(public readonly string $path, public readonly string $title, public readonly ?string $file, public readonly string $root, public readonly int $order, public array $children = [], public readonly ?string $supersedes = null)

DocPage::isOverride()

public function isOverride(): bool

True when this page replaces one that a lower-priority root also provides.

DocPage::isSection()

public function isSection(): bool

A section with no markdown file of its own - a directory that groups pages but has no index.md. It is a heading, not a link.

DocPage::contains()

public function contains(?string $path): bool

Whether this page, or anything nested under it, is the current one - what the sidebar uses to decide which branches to keep expanded.

DocumentationRegistry class

class DocumentationRegistry DocumentationRegistry.php:26

Builds the documentation tree from markdown on disk, across several roots.

Roots are declared lowest-priority first (base-bundle's own manual, then the application's), and a page's PATH is what identifies it: when two roots provide "install/requirements.md", the higher-priority one wins and records what it superseded. Same override model Symfony already uses for bundle templates, so "put a file at the same path to replace it" is a rule the reader already knows.

Hierarchy is the directory tree - no separate section concept, no ordering table to maintain. A directory becomes a section; its index.md, if there is one, becomes the section's own page rather than a child of it.

A root may also name a home file kept outside its folder - a package's README beside its docs/ (see Base\Wikidoc\Manual\ManualRegistry): it is the home page when the folder has no index.md, and the page "readme" when it has one.

DocumentationRegistry::__construct()

public function __construct(array $roots = [])
$roots

keyed by root name, LOWEST priority first

DocumentationRegistry::getRoots()

public function getRoots(): array

DocumentationRegistry::getTree()

public function getTree(): array
Returns

top-level pages and sections

DocumentationRegistry::get()

public function get(string $path): ?DocPage

DocumentationRegistry::getDefault()

public function getDefault(): ?DocPage

The first page a reader lands on when no path is given: an explicit top-level index.md if one exists, else the first real page in tree order. Deliberately never a section - landing on a heading with no content reads as a broken link.

DocumentationRegistry::getAllPages()

public function getAllPages(): array
Returns

every page with a file, flattened

DocumentationRegistry::getSequence()

public function getSequence(): array

Every page in reading order - the home page, then the tree top to bottom, sections opened where they stand: what "previous" and "next" walk through.

DocumentationRegistry::getNeighbours()

public function getNeighbours(DocPage $page): array

The page before and the page after $page in reading order.

DocumentationRegistry::getAncestors()

public function getAncestors(DocPage $page): array

The sections and pages above $page, outermost first - its breadcrumb.

DocumentationRegistry::findByFile()

public function findByFile(string $file): ?DocPage

The page a Markdown file is rendered as, or null when the file is none of the manual's.

DocumentationRegistry::getFingerprint()

public function getFingerprint(): string

Cheap fingerprint of every source file, so a caller can cache derived work (the search index) and drop it the moment any file changes.

MarkdownRenderer class

class MarkdownRenderer MarkdownRenderer.php:23

Markdown -> HTML for the manual, plus the plain-text extraction the search index is built from.

HTML input is NOT allowed through. These files are technical documentation kept in the repository, so raw HTML would only be there to do something markdown cannot - and the one thing it reliably does is turn a docs file into a script injection on an authenticated admin page.

MarkdownRenderer::render()

public function render(string $markdown): string

MarkdownRenderer::renderFile()

public function renderFile(string $file): string

MarkdownRenderer::extractHeadings()

public function extractHeadings(string $markdown): array

The headings of a document, for the search index and the "on this page" rail. Read from the markdown source rather than the rendered HTML - cheaper, and it cannot pick up headings that only exist inside a fenced code block, which a naive HTML scan would.

MarkdownRenderer::extractText()

public function extractText(string $markdown): string

Readable plain text for the search index: markdown syntax removed, so a query matches what the reader actually sees rather than the punctuation around it.

MarkdownRenderer::slugify()

public function slugify(string $text): string

Must match HeadingPermalink's own slugger, or every search result would link to an anchor that does not exist on the page.

SearchIndexBuilder class

class SearchIndexBuilder SearchIndexBuilder.php:19

The payload behind the ⌘K palette: one flat list of searchable records, built once per set of source files and matched entirely in the browser.

Flat rather than nested, and one record per HEADING rather than per page, so a hit can jump straight to the section it matched instead of dropping the reader at the top of a long document.

Kept deliberately small - this is downloaded whole. Page bodies are truncated, because a search index exists to locate a page, not to reproduce it.

SearchIndexBuilder::__construct()

public function __construct(protected readonly DocumentationRegistry $registry, protected readonly MarkdownRenderer $renderer, protected readonly ?CacheItemPoolInterface $cache = null)

SearchIndexBuilder::build()

public function build(): array

Base\Wikidoc\Manual

BranchExporter class

class BranchExporter BranchExporter.php:17

Copies the documentation of a branch that is not checked out - its docs/ folder and its README - out of the repository, under the export folder, where ManualRegistry finds it as a version of the manual.

The repository is only read (git ls-tree, git show): nothing is checked out, fetched or written in it, so a read-only mount is enough. Needs the git binary; without it the manual keeps the one version of its working tree.

BranchExporter::__construct()

public function __construct(protected readonly ManualRegistry $manuals)

BranchExporter::isAvailable()

public static function isAvailable(): bool

BranchExporter::branches()

public function branches(Manual $manual, string $remote = 'origin'): array

The branches of the manual's repository that are versions and are not the one checked out: name => ref.

BranchExporter::export()

public function export(Manual $manual, string $version, string $ref): int

Writes the branch's documentation under the export folder.

Returns

the number of files copied; 0 when the branch has no documentation (nothing is then left on disk)

BranchExporter::prune()

public function prune(Manual $manual, array $keep): int

Removes the exports of branches that are gone (or became the checked-out one).

Git class

final class Git Git.php:12

What a manual needs to know of a repository, read in its .git folder: the branch checked out, the branches there are, where it is published.

Files only - no git binary, so it works on a read-only mount and in an image that ships without git. Copying another branch's files is BranchExporter's job, and that one does need git.

Git::dir()

public static function dir(string $path): ?string

The .git folder of a checkout (a worktree's .git is a file naming it), or null.

Git::branch()

public static function branch(string $path): ?string

The branch checked out; null outside a repository or on a detached HEAD.

Git::branches()

public static function branches(string $path, string $remote = 'origin'): array

Every branch, local ones first then the remote's: name => ref ("1.x" => "refs/heads/1.x", "2.x" => "refs/remotes/origin/2.x").

Git::origin()

public static function origin(string $path, string $remote = 'origin'): ?string

Where the repository is published, as a web address: "https://github.com/glitchr-omnisense/omnitrade".

Git::webUrl()

public static function webUrl(string $url): ?string

"git@github.com:org/repo.git", "ssh://git@host/org/repo.git", "https://host/org/repo.git" → "https://host/org/repo".

Manual class

final class Manual Manual.php:13

One manual: the documentation of one package, read in its repository (its docs/ folder and its README), in one or several versions.

Its name is the package's own - "glitchr/omnitrade", "omnitrade/stripe", "@glitchr/stickyjs" - and its key is that name as an address spells it: lower case, without the "@" an npm scope starts with.

public readonly string $name
public readonly string $label
public readonly string $path
public readonly array $versions

keyed by name, newest first

public readonly ?string $defaultVersion
public readonly ?string $repository
public readonly ?string $description
public readonly string $docs

Manual::__construct()

public function __construct(public readonly string $name, public readonly string $label, public readonly string $path, public readonly array $versions, public readonly ?string $defaultVersion = null, public readonly ?string $repository = null, public readonly ?string $description = null, public readonly string $docs = 'docs')
$versions

keyed by name, newest first

Manual::keyOf()

public static function keyOf(string $name): string

Manual::getKey()

public function getKey(): string

The manual's part of an address: "glitchr/omnitrade".

Manual::getVersion()

public function getVersion(?string $name = null): ?ManualVersion

Manual::getDefaultVersion()

public function getDefaultVersion(): ?ManualVersion

The version a reader lands on: the configured one, else the checkout's branch, else the newest.

Manual::urlPath()

public function urlPath(?string $version = null, string $page = ''): string

"glitchr/omnitrade/1.x/webhooks": what follows the manuals' prefix in an address.

ManualLocation class

final class ManualLocation ManualLocation.php:9

What an address under the manuals' prefix names: a manual, one of its versions, a page path inside it ("" is the manual's home).

public readonly Manual $manual
public readonly ManualVersion $version
public readonly string $path
public readonly bool $explicit

False when the address names no version ("glitchr/omnitrade/webhooks") or says "current".

ManualLocation::__construct()

public function __construct(public readonly Manual $manual, public readonly ManualVersion $version, public readonly string $path, public readonly bool $explicit)

ManualMarkdownRenderer class

class ManualMarkdownRenderer extends MarkdownRenderer ManualMarkdownRenderer.php:30

The public manuals' Markdown: what the back office's renderer reads, plus

  • callouts, written as GitHub writes them - a quote whose first line is [!NOTE], [!TIP], [!IMPORTANT], [!WARNING] or [!CAUTION] - so a page reads the same on github.com and here;
  • code blocks in a frame that names their language, coloured on the server when tempest/highlight is installed (no script, no flash), plain otherwise.

A subclass, so that the back office's manual does not change by a byte.

const CALLOUTS = ['note' => 'Note', 'tip' => 'Tip', 'important' => 'Important', 'warning' => 'Warning', 'caution' => 'Caution']

ManualMarkdownRenderer::setLabels()

public function setLabels(array $labels): static

The callouts' titles, in the reader's language ("Note", "Astuce"...).

ManualMarkdownRenderer::canHighlight()

public static function canHighlight(): bool

ManualRegistry class

class ManualRegistry ManualRegistry.php:18

The manuals a site publishes: the ones its configuration names (wikidoc.manuals) and the ones found on disk (wikidoc.discover: every folder a pattern matches that holds a composer.json or a package.json).

A manual's versions are the branches of its repository: the one checked out, read where it is, and those wikidoc:sync copied under the export folder. Nothing is kept in a database - the registry is rebuilt from the files, and the list of discovered folders alone is cached for a while.

ManualRegistry::__construct()

public function __construct(protected readonly array $configured = [], protected readonly array $discover = [], protected readonly array $options = [], protected readonly ?CacheItemPoolInterface $cache = null)
$manuals

wikidoc.manuals, keyed by name

$discover

glob patterns

ManualRegistry::all()

public function all(): array
Returns

keyed by key ("glitchr/omnitrade"), sorted

ManualRegistry::get()

public function get(string $name): ?Manual

ManualRegistry::has()

public function has(string $name): bool

ManualRegistry::resolve()

public function resolve(string $path): ?ManualLocation

What an address names. "glitchr/omnitrade/1.x/webhooks" is the page "webhooks" of version 1.x; without a version, or with "current", the manual's default version. The longest manual key wins, so "omnibase/docs" is never read as a page of a manual called "omnibase".

ManualRegistry::pages()

public function pages(Manual $manual, ?ManualVersion $version = null): DocumentationRegistry

The pages of one version of a manual: its docs folder, its README as the home page.

ManualRegistry::exportDir()

public function exportDir(Manual|string $manual, string $version = ''): string

Where wikidoc:sync writes the branch $version of a manual.

ManualRegistry::isVersionBranch()

public function isVersionBranch(string $branch): bool

Whether a branch is a version ("1.x", "2.0", "main"): wikidoc.branches.

ManualRegistry::reset()

public function reset(): void

Forget what was read: after wikidoc:sync, or in a long-running process.

ManualRegistry::compareVersions()

public static function compareVersions(string $a, string $b): int

Newest first: "main" and "master" ahead, then 3.x, 2.x, 1.x.

ManualVersion class

final class ManualVersion ManualVersion.php:12

One version of a manual: a branch of the package's repository.

live is the branch checked out in the working tree - its files are read where they are, an edit shows at the next request. The other branches are copies written by wikidoc:sync (git archive) under the export folder.

public readonly string $name
public readonly string $branch
public readonly string $root

The folder holding this version's docs/ and README: the repository, or its export.

public readonly string $docs
public readonly ?string $readme
public readonly bool $live

ManualVersion::__construct()

public function __construct(public readonly string $name, public readonly string $branch, public readonly string $root, public readonly string $docs = 'docs', public readonly ?string $readme = 'README.md', public readonly bool $live = true)

ManualVersion::getDocsDir()

public function getDocsDir(): string

ManualVersion::getReadmeFile()

public function getReadmeFile(): ?string

ManualVersion::hasPages()

public function hasPages(): bool

Whether there is anything to read: a README, or at least one Markdown file under the docs folder.

ManualVersion::relative()

public function relative(string $file): ?string

A file's path inside the repository ("docs/webhooks.md"), or null when it is not this version's.

PageRenderer class

class PageRenderer PageRenderer.php:22

Renders one page of a manual for the public site: the Markdown to HTML, its relative links rewritten, its headings listed, its "edit this page" address worked out.

A link in a repository's Markdown is written for the repository - [webhooks](webhooks.md), [the bridge](../src/Bridge/Symfony), ![](img/flow.png). Here: - a link to another Markdown file of the manual goes to that page; - a picture of the manual is served by the site (the asset address); - anything else in the repository goes to the file on the forge ({repository}/blob/{branch}/{file}), when the repository is known; - what is absolute, or an anchor, is left alone.

const PICTURES = ['png', 'jpg', 'jpeg', 'gif', 'svg', 'webp', 'avif']

PageRenderer::__construct()

public function __construct(protected readonly ManualRegistry $manuals, protected readonly ManualMarkdownRenderer $markdown, protected readonly ?string $editPattern = null)

PageRenderer::render()

public function render(Manual $manual, ManualVersion $version, DocPage $page, callable $pageUrl, callable $assetUrl): RenderedPage
$pageUrl

the address of a page of this manual and version

$assetUrl

the address of a picture, by its path in the repository

PageRenderer::forgeUrl()

public function forgeUrl(Manual $manual, ManualVersion $version, string $file, string $action = 'blob'): ?string

Where a file of the repository is read (blob) or edited (edit) on its forge.

PageRenderer::headings()

public static function headings(string $html): array
Returns

the rendered page's headings, with the ids they really carry

PageRenderer::normalize()

public static function normalize(string $path): ?string

"docs/../src/./Foo.php" → "src/Foo.php"; null when it leaves the repository.

RenderedPage class

final class RenderedPage RenderedPage.php:9

A manual's page, ready for the template: its HTML, the headings its "on this page" list is made of, the address its source is edited at.

public readonly string $html
public readonly array $headings
public readonly bool $titled

Whether the Markdown has a first-level title of its own (else the template prints the page's).

public readonly ?string $editUrl
public readonly ?string $sourceUrl
public readonly ?string $source

The file's path inside the repository: "docs/webhooks.md".

RenderedPage::__construct()

public function __construct(public readonly string $html, public readonly array $headings, public readonly bool $titled, public readonly ?string $editUrl, public readonly ?string $sourceUrl, public readonly ?string $source)

LocalSearch class

class LocalSearch LocalSearch.php:15

The search without an engine: the manuals' records matched in PHP, the way the back office's palette matches them in the browser. Every word of the query must be found; a title outranks a heading, a heading the text, and the start of a word the middle of one. No typo tolerance: that is what Typesense adds.

LocalSearch::__construct()

public function __construct(protected readonly ManualIndex $index)
public function search(string $query, array $targets, int $limit = 20): SearchResult

LocalSearch::terms()

public static function terms(string $query): array
Returns

the query's words, lower case, accents kept

LocalSearch::snippet()

public static function snippet(string $text, array $terms, int $length = 180): string

ManualIndex class

class ManualIndex ManualIndex.php:19

The searchable records of a manual's version: one per page and one per heading - title, section, text. They are SearchIndexBuilder's, the index the back office's palette has always used; here it is built per manual and version, sent to Typesense by wikidoc:index, and searched in PHP when the engine does not answer.

ManualIndex::__construct()

public function __construct(protected readonly ManualRegistry $manuals, protected readonly MarkdownRenderer $renderer, protected readonly ?CacheItemPoolInterface $cache = null, protected readonly int $ttl = 0)

ManualIndex::records()

public function records(Manual $manual, ManualVersion $version): array

ManualIndex::forget()

public function forget(Manual $manual, ManualVersion $version): void

Forget a version's records (after wikidoc:sync or wikidoc:index).

ManualSearch class

class ManualSearch ManualSearch.php:18

The manuals' search: Typesense when it is switched on and answers (wikidoc.search.typesense), the pages' own index otherwise. The result says which of the two answered.

A search covers one manual (in one version, or its default one) or every manual, each in its default version.

ManualSearch::__construct()

public function __construct(protected readonly ManualRegistry $manuals, protected readonly LocalSearch $local, protected readonly ?TypesenseIndex $typesense = null, protected readonly ?LoggerInterface $logger = null)
public function search(string $query, ?string $manual = null, ?string $version = null, int $limit = 20): SearchResult

ManualSearch::engine()

public function engine(): string

Which engine would answer now - for a status line, not for a decision.

ManualSearch::targets()

public function targets(?string $manual = null, ?string $version = null): array

SearchHit class

final class SearchHit implements JsonSerializable SearchHit.php:8

One result: a page of a manual, or a section of it.

public readonly string $manual
public readonly string $version
public readonly string $path
public readonly string $title
public readonly ?string $section
public readonly ?string $anchor
public readonly string $snippet
public readonly float $score
public ?string $url
public ?string $label

SearchHit::__construct()

public function __construct(public readonly string $manual, public readonly string $version, public readonly string $path, public readonly string $title, public readonly ?string $section = null, public readonly ?string $anchor = null, public readonly string $snippet = '', public readonly float $score = 0.0, public ?string $url = null, public ?string $label = null)

SearchHit::jsonSerialize()

public function jsonSerialize(): array

SearchResult class

final class SearchResult implements JsonSerializable SearchResult.php:9

What a search returned, and who answered: "typesense" (the engine) or "local" (the pages' own index, when the engine is off or does not answer).

const TYPESENSE = 'typesense'
const LOCAL = 'local'
public readonly string $query
public readonly array $hits
public readonly int $found
public readonly string $engine

SearchResult::__construct()

public function __construct(public readonly string $query, public readonly array $hits, public readonly int $found, public readonly string $engine)

SearchResult::jsonSerialize()

public function jsonSerialize(): array

TypesenseClientInterface interface

interface TypesenseClientInterface TypesenseClientInterface.php:10

The few calls the manuals make to Typesense. BundleTypesenseClient makes them through glitchr/typesense-bundle's connection; a test hands in its own. Every method may throw: the caller falls back on the local index.

TypesenseClientInterface::isAvailable()

public function isAvailable(): bool

Whether the server answers at all. Never throws.

TypesenseClientInterface::collections()

public function collections(): array
Returns

the names of the collections there are

TypesenseClientInterface::createCollection()

public function createCollection(array $schema): void

TypesenseClientInterface::dropCollection()

public function dropCollection(string $name): void

TypesenseClientInterface::import()

public function import(string $collection, array $documents): int
Returns

how many were taken

TypesenseClientInterface::multiSearch()

public function multiSearch(array $searches): array

One search per entry, in one call (multi_search).

$searches

each with its collection

Returns

one result per search, in order; a failed one carries error

TypesenseIndex class

class TypesenseIndex TypesenseIndex.php:13

The manuals in Typesense: one collection per manual and version - <prefix>_<manual>_<version>, fields title, section, text - filled by wikidoc:index from the same records the local search reads.

TypesenseIndex::__construct()

public function __construct(protected readonly TypesenseClientInterface $client, protected readonly ManualIndex $index, protected readonly string $prefix = 'wikidoc')

TypesenseIndex::isAvailable()

public function isAvailable(): bool

TypesenseIndex::collection()

public function collection(Manual $manual, ManualVersion $version): string

"wikidoc_glitchr-omnitrade_1-x"

TypesenseIndex::index()

public function index(Manual $manual, ManualVersion $version): int

Drops the version's collection and writes it again.

Returns

the number of records indexed

TypesenseIndex::prune()

public function prune(array $keep): int

Removes the collections of manuals and versions that are no longer published.

public function search(string $query, array $targets, int $limit = 20): ?SearchResult
Returns

null when a collection is missing or a search failed: the caller answers locally

Base\Wikidoc\Site\Controller

ManualController class

class ManualController extends ManualController ManualController.php:13

The public manuals where the sites import them: they read the routes of the folder @WikidocBundle/src/Site/Controller (config/routes.yaml), and get them from this class, which inherits them. The controller itself is Base\Wikidoc\Bridge\Symfony\Site\Controller\ManualController since step C8 of the plan "omnibase agnostique"; both are services (config/services.php), both removed unless wikidoc.public.enabled.