Lue dans les fichiers PHP du paquet au commit 259f0ef9ecb3 : ses espaces de noms, classes, interfaces et énumérations, leurs constantes, propriétés et méthodes publiques avec leur PHPDoc, et les options que prennent ses fabriques. Rien n’en est exécuté.
WikidocBundleclassThe 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
WikidocBundleclassomnibase/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
IndexCommandclassFills 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
SyncCommandclassThe 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
ManualControllerclassThe manual, rendered from markdown on disk.
3 methods
Base\Wikidoc\Bridge\Symfony\DependencyInjection
WikidocConfigurationclass1 method
WikidocExtensionclass2 methods
Base\Wikidoc\Bridge\Symfony\Search
BundleTypesenseClientclassTypesense 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
ManualControllerclassThe 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
ManualControllerclassThe 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).
DocPageclassOne markdown file, resolved to the page the admin actually renders.
4 methods · 7 properties
DocumentationRegistryclassBuilds the documentation tree from markdown on disk, across several roots.
11 methods
MarkdownRendererclassMarkdown -> HTML for the manual, plus the plain-text extraction the search index is built from.
5 methods
SearchIndexBuilderclassThe 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
BranchExporterclassCopies 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
GitclassWhat 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
ManualclassOne 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
ManualLocationclassWhat 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
ManualMarkdownRendererclassThe public manuals' Markdown: what the back office's renderer reads, plus
2 methods · 1 constant
ManualRegistryclassThe 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
ManualVersionclassOne version of a manual: a branch of the package's repository.
5 methods · 6 properties
PageRendererclassRenders 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
RenderedPageclassA 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
LocalSearchclassThe 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
ManualIndexclassThe 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
ManualSearchclassThe 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
SearchHitclassOne result: a page of a manual, or a section of it.
2 methods · 10 properties
SearchResultclassWhat 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
TypesenseClientInterfaceinterfaceThe 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
TypesenseIndexclassThe 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
ManualControllerclassThe 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(): stringModern 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)ManualController::search()
public function search(): JsonResponseDeclared 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): ResponseOne 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(): TreeBuilderWikidocExtension class
class WikidocExtension extends AbstractBaseExtension WikidocExtension.php:24
WikidocExtension::getConfiguration()
public function getConfiguration(array $config, ContainerBuilder $container): WikidocConfigurationWikidocExtension::load()
public function load(array $configs, ContainerBuilder $container): voidBase\Wikidoc\Bridge\Symfony\Search
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(): boolBundleTypesenseClient::collections()
public function collections(): arrayBundleTypesenseClient::createCollection()
public function createCollection(array $schema): voidBundleTypesenseClient::dropCollection()
public function dropCollection(string $name): voidBundleTypesenseClient::import()
public function import(string $collection, array $documents): intBundleTypesenseClient::multiSearch()
public function multiSearch(array $searches): arrayBase\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(): ResponseManualController::search()
public function search(Request $request): ResponseDeclared before the page route, whose path takes anything.
ManualController::asset()
public function asset(string $path): ResponseA 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): ResponseBase\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 $pathpublic readonly string $titlepublic readonly ?string $filepublic readonly string $rootpublic readonly int $orderpublic array $childrenpublic 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(): boolTrue when this page replaces one that a lower-priority root also provides.
DocPage::isSection()
public function isSection(): boolA 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): boolWhether 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 = [])$rootskeyed by root name, LOWEST priority first
DocumentationRegistry::getRoots()
public function getRoots(): arrayDocumentationRegistry::getTree()
public function getTree(): array- Returns
top-level pages and sections
DocumentationRegistry::get()
public function get(string $path): ?DocPageDocumentationRegistry::getDefault()
public function getDefault(): ?DocPageThe 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(): arrayEvery 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): arrayThe page before and the page after $page in reading order.
DocumentationRegistry::getAncestors()
public function getAncestors(DocPage $page): arrayThe sections and pages above $page, outermost first - its breadcrumb.
DocumentationRegistry::findByFile()
public function findByFile(string $file): ?DocPageThe page a Markdown file is rendered as, or null when the file is none of the manual's.
DocumentationRegistry::getFingerprint()
public function getFingerprint(): stringCheap 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): stringMarkdownRenderer::renderFile()
public function renderFile(string $file): stringMarkdownRenderer::extractHeadings()
public function extractHeadings(string $markdown): arrayThe 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): stringReadable 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): stringMust 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(): arrayBase\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(): boolBranchExporter::branches()
public function branches(Manual $manual, string $remote = 'origin'): arrayThe 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): intWrites 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): intRemoves 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): ?stringThe .git folder of a checkout (a worktree's .git is a file naming it), or null.
Git::branch()
public static function branch(string $path): ?stringThe branch checked out; null outside a repository or on a detached HEAD.
Git::branches()
public static function branches(string $path, string $remote = 'origin'): arrayEvery 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'): ?stringWhere 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 $namepublic readonly string $labelpublic readonly string $pathpublic readonly array $versionskeyed by name, newest first
public readonly ?string $defaultVersionpublic readonly ?string $repositorypublic readonly ?string $descriptionpublic 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')$versionskeyed by name, newest first
Manual::keyOf()
public static function keyOf(string $name): stringManual::getKey()
public function getKey(): stringThe manual's part of an address: "glitchr/omnitrade".
Manual::getVersion()
public function getVersion(?string $name = null): ?ManualVersionManual::getDefaultVersion()
public function getDefaultVersion(): ?ManualVersionThe 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 $manualpublic readonly ManualVersion $versionpublic readonly string $pathpublic readonly bool $explicitFalse 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): staticThe callouts' titles, in the reader's language ("Note", "Astuce"...).
ManualMarkdownRenderer::canHighlight()
public static function canHighlight(): boolManualRegistry 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)$manualswikidoc.manuals, keyed by name$discoverglob patterns
ManualRegistry::all()
public function all(): array- Returns
keyed by key ("glitchr/omnitrade"), sorted
ManualRegistry::get()
public function get(string $name): ?ManualManualRegistry::has()
public function has(string $name): boolManualRegistry::resolve()
public function resolve(string $path): ?ManualLocationWhat 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): DocumentationRegistryThe 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 = ''): stringWhere wikidoc:sync writes the branch $version of a manual.
ManualRegistry::isVersionBranch()
public function isVersionBranch(string $branch): boolWhether a branch is a version ("1.x", "2.0", "main"): wikidoc.branches.
ManualRegistry::reset()
public function reset(): voidForget what was read: after wikidoc:sync, or in a long-running process.
ManualRegistry::compareVersions()
public static function compareVersions(string $a, string $b): intNewest 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 $namepublic readonly string $branchpublic readonly string $rootThe folder holding this version's
docs/and README: the repository, or its export.public readonly string $docspublic readonly ?string $readmepublic 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(): stringManualVersion::getReadmeFile()
public function getReadmeFile(): ?stringManualVersion::hasPages()
public function hasPages(): boolWhether there is anything to read: a README, or at least one Markdown file under the docs folder.
ManualVersion::relative()
public function relative(string $file): ?stringA 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),
. 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$pageUrlthe address of a page of this manual and version
$assetUrlthe address of a picture, by its path in the repository
PageRenderer::forgeUrl()
public function forgeUrl(Manual $manual, ManualVersion $version, string $file, string $action = 'blob'): ?stringWhere 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 $htmlpublic readonly array $headingspublic readonly bool $titledWhether the Markdown has a first-level title of its own (else the template prints the page's).
public readonly ?string $editUrlpublic readonly ?string $sourceUrlpublic readonly ?string $sourceThe 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)Base\Wikidoc\Search
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)LocalSearch::search()
public function search(string $query, array $targets, int $limit = 20): SearchResultLocalSearch::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): stringManualIndex 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): arrayManualIndex::forget()
public function forget(Manual $manual, ManualVersion $version): voidForget 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)ManualSearch::search()
public function search(string $query, ?string $manual = null, ?string $version = null, int $limit = 20): SearchResultManualSearch::engine()
public function engine(): stringWhich engine would answer now - for a status line, not for a decision.
ManualSearch::targets()
public function targets(?string $manual = null, ?string $version = null): arraySearchHit class
final class SearchHit implements JsonSerializable SearchHit.php:8
One result: a page of a manual, or a section of it.
public readonly string $manualpublic readonly string $versionpublic readonly string $pathpublic readonly string $titlepublic readonly ?string $sectionpublic readonly ?string $anchorpublic readonly string $snippetpublic readonly float $scorepublic ?string $urlpublic ?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(): arraySearchResult 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 $querypublic readonly array $hitspublic readonly int $foundpublic 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(): arrayTypesenseClientInterface 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(): boolWhether 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): voidTypesenseClientInterface::dropCollection()
public function dropCollection(string $name): voidTypesenseClientInterface::import()
public function import(string $collection, array $documents): int- Returns
how many were taken
TypesenseClientInterface::multiSearch()
public function multiSearch(array $searches): arrayOne search per entry, in one call (multi_search).
$searcheseach 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(): boolTypesenseIndex::collection()
public function collection(Manual $manual, ManualVersion $version): string"wikidoc_glitchr-omnitrade_1-x"
TypesenseIndex::index()
public function index(Manual $manual, ManualVersion $version): intDrops the version's collection and writes it again.
- Returns
the number of records indexed
TypesenseIndex::prune()
public function prune(array $keep): intRemoves the collections of manuals and versions that are no longer published.
TypesenseIndex::search()
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.