marko/codeindexer
Static analysis library that indexes Marko modules — attributes, configs, templates, and translations — into a cached symbol table. codeindexer is the shared foundation that powers Marko’s AI development tooling: marko/mcp (the MCP server) and marko/lsp (the language server) both build on its index rather than re-parsing the project themselves.
It walks every module discovered under vendor/, modules/, and app/, parses their PHP attributes (#[Observer], #[Plugin], #[Preference], #[Command], route attributes) plus config/template/translation files, and writes the result to a single on-disk cache. The cache is lazy-loaded and auto-rebuilt when stale, so reads are cheap.
Installation
Section titled “Installation”composer require marko/codeindexerMost users do not install this directly — it arrives automatically as a dependency of marko/mcp or marko/lsp. Install it standalone only if you are building your own tooling on top of the index.
Querying the index
Section titled “Querying the index”Inject the concrete IndexCache to use the high-level query API. The cache lazy-loads on first read and rebuilds itself if missing or stale:
use Marko\CodeIndexer\Cache\IndexCache;
class MyTool{ public function __construct( private IndexCache $cache, ) {}
public function inspect(): void { // Inverse lookups $observers = $this->cache->findObserversForEvent(UserCreated::class); $plugins = $this->cache->findPluginsForTarget(ProductRepository::class);
// Full listings $modules = $this->cache->getModules(); $commands = $this->cache->getCommands(); $routes = $this->cache->getRoutes(); $configKeys = $this->cache->getConfigKeys(); $templates = $this->cache->getTemplates(); $translations = $this->cache->getTranslationKeys(); }}Rebuilding the index
Section titled “Rebuilding the index”The index rebuilds automatically when stale, but you can force a rebuild from the CLI:
marko indexer:rebuildThis writes the cache to .marko/index.cache and reports the number of modules, observers, plugins, commands, routes, config keys, templates, and translations indexed.
API Reference
Section titled “API Reference”IndexCache
Section titled “IndexCache”The concrete indexer. Holds both the raw key/value cache and the high-level symbol queries.
| Method | Returns | Description |
|---|---|---|
build() | void | Scan all modules and write the cache file |
load() | bool | Load the cache from disk; false if missing/stale |
isStale() | bool | Whether any source file is newer than the cache |
getModules() | list<ModuleInfo> | Every discovered module |
getObservers() | list<ObserverEntry> | All #[Observer] registrations |
getPlugins() | list<PluginEntry> | All #[Plugin] registrations |
getPreferences() | list<PreferenceEntry> | All #[Preference] registrations |
getCommands() | list<CommandEntry> | All #[Command] registrations |
getRoutes() | list<RouteEntry> | All route attributes |
getConfigKeys() | list<ConfigKeyEntry> | All config keys |
getTemplates() | list<TemplateEntry> | All view templates |
getTranslationKeys() | list<TranslationEntry> | All translation keys |
findObserversForEvent(string $eventClass) | list<ObserverEntry> | Observers listening to a given event |
findPluginsForTarget(string $targetClass) | list<PluginEntry> | Plugins targeting a given class |
IndexCacheInterface
Section titled “IndexCacheInterface”The one swappable seam. Defines the raw cache backend (get, set, has, invalidate) so the storage mechanism can be replaced via a #[Preference] — e.g. an in-memory or Redis-backed cache instead of the default file cache. The symbol-query methods above are concrete to IndexCache; type-hint the concrete class when you need them.