Skip to content

Building a Framework Adapter

A framework adapter is a Composer package with no admin screens. This page compares how the five framework adapters solve the same problems, so a new adapter can follow the closest one. Laravel and Symfony ship; CakePHP, CodeIgniter and Yii are unreleased.

AdapterEntry pointAgent built by
LaravelPhpClaw\Laravel\PhpClawServiceProviderEngineFactory::build($app), bound as a singleton
SymfonyPhpClaw\Symfony\PhpClawBundlePhpClawFactory::create(), a container service
CakePHPPhpClaw\CakePHP\PhpClawPluginPhpClawService::make()
CodeIgniterCodeIgniter’s discovery of the package’s Config and routesPhpClawService::make()
YiiPhpClaw\Yii\PhpClawBootstrap, plus the PhpClawComponent application componentPhpClawComponent::getAgent()

Each one fires a booting event carrying a PhpClawExtensions object with tools, guards, hooks, skills, memory and providers arrays, so other packages can add to phpClaw without editing config.

In Laravel, Engine\EngineFactory is a static class, build($app) and resolveTools($app), and Telescope recording is wired by Telescope\PhpClawWatcher::register(). In Symfony, PhpClawFactory is a container service whose create() builds the agent, the two routes in the bundle’s config/routes.yaml point at Http\ApiController, and the Web Profiler panel is Profiler\PhpClawDataCollector.

AdapterWhereDefault memory driver
Laravelconfig/phpclaw.php, reading PHPCLAW_* variablesdatabase
Symfonythe bundle config tree; PHPCLAW_* variables only through the shipped phpclaw.yamldoctrine
CakePHPConfigure key PhpClaw, reading PHPCLAW_* variablesorm
CodeIgniterConfig\PhpClaw properties, overridden by PHPCLAW_* variablesdatabase
Yiicomponent properties, plus seven settings from variables or paramsdatabase

Frameworks differ most here, because not every framework has a user system.

AdapterUser id fromManage-all
LaravelAuth::id()ids in admin_ids, or the phpclaw.manage-all Gate ability
SymfonygetUserIdentifier() from the security tokenthe ROLE_PHPCLAW_MANAGE_ALL role
CakePHPthe identity request attributeids in manage_all_user_ids, default [1]
CodeIgniterthe app’s identityResolver callableids in manageAllUserIds, default [1]
Yiithe application user componentids in manage_all_user_ids, default [1]

In every adapter:

  • console commands skip the tool permission check (in Laravel any Artisan command that is not a queue worker, including schedule:run from cron; in CakePHP, CodeIgniter and Yii only the adapter’s own commands; in Symfony any console command); queue workers do not;
  • a queued job runs as the user who dispatched it, but never with the manage-all tier, so db_query refuses it with FORBIDDEN_IN_QUEUED_RUN (Laravel’s jobs run with the user’s normal checks instead);
  • conversations and job results are stored with the user id and scoped to it. In CakePHP, CodeIgniter and Yii, a caller with no user id sees every stored job.
AdapterCommands
Laravelphpclaw, phpclaw:about, phpclaw:guide, phpclaw:stats, phpclaw:jobs:list, phpclaw:jobs:status, phpclaw:mcp-server
Symfonythe same seven names
CakePHPphpclaw, phpclaw about, phpclaw guide, phpclaw stats, phpclaw:jobs:list, phpclaw:jobs:status
CodeIgniterphpclaw, phpclaw:about, phpclaw:guide, phpclaw:stats, phpclaw:jobs:list, phpclaw:jobs:status, phpclaw:publish
Yiiphpclaw, phpclaw/about, phpclaw/guide, phpclaw/stats, phpclaw/jobs-list, phpclaw/jobs-status, phpclaw/mcp-server

No adapter has a command that reads or deletes stored conversations.

A CMS adapter has two commands because its about, guide and statistics views are admin pages. A framework adapter has no admin pages, so those views are the about, guide and stats commands.

Every framework adapter exposes POST {prefix}/send and POST {prefix}/chat/stream, with the stream sending tool_before, tool_after, chunk, done and error events.

AdapterAuthentication
Laravelyour api.middleware (default auth:sanctum), plus a package middleware that requires a user
Symfonya firewall from symfony/security-bundle; the listener requires a user
CakePHPyour authentication middleware; a user is required only with api.require_identity
CodeIgniterone shared token (PHPCLAW_API_TOKEN); users come from identityResolver
Yiione shared token (PHPCLAW_API_TOKEN); users come from the user component
AdapterTools, opt-in through config
Laraveldb_query, read_log (storage/logs/laravel.log), route_list (routes with method, URI, name and action), config_get (one config value, secrets withheld), cache_inspect (the active store and whether a key exists), queue_status (connection, pending and failed counts)
Symfonydb_query, read_log
CakePHP, CodeIgniter, Yiidb_query, read_log

Core’s seven default tools are added too, except that Yii removes them, with db_query and read_log, from web requests unless allowSystemToolsOverHttp is true.

AdapterLifecycle events forwarded asDebug integration
LaravelLaravel events phpclaw.{event}Telescope
SymfonyPhpClawEvent named phpclaw.{event}Web Profiler
CakePHPEventManager events phpclaw.{event}DebugKit panel
CodeIgniterEvents::trigger('phpclaw.{event}')debug toolbar collector
YiiYii::$app->trigger('phpclaw.{event}')debug module panel

All forward the 40 events of PhpClaw\Hooks\LifecycleEvent. See Hooks.

Every framework adapter uses phpclaw_conversations, phpclaw_messages and phpclaw_memory, created with the framework’s own migrations. In Laravel’s migration:

TableColumns
phpclaw_conversationsid (26-character ULID), namespace, user_id, title, metadata (JSON), created_at, updated_at
phpclaw_messagesid, conversation_id (deleted with its conversation), role, content, tool_name, tool_input, created_at
phpclaw_memoryid, namespace, lookup_key, value, expires_at, created_at, updated_at; unique on namespace and lookup_key

Framework adapters install with Composer only. To publish one, update CHANGELOG.md, bump the version and tag the release.

For each adapter’s full reference, see All Adapters.