Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/nowo-tech/TwigInspectorBundle/llms.txt

Use this file to discover all available pages before exploring further.

Twig Inspector Bundle works by instrumenting every Twig template at compile time, activating through a browser cookie, and surfacing collected data in the Symfony Web Profiler. The sections below explain each stage of the pipeline.

Request lifecycle overview

1

Compile-time instrumentation

When Twig compiles a template, TwigInspectorExtension registers DebugInfoNodeVisitor as a node visitor. The visitor walks the compiled AST and wraps every ModuleNode (template) and BlockNode (block) with NodeStart / NodeEnd nodes that call into HtmlCommentsExtension at render time.
2

Cookie-based activation check

At render time HtmlCommentsExtension::shouldInspect() runs before any output buffering. It checks:
  • kernel.debug is true (production is skipped unconditionally)
  • The current request carries the inspector cookie (default name twig_inspector_is_active) set to true
  • The template extension is in enabled_extensions (default .html.twig)
  • The template and block name are not in any exclusion list
If any check fails, the node is skipped with zero overhead.
3

HTML comment injection

When the inspector is active, HtmlCommentsExtension wraps each block’s rendered output with a pair of HTML comments that include the template name, a link to the open-in-IDE route, a unique ID, and a box-drawing nesting prefix:
<!-- ┏ content [/_template/templates%2Fbase.html.twig?line=1] #a3f2b1-->
<main>...</main>
<!-- ┗ content [/_template/templates%2Fbase.html.twig?line=1] #a3f2b1-->
Nesting is tracked via nestingLevel and BoxDrawings so deeply nested blocks get distinct visual indicators in DevTools.
4

Controller comment injection

ControllerCommentSubscriber listens to kernel.response at priority -512. When the inspector cookie is present it injects a controller comment directly after the <body> tag for the main request, and wraps each render(controller(...)) sub-request response with start/end comments:
<!-- ┏ controller: App\Controller\HomeController::index [main] template: templates/home.html.twig -->
<!-- ┗ /controller -->
5

JavaScript overlay

The toolbar panel loads a JavaScript overlay. The overlay scans the live DOM for the injected HTML comments, maps comment pairs to the surrounding HTML elements, and attaches hover highlights and a tooltip popup showing the template name(s) for each element.
6

Click to open in IDE

Clicking a highlighted element fires a request to the bundle route /_template/{template}?line={n} (route name: nowo_twig_inspector_template_link). OpenTemplateController validates the path, resolves it through the Twig loader, and returns a redirect to the IDE URL scheme configured in framework.ide (e.g. phpstorm://open?file=…&line=…).
7

Web Profiler data collection

TwigInspectorCollector implements DataCollectorInterface and LateDataCollectorInterface. During collect() it parses the HTML comments from the response body to count template and block render occurrences. During lateCollect() it reads render durations from the Twig ProfilerExtension. ControllerRenderSubscriber listens to kernel.controller to record every controller invocation (main + fragments) so the profiler panel can display them with Main / Fragment badges.

Component reference

Twig/

NodeVisitor pipeline. TwigInspectorExtension registers DebugInfoNodeVisitor with Twig. The visitor injects NodeStart / NodeEnd nodes at compile time. At render time HtmlCommentsExtension is called by those nodes to produce or skip HTML comments based on the current request context.

EventSubscriber/

Kernel event listeners. ControllerCommentSubscriber injects controller-boundary comments into responses when the inspector is enabled. ControllerRenderSubscriber records every controller call (main request and render(controller(...)) sub-requests) so the profiler panel can display them.

DataCollector/

Web Profiler panel data. TwigInspectorCollector parses injected HTML comments to count template and block renders, fetches render timings from the Twig profiler, and exposes controller usage — all surfaced in the Symfony Web Profiler under the </> icon.

Controller/

IDE redirect endpoint. OpenTemplateController handles GET /nowo-twig-inspector/open/{template}. It validates the template name against path traversal, resolves the absolute file path through the Twig loader, and redirects to the IDE URL produced by Symfony’s FileLinkFormatter.

Command/

Installation helper. InstallCommand (nowo:twig-inspector:install) creates the config/packages/nowo_twig_inspector.yaml configuration file and registers the bundle routes in config/routes.yaml. Run this when Symfony Flex is not available.

RequestStack/

Sub-request detection. MainOrMasterRequestProvider and RequestStackMainOrMasterAdapter abstract over the Symfony RequestStack API to reliably identify the main (master) request across Symfony 6, 7, and 8 — used by both event subscribers to distinguish main requests from fragment sub-requests.
The inspector is toggled by a browser cookie rather than a config flag. This means you can switch it on and off in the browser without restarting the server or changing any files.
# config/packages/nowo_twig_inspector.yaml
nowo_twig_inspector:
  cookie_name: twig_inspector_is_active  # default
The cookie value must be the string "true" (or PHP truthy). The Symfony Web Profiler toolbar injects a checkbox that sets and removes the cookie for you.
When the cookie is absent, HtmlCommentsExtension::shouldInspect() returns false immediately. The DebugInfoNodeVisitor has already compiled the NodeStart / NodeEnd calls into the template, but those calls are no-ops — Twig’s template cache is unaffected.

Performance when disabled

The bundle is designed to impose no measurable cost when the inspector is off:
  • HtmlCommentsExtension::shouldInspect() first checks $this->debug. If kernel.debug is false (production build), the method returns false before touching the request stack.
  • No output buffering (ob_start) is started unless the check passes.
  • ControllerCommentSubscriber performs the same debug flag check at the top of onKernelResponse().
  • TwigInspectorCollector::collect() short-circuits immediately when the cookie is absent.
Register the bundle only in dev and test environments (see Installation). Even if it is accidentally loaded in prod, all code paths guard on kernel.debug === false.

Build docs developers (and LLMs) love