# mmo.doc — overview for AI code generation
`mmo.doc` (PHP package `mmo.doc`, PHP namespace `MMO\doc`) is **the framework's documentation generator**, not a
hand-written guide itself. It reads a package's own PHP (via reflection) and JavaScript/`.jhs` (via a real
parser) and produces browsable, searchable documentation out of that source: one `.md` page and one matching
`.doc.json` page model per documented file, plus an `index.json` search/call-graph index per language folder.
It depends on `mmo.core` and soft-depends on `mmo.client` (present → richer html rendering and a menu entry;
absent → the generator and its json endpoints still work, just with no page renderer wired in).
**This file you're reading is dogfooding evidence of exactly that split**: it is the one hand-authored guide in
this package's `docs/` folder. Everything else that can appear under `docs/php/` and `docs/js/` — including,
recursively, this very package's own reference (`docs/php.md`, `docs/php/**/*.md`) — is *generated output*,
gitignored (`docs/php*`, `docs/js*`, `*.doc.json` in `.gitignore`) and only present once someone has actually run
the generator against this checkout. If those folders exist when you're reading this, they document `mmo.doc`
itself; if they don't, that's expected — regenerate them (see below) rather than assuming something is broken.
## The five things that matter most
1. **It is not a CLI tool.** There is no `mmo-doc generate` command. It is a package that wires itself into a
running MMOFramework app: call `\MMO\doc\Documentor::Shared();` once, right after `\MMO\app([...])` — the
exact same eager-singleton idiom as `\MMO\client\Theme::Shared();`. That call (`Documentor::__wakeup()`)
registers three of this package's four route controllers and, only if `mmo.client` is already loaded, the
fourth (`DocPageController`, see point 4) along with the documentation menu/page rendering. A deferred
`'jobs'` config entry is deliberately *not* how this is wired: routing starts before jobs run, so that
pattern would be too late.
2. **Two independent generators, same output shape.** `Documentor` (`src/Documentor.php`) generates PHP docs
from **live reflection** of whatever is currently loaded — `Documentor::Shared()->generate(-1, 'MMO\core')`.
`JSDocumentor` (`src/JSDocumentor.php`) generates JavaScript/`.jhs` docs by **parsing source files on disk**
(there's no JS reflection) — `JSDocumentor::Shared()->generate('mmo.client')`. Both write into the target
package's own manifest-declared `documentations` folder (`docs/php/...` and `docs/js/...` by convention),
never into `mmo.doc`'s own folder unless `mmo.doc` itself is the library being documented.
3. **Everything generated is a pair, plus a shared index.** Every page is written twice: `Foo.md` (readable in
the repo, in an IDE, by another tool) and `Foo.doc.json` (the same `DocPage` model, verbatim — what
`DocPageController` reloads to render real html *without* re-running reflection or a parser). Generating also
updates one `index.json` per doc root, merging in (never dropping) previously indexed symbols — that file is
what `SearchController` and the call-graph ("uses"/"used by") sections read.
4. **The four routes, and what each needs.** `#[APIRoute('tools/mmo/doc')]` (`DocController`) is the control
panel — pick libraries + languages, POST to regenerate; always registered. `#[APIRoute('documentation/**')]`
(`DocPageController`) renders a generated page as real html; only registered (and only useful) when
`mmo.client` is loaded, since it needs `mmo.client`'s page tree and design system — without it, a generated
`.md` file is just a normal file on disk. `GET doc/search?q=...` (`SearchController`) answers json from the
merged `index.json` files. `POST doc/example/run` (`ExampleController`) re-resolves a symbol server-side and
runs one of its `@example` snippets in a sandbox — see point 5. All four are plain json/html endpoints; there
is no MCP tool wrapper here, this package predates and is unrelated to MMOFramework's own MCP server.
5. **`@example` execution needs explicit, deliberate configuration, and is refused otherwise.** The client never
sends code — only a symbol name and an example index; the snippet is always re-read server-side from that
symbol's real doc comment. But running it needs somewhere to boot the framework from, so
`MMO\doc\ExampleRunner->bootstrap` must be set (in your app's config) to a small, side-effect-free file
(placeholders are resolved: `{root}/tools/example-bootstrap.php`) that defines the `MMO_*` constants,
requires `mmo.core`'s `bootstrap.php`, and calls `\MMO\app([...])` — **not** `\MMO\run()` (see
`demo/www/example-bootstrap.php` for a working one). Until
`bootstrap` is set, every `doc/example/run` request throws. Even then, `ExampleController->enabled` defaults
to `false` outside `MODE_DEV`, so example execution is dev-only unless you opt in explicitly. Execution itself
runs inside `bwrap` (fresh mount/network/PID namespaces, read-only package roots, no network, a
`disable_functions` deny-list, a wall-clock timeout and memory limit) — real, layered sandboxing, not a bare
`eval`.
## Generating documentation for a package
Programmatically, from anywhere already inside a booted app:
```php
// php reference of every MMO\core\** symbol currently loaded (flags default to "everything": functions,
// classes, traits, interfaces)
\MMO\doc\Documentor::Shared()->generate(-1, 'MMO\core');
// javascript/.jhs reference for one or more installed packages (all installed packages if none given)
\MMO\doc\JSDocumentor::Shared()->generate('mmo.client');
```
Or through the browser once `Documentor::Shared()` has run: visit `tools/mmo/doc`, pick libraries and
languages (`DocController::LANGUAGES` is just `php` and `js`), submit. That form ultimately calls the same two
methods above.
Where output lands is derived from the target package's own `mmo.json`, not from `mmo.doc`'s: PHP goes to
`/php/...` (`Documentor::getRootNamespaceFolder`), JavaScript to
the sibling `.../js/...` (`JSDocumentor::getOutputFolder`). A page's exact sub-path mirrors either the symbol's
namespace (PHP always; JS in "namespace" mode, the default for `src/js`) or the source file's own location on
disk (JS in "path" mode, used for `src/customElements` and `src/jhs` by default — see
`JSDocumentor::DefaultConfig()`'s `sources` array, and `demo/configs/config.json`'s copy of it under
`MMO\doc\JSDocumentor` for a worked example of overriding it).
## What an app actually configures
Everything here is ordinary MMOFramework module config (a public property on an `aModule`, settable under the
class's own key) — none of it is required just to install the package, only to use the specific feature:
| Class | Property | What it's for |
|---|---|---|
| `MMO\doc\ExampleRunner` | `bootstrap` | the sandbox's boot file (placeholders such as `{root}` are resolved); unset ⇒ `@example` execution refused |
| `MMO\doc\ExampleRunner` | `timeout`, `memoryLimit`, `maxOutputBytes`, `disabledFunctions` | sandbox limits, sensible defaults |
| `MMO\doc\controllers\ExampleController` | `enabled` | gate on `doc/example/run`; `true` only in `MODE_DEV` by default |
| `MMO\doc\Documentor` | `docFolders` | namespace ⇒ folder overrides for namespaces that aren't a normal installed package (ships one default: PSR interfaces) |
| `MMO\doc\JSDocumentor` | `output`, `rootNamespace`, `sources`, `folders` | where JS/`.jhs` sources are found and where their pages land |
`demo/configs/config.json` is a real, working example of the minimal config an app needs (mainly: point
`ExampleRunner->bootstrap` somewhere real).
## Rendering: reading vs. browsing
A generated `.md` file is always plain, valid markdown — readable on its own, in an editor, by another tool, or
by MMOFramework's own MCP documentation search, with no dependency on `mmo.client` or this package's renderer.
`DocPageController` is a *nicer* way to view the same content as real html (rail navigation, resolved
cross-links, syntax highlighting via a vendored Prism build, an interactive `` runner) by
reading the `.doc.json` model instead of re-parsing the `.md` — but it only takes over `documentation/**` when
`mmo.client` is loaded; otherwise `mmo.client`'s own catch-all page controller would have rendered the raw
markdown through `` if it existed there at all, and without `mmo.client` there is no page renderer
in the loop at all — the `.md`/`.json` files just sit on disk, still fully valid as plain documentation.
## Things left genuinely unclear from reading the source
- `demo/configs/config.json` (its `MMO\doc\ExampleRunner` and `MMO\doc\JSDocumentor` entries) is the closest
thing to a canonical "what does a real app wire up" example; there is no committed real-world consuming app to
cross-check it against.
- `Graphviz` (`src/Graphviz.php`) and `Documentor::graphFromReflections()` generate entity/class diagrams (`dot`
binary required) — `docs/dot.pdf`/`docs/dot-summary.pdf` are presumably samples of that output, but nothing in
the source documents how those two PDFs were produced or what they're meant to demonstrate.