Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message
symfony-service-inspection
symfony/ai-symfony-mate-extension
claude-code
symfony
debug
profiler
Bridge
dev
ai

Diagnose a Symfony dependency-injection or wiring problem, service not found, wrong implementation injected, an autoconfigured tag not applied, or a factory/decorator not doing what you expect. Use for container/config bugs, not runtime request errors (profiler), log trends (log investigation), or a missing PHP extension breaking the tool itself (php environment check).

Install
php artisan boost:add-skill symfony/ai-symfony-mate-extension

Save this content to: .claude/skills/symfony-service-inspection/SKILL.md

# Service inspection

Reads the compiled DI container through Mate's CLI, from the dumped `*DebugContainer.xml`. Two tools:

- `symfony-services` (opt `query`, `tag`, `limit`): `query` is a case-insensitive partial match on service id OR class; `tag` is an exact tag name. Returns `{services: {id => class}, count, truncated}`. `count` is the number of matches before the cut, `truncated` says whether `limit` (default 100) hid some. Narrow the filter rather than raising `limit`.
- `symfony-service-detail --id=<exact id>`: full detail for one service, `{id, class, public, synthetic, lazy, shared, abstract, autowired, autoconfigured, tags, calls, arguments, factory?}`. The id must be exact.

Both commands accept `--format`: `json` to parse the result, `toon` (when `helgesverre/toon` is installed) for the smallest context footprint. The service map can be large, so filter it rather than dumping it wide.

## Workflow

1. Locate candidates with `symfony-services`, filtered. Never dump the whole container.
   - By class or interface: `vendor/bin/mate tools:call symfony-services --query=MailerInterface`
   - By id fragment: `--query=app.handler`
   - By tag, to see who participates in a hook: `vendor/bin/mate tools:call symfony-services --tag=kernel.event_listener`
2. Take the exact id from that map and read it: `vendor/bin/mate tools:call symfony-service-detail --id=App\\Mailer\\Mailer`.
3. Interpret against the symptom below.

## Reading

- **Wrong implementation injected:** query the interface. Every id mapping to a class is a candidate. The one wired in is usually the alias whose id equals the interface FQCN. Aliases are resolved for you: asking for the alias id returns the target's class, tags, and calls, so `detail` on the interface id shows the concrete class that actually gets injected.
- **Tag not applied (listener/subscriber/extension silent):** `detail` the service and check `tags`. If the expected tag is absent, autoconfiguration did not fire: `autoconfigured: false` says so directly, otherwise the class does not implement the expected interface/attribute. Each tag entry carries its attributes (`event`, `priority`, `method`, ...); a listener bound to the wrong `event` or `priority` is a common cause.
- **Class in a resource glob but not registered as expected:** `detail` the FQCN. The boolean flags are the effective values of the compiled definition, the same `debug:container <id>` reports (`synthetic: true` means the object is set at runtime, e.g. `kernel`, so there is no class wiring to inspect). `abstract: true` with a `container.excluded` tag means the container holds a placeholder instead of a service. When the tag has a `source`, it narrows the cause: `in "config/services.yaml"` names the file whose glob skipped the class, either through its `exclude` pattern or because the class carries `#[Exclude]` / a non-matching `#[When]` (check the class before hunting for a pattern); `because the class is abstract` and `by tag "..."` speak for themselves. To check one class, use `symfony-services --tag=container.excluded --query=<class>`; the unfiltered tag listing is cut at `limit`.
- **Constructor/factory surprise:** `factory` (present only when set) is `Class::method`, telling you the object is built by a factory, not `new`. `calls` lists setter-injection method names invoked after construction. A dependency that is null at runtime is often a missing setter call here.
- **What is actually wired in:** `arguments` is the constructor argument list in declaration order, each `{name, type, value}`.
  - `type: service`: `value` is the referenced service id. Feed it straight back into `--id=` to walk one level deeper.
  - `type: collection`: `value` is a nested list of the same shape. This is where a list of collaborators lives, and it is usually the answer to "which of these is registered": **the middleware on a messenger bus**, the handlers on a chain, the wrapped services of a decorator.
  - `type: scalar`: a literal, or a description like `all services tagged "app.plugin"` for a tagged iterator.
  - `name` comes from the constructor (or factory method) signature. A name like `#3` means the position could not be matched to a parameter: the class is not loadable, or it is built by a factory service that records no class.
  - `value: ***REDACTED***` means the parameter name looked like a secret (`key`, `password`, `token`, `secret`, `auth`, `dsn`, `url`, …) **or** the parameter could not be identified. Service ids and collections are never redacted, so wiring stays readable either way. If you need the literal, read the config that sets it, not this tool.

## Failure paths

- `symfony-service-detail` errors "Service ... not found": the id is not exact. Ids are case-sensitive and a leading dot is stripped (`.inner` is stored as `inner`). Re-run `symfony-services` with a fragment to copy the real id. Note a service can exist yet be private; it still appears here.
- Either tool errors "No compiled container found": the dump only exists after the container is compiled. Warm it (`bin/console cache:warmup`, or just boot the app once) in the environment you are inspecting, then retry. An empty `services` map with `count: 0` is a different answer and means the filter matched nothing. If a warm cache still yields nothing, the parse itself may be failing because the runtime lacks `simplexml`; confirm the environment with php environment check.
- `arguments` is empty for a service that takes none, and for a synthetic or synthesized one there is nothing to read. It is also empty for anything injected by a setter instead: check `calls`.
- Reads the first of dev/test/prod it finds. If you are chasing an env-specific binding, make sure that environment's container has been compiled, otherwise you are reading dev.

Service inspection

Reads the compiled DI container through Mate's CLI, from the dumped *DebugContainer.xml. Two tools:

  • symfony-services (opt query, tag, limit): query is a case-insensitive partial match on service id OR class; tag is an exact tag name. Returns {services: {id => class}, count, truncated}. count is the number of matches before the cut, truncated says whether limit (default 100) hid some. Narrow the filter rather than raising limit.
  • symfony-service-detail --id=<exact id>: full detail for one service, {id, class, public, synthetic, lazy, shared, abstract, autowired, autoconfigured, tags, calls, arguments, factory?}. The id must be exact.

Both commands accept --format: json to parse the result, toon (when helgesverre/toon is installed) for the smallest context footprint. The service map can be large, so filter it rather than dumping it wide.

Workflow

  1. Locate candidates with symfony-services, filtered. Never dump the whole container.
    • By class or interface: vendor/bin/mate tools:call symfony-services --query=MailerInterface
    • By id fragment: --query=app.handler
    • By tag, to see who participates in a hook: vendor/bin/mate tools:call symfony-services --tag=kernel.event_listener
  2. Take the exact id from that map and read it: vendor/bin/mate tools:call symfony-service-detail --id=App\\Mailer\\Mailer.
  3. Interpret against the symptom below.

Reading

  • Wrong implementation injected: query the interface. Every id mapping to a class is a candidate. The one wired in is usually the alias whose id equals the interface FQCN. Aliases are resolved for you: asking for the alias id returns the target's class, tags, and calls, so detail on the interface id shows the concrete class that actually gets injected.
  • Tag not applied (listener/subscriber/extension silent): detail the service and check tags. If the expected tag is absent, autoconfiguration did not fire: autoconfigured: false says so directly, otherwise the class does not implement the expected interface/attribute. Each tag entry carries its attributes (event, priority, method, ...); a listener bound to the wrong event or priority is a common cause.
  • Class in a resource glob but not registered as expected: detail the FQCN. The boolean flags are the effective values of the compiled definition, the same debug:container <id> reports (synthetic: true means the object is set at runtime, e.g. kernel, so there is no class wiring to inspect). abstract: true with a container.excluded tag means the container holds a placeholder instead of a service. When the tag has a source, it narrows the cause: in "config/services.yaml" names the file whose glob skipped the class, either through its exclude pattern or because the class carries #[Exclude] / a non-matching #[When] (check the class before hunting for a pattern); because the class is abstract and by tag "..." speak for themselves. To check one class, use symfony-services --tag=container.excluded --query=<class>; the unfiltered tag listing is cut at limit.
  • Constructor/factory surprise: factory (present only when set) is Class::method, telling you the object is built by a factory, not new. calls lists setter-injection method names invoked after construction. A dependency that is null at runtime is often a missing setter call here.
  • What is actually wired in: arguments is the constructor argument list in declaration order, each {name, type, value}.
    • type: service: value is the referenced service id. Feed it straight back into --id= to walk one level deeper.
    • type: collection: value is a nested list of the same shape. This is where a list of collaborators lives, and it is usually the answer to "which of these is registered": the middleware on a messenger bus, the handlers on a chain, the wrapped services of a decorator.
    • type: scalar: a literal, or a description like all services tagged "app.plugin" for a tagged iterator.
    • name comes from the constructor (or factory method) signature. A name like #3 means the position could not be matched to a parameter: the class is not loadable, or it is built by a factory service that records no class.
    • value: ***REDACTED*** means the parameter name looked like a secret (key, password, token, secret, auth, dsn, url, …) or the parameter could not be identified. Service ids and collections are never redacted, so wiring stays readable either way. If you need the literal, read the config that sets it, not this tool.

Failure paths

  • symfony-service-detail errors "Service ... not found": the id is not exact. Ids are case-sensitive and a leading dot is stripped (.inner is stored as inner). Re-run symfony-services with a fragment to copy the real id. Note a service can exist yet be private; it still appears here.
  • Either tool errors "No compiled container found": the dump only exists after the container is compiled. Warm it (bin/console cache:warmup, or just boot the app once) in the environment you are inspecting, then retry. An empty services map with count: 0 is a different answer and means the filter matched nothing. If a warm cache still yields nothing, the parse itself may be failing because the runtime lacks simplexml; confirm the environment with php environment check.
  • arguments is empty for a service that takes none, and for a synthetic or synthesized one there is nothing to read. It is also empty for anything injected by a setter instead: check calls.
  • Reads the first of dev/test/prod it finds. If you are chasing an env-specific binding, make sure that environment's container has been compiled, otherwise you are reading dev.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
mehedimi/laravel-amp-mail
happones/laravel-vue-i18n-generator
jwerd/inertia-table
siberfx/flysystem-wasabi
hollyit/token-replace
ippanelcom/laravel-sdk
adachsoft/changelog-linter
adachsoft/collection
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel