--- url: 'https://app-dev-panel.github.io/app-dev-panel/api.md' --- # API Overview ADP exposes three API domains over HTTP: **Debug** (stored debug entries), **Inspector** (live application state), and **Ingestion** (external data intake). ## Base URLs | Domain | Base Path | Purpose | |--------|-----------|---------| | Debug | `/debug/api` | Access stored debug entries and SSE stream | | Inspector | `/inspect/api` | Query live application state (routes, config, database, files) | | Ingestion | `/debug/api/ingest` | Accept debug data from external applications | ## Response Format All responses (except SSE and MCP) are wrapped in a standard envelope: ```json { "id": "debug-entry-id", "data": { ... }, "error": null, "success": true, "status": 200 } ``` On error, `success` is `false`, `error` contains the error message, and `data` is `null`. ## Middleware Chain Every API request passes through: 1. **AppDevPanel\Api\Middleware\IpFilterMiddleware** -- validates request IP against allowed IPs (default: `127.0.0.1`, `::1`) 2. **AppDevPanel\Api\Middleware\CorsMiddleware** -- adds permissive CORS headers 3. **AppDevPanel\Api\Debug\Middleware\ResponseDataWrapper** -- wraps responses in the standard envelope 4. **AppDevPanel\Api\Debug\Middleware\DebugHeaders** -- adds `X-Debug-Id` and `X-Debug-Link` response headers Inspector endpoints additionally pass through: 5. **AppDevPanel\Api\Inspector\Middleware\InspectorProxyMiddleware** -- routes `?service=` requests to registered external services ## Authentication By default, the API is restricted to localhost via IP filtering. An optional `auth_token` can be configured for additional security. ## Transports * **REST** -- standard JSON request/response ([reference](./rest)) * **SSE** -- real-time push notifications for new debug entries ([reference](./sse)) * **MCP** -- JSON-RPC 2.0 endpoint for AI assistant integration ([reference](./inspector)) --- --- url: 'https://app-dev-panel.github.io/app-dev-panel/guide/architecture.md' --- # Architecture ADP follows a strict layered architecture where each layer has clear responsibilities and dependencies flow in one direction. ## Layers ### 1. Kernel The core engine. **Framework-independent** — depends only on PSR interfaces and generic PHP libraries. Manages: * **Debugger** — Lifecycle management (start, collect, flush) * **Collectors** — Gather runtime data via AppDevPanel\Kernel\Collector\CollectorInterface * **Storage** — Persist debug data (JSON files by default) via AppDevPanel\Kernel\Storage\StorageInterface * **Proxies** — Intercept PSR interfaces transparently ### 2. API HTTP layer built on PSR-7/15. Provides: * **REST endpoints** — Fetch debug entries, collector data * **SSE** — Real-time notifications for new entries * **Inspector** — Runtime inspection endpoints (config, routes, database schema, etc.) * **MCP** — AI assistant integration via Model Context Protocol * **Ingestion** — Accept debug data from external (non-PHP) applications ### 3. Adapters Framework bridges. Each adapter: * Registers proxy services in the framework's DI container * Maps framework lifecycle events to AppDevPanel\Kernel\Debugger`::startup()` / `::shutdown()` * Configures collectors and storage with framework-appropriate settings * Registers API routes (`/debug/api/*`, `/inspect/api/*`) * Serves the debug panel frontend at `/debug` * Implements framework-specific inspector providers (config, routes, database schema) ### 4. Frontend React 19 SPA with: * Material-UI 5 design system * Redux Toolkit for state management * Module system (Debug, Inspector, LLM, MCP, OpenAPI, Frames) ## Dependency Graph ``` ┌────────────────────────────────────────────────────────┐ │ Dependency Direction │ │ │ │ Adapter ──▶ API ──▶ Kernel │ │ │ ▲ │ │ └───────────────────┘ │ │ │ │ Cli ──▶ API ──▶ Kernel │ │ │ │ Frontend ──▶ API (via HTTP only) │ └────────────────────────────────────────────────────────┘ ``` * **Kernel** depends on nothing (PSR interfaces only) * **API** depends only on Kernel * **Cli** depends on Kernel and API * **Adapter** depends on Kernel, API, and the target framework * **Frontend** communicates via HTTP — no PHP dependencies ## Dependency Rules The core principle: **common modules must never depend on framework-specific code**. | Module | Can depend on | Cannot depend on | |--------|--------------|-----------------| | **Kernel** | PSR interfaces only | API, Cli, Adapter, any framework | | **API** | Kernel, PSR interfaces | Adapter, any framework | | **Cli** | Kernel, API, Symfony Console | Adapter, any framework | | **Adapter** | Kernel, API, Cli, framework packages | Other adapters | | **Frontend** | Nothing (HTTP only) | Any PHP package | ::: warning Adapters must not depend on other adapters. Each adapter is an independent bridge between the Kernel and a specific framework. ::: ## Abstractions Storage and serialization remain behind interfaces to ensure pluggability: | Concern | Abstraction | Implementations | |---------|-------------|-----------------| | Debug data storage | AppDevPanel\Kernel\Storage\StorageInterface | AppDevPanel\Kernel\Storage\FileStorage, AppDevPanel\Kernel\Storage\MemoryStorage | | Object serialization | AppDevPanel\Kernel\Dumper | JSON-based (built-in) | | Database inspection | AppDevPanel\Api\Inspector\Database\SchemaProviderInterface | Per-adapter: AppDevPanel\Adapter\Yii3\Inspector\DbSchemaProvider, AppDevPanel\Adapter\Symfony\Inspector\DoctrineSchemaProvider, AppDevPanel\Adapter\Laravel\Inspector\LaravelSchemaProvider, AppDevPanel\Adapter\Yii2\Inspector\NullSchemaProvider, AppDevPanel\Adapter\Cycle\Inspector\CycleSchemaProvider | ## Data Flow 1. Target app runs with an Adapter installed 2. Adapter registers proxies that intercept PSR interfaces 3. Proxies feed data to Collectors 4. On request completion, Debugger flushes collector data to Storage 5. API serves stored data; SSE notifies the frontend 6. Frontend renders the data See [Data Flow](/guide/data-flow) for the full lifecycle details. ## Frontend Module System The frontend uses a module system where each module implements `ModuleInterface`: ```typescript interface ModuleInterface { routes: RouteObject[]; reducers: Record; middlewares: Middleware[]; standalone: boolean; } ``` Current modules: Debug, Inspector, LLM, MCP, OpenAPI, Frames. ## Creating a New Adapter When creating an adapter for a new framework: 1. Create `libs/Adapter//` 2. The adapter **must** depend on app-dev-panel/kernel 3. The adapter **may** depend on app-dev-panel/api (for route and inspector registration) 4. The adapter **may** depend on app-dev-panel/cli (for CLI commands) 5. The adapter **must not** depend on other adapters 6. The adapter **must not** modify Kernel or API code — only wire into them via configuration ### Adapter Responsibilities | Responsibility | Description | |----------------|-------------| | Lifecycle mapping | Map framework events → AppDevPanel\Kernel\Debugger`::startup()` / `::shutdown()` | | Proxy wiring | Register Kernel PSR proxies as service decorators in the framework's DI | | Framework-specific proxies | Create proxies for non-PSR APIs (e.g., AppDevPanel\Adapter\Symfony\Proxy\SymfonyEventDispatcherProxy) | | Collector configuration | Configure active collectors and pass framework-specific settings | | Storage setup | Wire AppDevPanel\Kernel\Storage\StorageInterface with framework-appropriate paths | | Route registration | Register API routes for `/debug/api/*`, `/inspect/api/*` and serve the frontend at `/debug` | | Inspector providers | Implement AppDevPanel\Api\Inspector\Database\SchemaProviderInterface, AppDevPanel\Api\Inspector\Elasticsearch\ElasticsearchProviderInterface, etc. | ### Reference Implementations | Adapter | Framework | Pattern | |---------|-----------|---------| | Symfony | Symfony 6.4–8.x | Bundle + Extension + CompilerPass | | Yii2 | Yii 2 | Module + BootstrapInterface | | Yii 3 | Yii 3 | Config plugin + ServiceProvider | | Laravel | Laravel 11.x–12.x | ServiceProvider (register + boot) | ### Minimal Checklist 1. `composer.json` with app-dev-panel/kernel + app-dev-panel/api dependencies 2. Lifecycle event mapping → AppDevPanel\Kernel\Debugger`::startup()` / `::shutdown()` 3. Register Kernel PSR proxies as service decorators (logger, events, HTTP client) 4. Wire AppDevPanel\Kernel\Storage\FileStorage with a framework-appropriate path 5. Register API controller routes 6. Create a [playground](/guide/playgrounds) for testing and demo --- --- url: 'https://app-dev-panel.github.io/app-dev-panel/guide/collectors/asset-bundle.md' --- # AssetBundle Collector Captures registered frontend asset bundles — CSS files, JavaScript files, dependencies, and configuration. ## What It Captures | Field | Description | |-------|-------------| | `class` | Asset bundle class name | | `sourcePath` | Source path for published assets | | `basePath` | Published base path | | `baseUrl` | Published base URL | | `css` | CSS file list | | `js` | JavaScript file list | | `depends` | Bundle dependencies | | `options` | Bundle options | ## Data Schema ```json { "bundles": { "AppAsset": { "class": "App\\Assets\\AppAsset", "sourcePath": "/app/assets", "basePath": "/public/assets/abc123", "baseUrl": "/assets/abc123", "css": ["css/app.css"], "js": ["js/app.js"], "depends": ["yii\\web\\JqueryAsset"], "options": {} } }, "bundleCount": 3 } ``` **Summary** (shown in debug entry list): ```json { "assets": { "bundleCount": 3 } } ``` ## Contract ```php use AppDevPanel\Kernel\Collector\AssetBundleCollector; $collector->collectBundle(name: 'AppAsset', bundle: [ 'class' => 'App\\Assets\\AppAsset', 'css' => ['css/app.css'], 'js' => ['js/app.js'], 'depends' => ['yii\\web\\JqueryAsset'], ]); // Or collect all bundles at once $collector->collectBundles(bundles: $allBundles); ``` ::: info \AppDevPanel\Kernel\Collector\AssetBundleCollector implements \AppDevPanel\Kernel\Collector\SummaryCollectorInterface and depends on \AppDevPanel\Kernel\Collector\TimelineCollector. Primarily used with Yii frameworks. ::: ## Debug Panel * **Bundle list** — all registered asset bundles with file counts * **Asset files** — CSS and JS files per bundle * **Dependency tree** — bundle dependency graph --- --- url: >- https://app-dev-panel.github.io/app-dev-panel/guide/collectors/authorization.md --- # Authorization Collector Captures authentication and authorization data — user identity, roles, tokens, access decisions, guards, role hierarchy, and impersonation status. ![Authorization Collector panel](/images/collectors/authorization.png) ## What It Captures | Field | Description | |-------|-------------| | `username` | Authenticated user identifier | | `roles` | Assigned roles | | `effectiveRoles` | Roles after hierarchy resolution | | `authenticated` | Whether the user is authenticated | | `firewallName` | Active firewall/guard name | | `token` | Auth token details (type, attributes, expiration) | | `impersonation` | Impersonation data (original and impersonated user) | | `guards` | Registered authentication guards | | `roleHierarchy` | Role inheritance tree | | `authenticationEvents` | Login/logout/failure events | | `accessDecisions` | Authorization check results (granted/denied) | ## Data Schema ```json { "username": "admin@example.com", "roles": ["ROLE_ADMIN"], "effectiveRoles": ["ROLE_ADMIN", "ROLE_USER"], "firewallName": "main", "authenticated": true, "token": { "type": "Bearer", "attributes": {}, "expiresAt": "2026-03-31T23:59:59+00:00" }, "impersonation": null, "guards": [ {"name": "main", "provider": "users", "config": {}} ], "roleHierarchy": {"ROLE_ADMIN": ["ROLE_USER"]}, "authenticationEvents": [ {"type": "login", "provider": "form", "result": "success", "time": 1711878000.1, "details": {}} ], "accessDecisions": [ {"attribute": "ROLE_ADMIN", "subject": "route:/admin", "result": "granted", "voters": [...], "duration": 0.0001, "context": {}} ] } ``` **Summary** (shown in debug entry list): ```json { "authorization": { "username": "admin@example.com", "authenticated": true, "roles": ["ROLE_ADMIN"], "accessDecisions": {"total": 3, "granted": 3, "denied": 0}, "authEvents": 1 } } ``` ## Contract ```php use AppDevPanel\Kernel\Collector\AuthorizationCollector; $collector->collectUser( username: 'admin@example.com', roles: ['ROLE_ADMIN'], authenticated: true, ); $collector->collectFirewall(firewallName: 'main'); $collector->collectToken(type: 'Bearer', attributes: [], expiresAt: '2026-03-31T23:59:59+00:00'); $collector->collectRoleHierarchy(hierarchy: ['ROLE_ADMIN' => ['ROLE_USER']]); $collector->collectEffectiveRoles(effectiveRoles: ['ROLE_ADMIN', 'ROLE_USER']); $collector->logAccessDecision( attribute: 'ROLE_ADMIN', subject: 'route:/admin', result: 'granted', voters: [...], ); ``` ::: info \AppDevPanel\Kernel\Collector\AuthorizationCollector implements \AppDevPanel\Kernel\Collector\SummaryCollectorInterface. It has no dependencies on other collectors. ::: ## How It Works Framework adapters extract authentication state from the security component: * **Symfony**: Security token, firewall, voter results via event listeners * **Laravel**: Auth guards, Gate authorization checks * **Yii 3**: Identity interface and RBAC system ## Debug Panel * **User identity** — username, authentication status, roles * **Access decisions** — list of authorization checks with granted/denied results * **Role hierarchy** — visual role inheritance tree * **Auth events** — login, logout, and failure events * **Token details** — token type, attributes, and expiration --- --- url: 'https://app-dev-panel.github.io/app-dev-panel/guide/inspector/authorization.md' --- # Authorization Inspector Inspect the security and authorization configuration of your application. ![Authorization Inspector](/images/inspector/authorization.png) ## What It Shows | Section | Description | |---------|-------------| | Guards | Security guards/firewalls configuration | | Role hierarchy | Role inheritance tree | | Voters | Authorization voters/policies registered | | Security config | Full security configuration dump | ## API Endpoints | Method | Path | Description | |--------|------|-------------| | GET | `/inspect/api/authorization` | Guards, role hierarchy, voters, security config | ## Adapter Support | Adapter | Provider | |---------|----------| | Symfony | AppDevPanel\Adapter\Symfony\Inspector\SymfonyConfigProvider (reads `security.yaml` config) | | Others | AppDevPanel\Api\Inspector\Authorization\NullAuthorizationConfigProvider (returns empty) | ::: info Authorization inspection requires framework-specific integration. Currently, only the Symfony adapter provides full security config introspection. ::: --- --- url: 'https://app-dev-panel.github.io/app-dev-panel/guide/collectors/cache.md' --- # Cache Collector Captures cache operations (get, set, delete) with hit/miss rates, timing, and per-pool breakdowns. ![Cache Collector panel](/images/collectors/cache.png) ## What It Captures | Field | Description | |-------|-------------| | `pool` | Cache pool name (e.g., `default`, `sessions`) | | `operation` | Operation type (`get`, `set`, `delete`, `has`, `clear`) | | `key` | Cache key | | `hit` | Whether the operation was a cache hit | | `duration` | Operation execution time in seconds | | `value` | Cached value (for get/set operations) | ## Data Schema ```json { "operations": [ { "pool": "default", "operation": "get", "key": "user:42", "hit": true, "duration": 0.0003, "value": {"name": "John"} } ], "hits": 8, "misses": 2, "totalOperations": 10 } ``` **Summary** (shown in debug entry list): ```json { "cache": { "hits": 8, "misses": 2, "totalOperations": 10 } } ``` ## Contract ```php use AppDevPanel\Kernel\Collector\CacheCollector; use AppDevPanel\Kernel\Collector\CacheOperationRecord; $collector->logCacheOperation(new CacheOperationRecord( pool: 'default', operation: 'get', key: 'user:42', hit: true, duration: 0.0003, value: ['name' => 'John'], )); ``` ::: info \AppDevPanel\Kernel\Collector\CacheCollector implements \AppDevPanel\Kernel\Collector\SummaryCollectorInterface and depends on \AppDevPanel\Kernel\Collector\TimelineCollector. ::: ## How It Works Framework adapters intercept PSR-16 Psr\SimpleCache\CacheInterface operations through the `CacheInterfaceProxy` decorator. Every `get()`, `set()`, `delete()`, `has()`, and `clear()` call is automatically captured. ## Debug Panel * **Hit rate summary** — total operations, hits, misses with percentage * **Per-pool breakdown** — statistics grouped by cache pool when multiple pools are used * **Operation list** — filterable list with operation type, key, hit/miss status, and timing * **Color coding** — hits (green), misses (orange), deletes (yellow) * **Value preview** — expandable cached values for get/set operations --- --- url: 'https://app-dev-panel.github.io/app-dev-panel/guide/inspector/cache.md' --- # Cache Inspector View, delete, and clear PSR-16 cache entries. ## What It Shows | Feature | Description | |---------|-------------| | View | Retrieve a cached value by key | | Delete | Remove a specific cache key | | Clear | Flush the entire cache | ## How To Use Enter a cache key to view its stored value. The inspector displays the deserialized value with type information. ## API Endpoints | Method | Path | Description | |--------|------|-------------| | GET | `/inspect/api/cache?key=my_cache_key` | View cached value | | DELETE | `/inspect/api/cache?key=my_cache_key` | Delete a cache key | | POST | `/inspect/api/cache/clear` | Clear entire cache | ## Requirements Requires a PSR-16 Psr\SimpleCache\CacheInterface implementation registered in the DI container. ::: warning The **Clear** action is destructive — it removes all cache entries. Use with care in production. ::: --- --- url: 'https://app-dev-panel.github.io/app-dev-panel/guide/ci-and-tooling.md' --- # CI & Tooling ADP uses GitHub Actions for continuous integration and [Mago](https://mago.carthage.software/) for PHP code quality enforcement. ## Mago — PHP Toolchain Mago is a Rust-powered toolchain that replaces PHPStan, PHP-CS-Fixer, and Psalm with a single binary. It provides three tools: | Tool | Purpose | Command | |------|---------|---------| | **Formatter** | Enforces PSR-12 code style | `make mago-format` | | **Linter** | Finds code smells, inconsistencies, anti-patterns | `make mago-lint` | | **Analyzer** | Static analysis: type errors, null safety, logic bugs | `make mago-analyze` | ### Running Mago ```bash make mago # Run all checks (format + lint + analyze) make mago-fix # Fix formatting, then lint + analyze make mago-playgrounds # Check all playground apps make mago-playgrounds-fix # Fix formatting in playground apps ``` ### Configuration Mago is configured via `mago.toml` in the project root: ```toml [source] paths = ["libs/Kernel/src", "libs/API/src", ...] includes = ["vendor"] # Parsed for type info only [formatter] preset = "psr-12" [linter] default-level = "warning" ``` ### Baselines Existing lint issues in legacy code are suppressed via `mago-lint-baseline.php`. The analyzer has no baseline — rules that produce false positives are suppressed via `ignore` in `mago.toml`. New code must not introduce new issues. ```bash composer lint:baseline # Regenerate lint baseline ``` ## PHPUnit — Testing Tests are organized per-module with a unified root `phpunit.xml.dist`: ```bash make test-php # Run all PHP tests ``` ### Test Suites | Suite | Directory | |-------|-----------| | Kernel | `libs/Kernel/tests` | | API | `libs/API/tests` | | Cli | `libs/Cli/tests` | | Adapter/Symfony | `libs/Adapter/Symfony/tests` | | Adapter/Laravel | `libs/Adapter/Laravel/tests` | | Adapter/Yii2 | `libs/Adapter/Yii2/tests` | | Adapter/Cycle | `libs/Adapter/Cycle/tests` | | McpServer | `libs/McpServer/tests` | ### Coverage Coverage requires the PCOV extension: ```bash pecl install pcov php vendor/bin/phpunit --coverage-text # Text summary php vendor/bin/phpunit --coverage-html=coverage # HTML report ``` ## Frontend Checks ```bash make frontend-check # Prettier + ESLint make frontend-fix # Auto-fix issues make test-frontend # Vitest unit tests make test-frontend-e2e # Browser tests (requires Chrome) ``` ## GitHub Actions ### CI Workflow (`ci.yml`) Runs on every push and PR. **Test matrix:** | OS | PHP 8.4 | PHP 8.5 | |----|:-------:|:-------:| | Linux | ✅ | ✅ | | Windows | ✅ | ✅ | **Mago checks** run as separate parallel jobs: * `mago fmt --check` * `mago lint --reporting-format=github` (annotates PR files) * `mago analyze --reporting-format=github` (annotates PR files) ### PR Report (`pr-report.yml`) Runs on PRs only. Posts two comments: 1. **Coverage report** — Code coverage summary from PHPUnit 2. **Mago report** — Pass/fail status for format, lint, analyze with expandable output on failure ## Full Pipeline Run the complete CI pipeline locally: ```bash make ci # Full CI: all checks + all tests make check # Checks only (Mago + frontend) make test # Tests only (PHP + frontend) make all # Same as make check && make test ``` ## Adding a New Library When adding a new library under `libs/`: 1. Add its `src/` and `tests/` paths to `mago.toml` under `[source] paths` 2. Add its test directory to `phpunit.xml.dist` as a new `` 3. Add its `src/` directory to the `` section of `phpunit.xml.dist` --- --- url: 'https://app-dev-panel.github.io/app-dev-panel/guide/cli.md' --- # CLI Commands ADP provides console commands for managing the debug system. All commands are built on Symfony Console and available through your framework's CLI runner or the standalone `debug:serve` server. ## Available Commands ### `dev` -- Debug Server Starts a UDP socket server that receives real-time debug messages from the application. ```bash php yii dev # Default: 0.0.0.0:8890 php yii dev -a 127.0.0.1 -p 9000 # Custom address and port ``` The server receives and displays three message types: * Variable dumps (`MESSAGE_TYPE_VAR_DUMPER`) * Log messages (`MESSAGE_TYPE_LOGGER`) * Plain text messages Supports graceful shutdown via `SIGINT` (Ctrl+C). ### `debug:reset` -- Clear Debug Data Stops the debugger and clears all stored debug data. ```bash php yii debug:reset ``` Internally calls AppDevPanel\Kernel\Debugger`::stop()` followed by AppDevPanel\Kernel\Storage\StorageInterface`::clear()`. ### `dev:broadcast` -- Broadcast Test Messages Sends test messages to all connected debug server clients. Useful for verifying connectivity. ```bash php yii dev:broadcast # Default: "Test message" php yii dev:broadcast -m "Hello world" # Custom message ``` ### `debug:query` -- Query Debug Data Query stored debug data from the command line. ```bash debug:query list # List recent entries (default 20) debug:query list --limit=5 # Limit entries debug:query list --json # Raw JSON output debug:query view # Full entry data debug:query view -c # Specific collector data ``` ### `debug:serve` -- Standalone ADP Server Starts a standalone HTTP server using PHP's built-in server, serving the ADP API directly. No framework required. ```bash debug:serve # Default: 127.0.0.1:8888 debug:serve --host=0.0.0.0 --port=9000 # Custom host/port debug:serve --storage-path=/path/to/debug/data # Custom storage directory debug:serve --frontend-path=/path/to/built/assets # Serve frontend assets ``` ### `mcp:serve` -- MCP Server (stdio) Starts the MCP server in stdio mode for AI assistant integration. See the [MCP Server](./mcp-server.md) page for details. ```bash php yii mcp:serve --storage-path=/path/to/debug-data ``` ## Command Summary | Command | Purpose | |---------|---------| | `dev` | Start real-time UDP debug server | | `debug:reset` | Clear all stored debug data | | `dev:broadcast` | Send test messages to debug server | | `debug:query` | Query stored entries from CLI | | `debug:serve` | Start standalone HTTP API server | | `mcp:serve` | Start MCP server (stdio transport) | --- --- url: 'https://app-dev-panel.github.io/app-dev-panel/guide/inspector/coverage.md' --- # Code Coverage Collect and view PHP code coverage data in real-time. ![Code Coverage](/images/inspector/coverage.png) ## What It Shows | Field | Description | |-------|-------------| | Driver | Coverage driver in use (`pcov` or `xdebug`) | | Total files | Number of files with coverage data | | Covered lines | Lines executed during the request | | Executable lines | Total lines that can be executed | | Percentage | Overall coverage percentage | ## Per-File Coverage Click a file to see line-by-line coverage highlighting — which lines were executed (green) and which were missed (red). ## API Endpoints | Method | Path | Description | |--------|------|-------------| | GET | `/inspect/api/coverage` | Collect coverage data with per-file stats | | GET | `/inspect/api/coverage/file?path=/src/Service.php` | Read source file for coverage display | ## Requirements Requires one of: * **PCOV** extension (recommended, lightweight) * **Xdebug** extension with coverage mode enabled ::: warning Code coverage collection adds overhead. Enable only during development/testing. ::: --- --- url: 'https://app-dev-panel.github.io/app-dev-panel/guide/collectors.md' --- # Collectors Collectors are the core data-gathering mechanism in ADP. Each collector implements AppDevPanel\Kernel\Collector\CollectorInterface and is responsible for capturing a specific type of runtime data during the application lifecycle. ## Built-in Collectors ### Core Collectors | Collector | Data Collected | Guide | |-----------|---------------|-------| | AppDevPanel\Kernel\Collector\LogCollector | PSR-3 log messages (level, message, context) | [Log](/guide/collectors/log) | | AppDevPanel\Kernel\Collector\EventCollector | PSR-14 dispatched events and listeners | [Event](/guide/collectors/event) | | AppDevPanel\Kernel\Collector\ExceptionCollector | Uncaught exceptions with stack traces | [Exception](/guide/collectors/exception) | | AppDevPanel\Kernel\Collector\HttpClientCollector | PSR-18 outgoing HTTP requests and responses | [HTTP Client](/guide/collectors/http-client) | | AppDevPanel\Kernel\Collector\DatabaseCollector | SQL queries, execution time, transactions | [Database](/guide/collectors/database) | | AppDevPanel\Kernel\Collector\ElasticsearchCollector | Elasticsearch requests, timing, hits count | [Elasticsearch](/guide/collectors/elasticsearch) | | AppDevPanel\Kernel\Collector\CacheCollector | Cache get/set/delete operations with hit/miss rates | [Cache](/guide/collectors/cache) | | AppDevPanel\Kernel\Collector\RedisCollector | Redis commands with timing and error tracking | [Redis](/guide/collectors/redis) | | AppDevPanel\Kernel\Collector\MailerCollector | Sent email messages | [Mailer](/guide/collectors/mailer) | | AppDevPanel\Kernel\Collector\TranslatorCollector | Translation lookups, missing translations | [Translator](/guide/collectors/translator) | | AppDevPanel\Kernel\Collector\QueueCollector | Message queue operations (push, consume, fail) | [Queue](/guide/collectors/queue) | | AppDevPanel\Kernel\Collector\ServiceCollector | DI container service resolutions | [Service](/guide/collectors/service) | | AppDevPanel\Kernel\Collector\RouterCollector | HTTP route matching data | [Router](/guide/collectors/router) | | AppDevPanel\Kernel\Collector\MiddlewareCollector | Middleware stack execution and timing | [Middleware](/guide/collectors/middleware) | | AppDevPanel\Kernel\Collector\ValidatorCollector | Validation operations and results | [Validator](/guide/collectors/validator) | | AppDevPanel\Kernel\Collector\AuthorizationCollector | Authentication and authorization data | [Authorization](/guide/collectors/authorization) | | AppDevPanel\Kernel\Collector\TemplateCollector | Template/view rendering with timing, output capture, and duplicate detection | [Template](/guide/collectors/template) | | AppDevPanel\Kernel\Collector\VarDumperCollector | Manual `dump()` / `dd()` calls | [VarDumper](/guide/collectors/var-dumper) | | AppDevPanel\Kernel\Collector\TimelineCollector | Cross-collector performance timeline | [Timeline](/guide/collectors/timeline) | | AppDevPanel\Kernel\Collector\EnvironmentCollector | PHP and OS environment info | [Environment](/guide/collectors/environment) | | AppDevPanel\Kernel\Collector\DeprecationCollector | PHP deprecation warnings | [Deprecation](/guide/collectors/deprecation) | | AppDevPanel\Kernel\Collector\OpenTelemetryCollector | OpenTelemetry spans and traces | [OpenTelemetry](/guide/collectors/opentelemetry) | | AppDevPanel\Kernel\Collector\AssetBundleCollector | Frontend asset bundles (Yii) | [Asset Bundle](/guide/collectors/asset-bundle) | | AppDevPanel\Kernel\Collector\Stream\FilesystemStreamCollector | Filesystem stream operations | [Filesystem Stream](/guide/collectors/filesystem-stream) | | AppDevPanel\Kernel\Collector\Stream\HttpStreamCollector | HTTP stream wrapper operations | [HTTP Stream](/guide/collectors/http-stream) | | AppDevPanel\Kernel\Collector\CodeCoverageCollector | Per-request PHP line coverage (requires pcov or xdebug) | [Coverage](/guide/inspector/coverage) | ### Web-Specific | Collector | Data Collected | Guide | |-----------|---------------|-------| | AppDevPanel\Kernel\Collector\Web\RequestCollector | Incoming HTTP request and response details | [Request](/guide/collectors/request) | | AppDevPanel\Kernel\Collector\Web\WebAppInfoCollector | PHP version, memory, execution time | [Web App Info](/guide/collectors/web-app-info) | ### Console-Specific | Collector | Data Collected | Guide | |-----------|---------------|-------| | AppDevPanel\Kernel\Collector\Console\CommandCollector | Console command executions | [Command](/guide/collectors/command) | | AppDevPanel\Kernel\Collector\Console\ConsoleAppInfoCollector | Console application metadata | [Console App Info](/guide/collectors/console-app-info) | ## CollectorInterface Every collector implements five methods: ```php interface CollectorInterface { public function getId(): string; // Unique ID (typically FQCN) public function getName(): string; // Human-readable short name public function startup(): void; // Called at request start public function shutdown(): void; // Called at request end public function getCollected(): array; // Returns all collected data } ``` The AppDevPanel\Kernel\Debugger calls `startup()` on all registered collectors at the beginning of a request, and `shutdown()` followed by `getCollected()` at the end. ## Creating a Custom Collector ```php metrics = []; } public function shutdown(): void { // Finalize data if needed } public function getCollected(): array { return $this->metrics; } public function record(string $name, float $value): void { $this->metrics[] = ['name' => $name, 'value' => $value]; } } ``` Register the collector in your framework adapter's DI configuration so the `Debugger` picks it up automatically. ## Data Flow Collectors receive data in two ways: 1. **Via proxies** -- PSR interface proxies (e.g., AppDevPanel\Kernel\Collector\LoggerInterfaceProxy) intercept calls and feed data to their paired collector automatically. 2. **Via direct calls** -- Adapter hooks or application code call methods on the collector directly (e.g., AppDevPanel\Kernel\Collector\DatabaseCollector receives query data from framework-specific database hooks). ## SummaryCollectorInterface Collectors can also implement AppDevPanel\Kernel\Collector\SummaryCollectorInterface to provide summary data displayed in the debug entry list without loading full collector data. ## TranslatorCollector Captures translation lookups during request execution, including missing translation detection. Implements AppDevPanel\Kernel\Collector\SummaryCollectorInterface. See the dedicated [Translator](/guide/translator) page for full details: TranslationRecord fields, collected data structure, missing detection logic, framework proxy integrations, and configuration examples. ## Code Coverage Collector AppDevPanel\Kernel\Collector\CodeCoverageCollector captures per-request PHP line coverage using [pcov](https://github.com/krakjoe/pcov) or [xdebug](https://xdebug.org/) as the coverage driver. ::: warning Prerequisites Requires the **pcov** extension (recommended) or **xdebug** with coverage mode enabled. Without either, the collector returns an empty result with `driver: null`. ::: ### How It Works 1. On `startup()`, the collector detects the available driver and starts coverage collection 2. Your application code runs normally — every executed PHP line is tracked 3. On `shutdown()`, coverage is stopped and raw data is processed into per-file statistics 4. Files matching `excludePaths` (default: `vendor`) are filtered out ### Enabling Code coverage is **opt-in** (disabled by default) due to performance overhead. :::tabs key:framework \== Symfony ```yaml # config/packages/app_dev_panel.yaml app_dev_panel: collectors: code_coverage: true ``` \== Laravel ```php // config/app-dev-panel.php 'collectors' => [ 'code_coverage' => true, ], ``` \== Yii 2 ```php // config/web.php — modules.debug-panel 'collectors' => [ 'code_coverage' => true, ], ``` ::: ### Output Format ```json { "driver": "pcov", "files": { "/app/src/Controller/HomeController.php": { "coveredLines": 12, "executableLines": 15, "percentage": 80.0 } }, "summary": { "totalFiles": 42, "coveredLines": 340, "executableLines": 500, "percentage": 68.0 } } ``` ### Inspector Endpoint The inspector also provides a live coverage endpoint at `GET /inspect/api/coverage` that performs a one-shot coverage collection. See [Inspector Endpoints](/api/inspector) for details. --- --- url: 'https://app-dev-panel.github.io/app-dev-panel/guide/collectors/command.md' --- # Command Collector Captures console command executions — command name, input/output, arguments, options, exit code, and errors. ## What It Captures | Field | Description | |-------|-------------| | `name` | Command name | | `command` | Command object | | `input` | Command input string | | `output` | Command output | | `exitCode` | Process exit code | | `error` | Error message if command failed | | `arguments` | Command arguments | | `options` | Command options | ## Data Schema ```json { "command": { "name": "app:import-users", "class": "App\\Command\\ImportUsersCommand", "input": "app:import-users --force", "output": "Imported 42 users.", "exitCode": 0, "error": null, "arguments": {}, "options": {"force": true} } } ``` **Summary** (shown in debug entry list): ```json { "command": { "name": "app:import-users", "class": "App\\Command\\ImportUsersCommand", "input": "app:import-users --force", "exitCode": 0 } } ``` ## Contract ```php use AppDevPanel\Kernel\Collector\Console\CommandCollector; // Collect from Symfony Console events $collector->collect(event: $consoleEvent); // Or collect raw command data $collector->collectCommandData([ 'name' => 'app:import-users', 'input' => 'app:import-users --force', 'exitCode' => 0, ]); ``` ::: info \AppDevPanel\Kernel\Collector\Console\CommandCollector implements \AppDevPanel\Kernel\Collector\SummaryCollectorInterface and depends on \AppDevPanel\Kernel\Collector\TimelineCollector. Located in the `Console` sub-namespace. ::: ## How It Works Framework adapters hook into console event lifecycle: * **Symfony**: `ConsoleCommandEvent`, `ConsoleTerminateEvent`, `ConsoleErrorEvent` * **Laravel**: Artisan command events * **Yii 3**: Console application events ## Debug Panel * **Command details** — name, class, input, and exit code * **Output capture** — full command output * **Error display** — error message and trace for failed commands --- --- url: 'https://app-dev-panel.github.io/app-dev-panel/guide/inspector/commands.md' --- # Commands Run application commands directly from the debug panel — tests, static analysis, and composer scripts. ![Commands](/images/inspector/commands.png) ## Available Command Types | Type | Description | |------|-------------| | PHPUnit | Run unit tests with JSON-formatted output | | Codeception | Run Codeception tests with JSON reporter | | Psalm | Run Psalm static analysis with JSON report | | Composer scripts | All scripts from `composer.json` (auto-discovered) | | Bash | Execute arbitrary shell commands | ## How It Works Commands are automatically discovered from two sources: 1. **Registered commands** — PHPUnit, Codeception, Psalm (if configured in the adapter) 2. **Composer scripts** — All `scripts` entries from `composer.json` are exposed as `composer/{scriptName}` commands Click a command button to execute it. Output is displayed in real-time. ## API Endpoints | Method | Path | Description | |--------|------|-------------| | GET | `/inspect/api/command` | List available commands | | POST | `/inspect/api/command?command=composer/test` | Execute a command | **Response format:** ```json { "status": "ok", "result": "PHPUnit 11.0.0 ...\nOK (42 tests, 100 assertions)", "error": "" } ``` ::: tip PHPUnit and Codeception commands use custom JSON reporters for structured output in the panel. ::: --- --- url: 'https://app-dev-panel.github.io/app-dev-panel/guide/inspector/composer.md' --- # Composer Inspector Browse installed packages, inspect package details, and install new dependencies from the panel. ![Composer Inspector](/images/inspector/composer.png) ## Tabs ### Packages Lists all installed Composer packages with version info. Shows both `require` and `require-dev` dependencies. Click **Switch** to toggle between production and development packages. ### composer.json View the raw `composer.json` contents. ### composer.lock View the raw `composer.lock` contents (if present). ## Package Inspection Click a package to see detailed info via `composer show --all --format=json`: * Description, homepage, license * All available versions * Dependencies and conflicts * Installation source ## Install Packages Install new packages directly from the panel: * Specify package name and optional version constraint * Choose between `--dev` and production dependency * Runs `composer require` in non-interactive mode ## API Endpoints | Method | Path | Description | |--------|------|-------------| | GET | `/inspect/api/composer` | Get composer.json and composer.lock | | GET | `/inspect/api/composer/inspect?package=vendor/name` | Package details | | POST | `/inspect/api/composer/require` | Install a package | **Install request body:** ```json { "package": "vendor/package-name", "version": "^2.0", "isDev": false } ``` ::: warning Package installation modifies `composer.json` and `composer.lock`. This runs `composer require` on the server. ::: --- --- url: 'https://app-dev-panel.github.io/app-dev-panel/guide/inspector/config.md' --- # Configuration Inspector Inspect your application's DI container configuration, service definitions, and parameters. ![Configuration Inspector](/images/inspector/config.png) ## Tabs ### Parameters View all application parameters and their values. Includes framework-specific parameters (e.g., `kernel.project_dir` for Symfony, `app.debug` for Laravel). ### Definitions Browse all service class definitions registered in the DI container. See which classes and interfaces are available for dependency injection. ### Container Inspect individual container services. View how a service is configured, its dependencies, and runtime state. ## Container Entry Viewer Click any service to see its full object dump — properties, dependencies, and configuration values resolved at runtime. ## API Endpoints | Method | Path | Description | |--------|------|-------------| | GET | `/inspect/api/params` | Application parameters | | GET | `/inspect/api/config` | DI configuration by group | | GET | `/inspect/api/classes` | All declared classes/interfaces | | GET | `/inspect/api/object?classname=App\Service\UserService` | Instantiate and dump a container object | ## Adapter Support Each adapter maps its framework's DI container to the inspector interface: * **Symfony**: Introspects `service_container` and compiler pass data * **Laravel**: Maps service bindings and resolved instances * **Yii 3**: Maps container definitions * **Yii 2**: Maps component and service locator config --- --- url: >- https://app-dev-panel.github.io/app-dev-panel/guide/collectors/console-app-info.md --- # ConsoleAppInfo Collector Collects console application performance metrics — processing time, memory usage, and adapter name. The console equivalent of [WebAppInfo Collector](/guide/collectors/web-app-info). ## What It Captures | Field | Description | |-------|-------------| | `applicationProcessingTime` | Total application processing time | | `requestProcessingTime` | Command execution time | | `applicationEmit` | Output emit time | | `preloadTime` | Bootstrap/preload time | | `memoryPeakUsage` | Peak memory usage in bytes | | `memoryUsage` | Current memory usage in bytes | | `adapter` | Framework adapter name | ## Data Schema ```json { "applicationProcessingTime": 1.250, "requestProcessingTime": 1.200, "applicationEmit": 0.001, "preloadTime": 0.049, "memoryPeakUsage": 16777216, "memoryUsage": 12582912, "adapter": "symfony" } ``` **Summary** (shown in debug entry list): ```json { "console": { "adapter": "symfony", "request": { "startTime": 1711878000.100, "processingTime": 1.200 }, "memory": { "peakUsage": 16777216 } } } ``` ## Contract ```php use AppDevPanel\Kernel\Collector\Console\ConsoleAppInfoCollector; $collector->markApplicationStarted(); // ... command execution ... $collector->markApplicationFinished(); ``` ::: info \AppDevPanel\Kernel\Collector\Console\ConsoleAppInfoCollector implements \AppDevPanel\Kernel\Collector\SummaryCollectorInterface and depends on \AppDevPanel\Kernel\Collector\TimelineCollector. Located in the `Console` sub-namespace. ::: ## How It Works Framework adapters call the `mark*()` methods at key points in the console command lifecycle. Memory metrics are captured via `memory_get_peak_usage()` and `memory_get_usage()`. ## Debug Panel Console entry metadata (processing time, memory) is displayed in the debug entry header, similar to web entries. --- --- url: 'https://app-dev-panel.github.io/app-dev-panel/guide/contributing.md' --- # Contributing ADP is a monorepo containing PHP backend libraries and a React/TypeScript frontend. This guide covers setting up your development environment, code conventions, and how to add new components. ## Prerequisites * PHP 8.4+ * Node.js 18+ and npm * Composer ## Installation ```bash make install # Install ALL dependencies (PHP + frontend + playgrounds) ``` Or install selectively: ```bash make install-php # Composer install (root) make install-frontend # npm install (libs/frontend) make install-playgrounds # Composer install for each playground app ``` ## Running Tests ```bash make test # Run ALL tests in parallel (PHP + frontend) make test-php # PHP unit tests (PHPUnit) make test-frontend # Frontend unit tests (Vitest) make test-frontend-e2e # Frontend browser tests (requires Chrome) ``` For PHP coverage reports, install the PCOV extension: ```bash pecl install pcov php vendor/bin/phpunit --coverage-text ``` ## Code Quality ADP uses [Mago](https://mago.carthage.software/) for PHP (formatting, linting, static analysis) and Prettier + ESLint for TypeScript. ```bash make check # Run ALL code quality checks make fix # Fix all auto-fixable issues # PHP only make mago # Check formatting + lint + analyze make mago-fix # Fix formatting, then lint + analyze # Frontend only make frontend-check # Prettier + ESLint make frontend-fix # Fix frontend issues ``` ### Mago Baselines Existing lint issues in legacy code are suppressed via a baseline file. The analyzer has no baseline — rules that produce false positives are suppressed via `ignore` in `mago.toml`. New code must not introduce new issues. ```bash composer lint:baseline # Regenerate lint baseline after fixing existing issues ``` ## Code Style ### PHP * **PER-CS (PER-2)** via [Mago](https://mago.carthage.software/) * `declare(strict_types=1)` in every file * `final class` by default * PSR interfaces for all abstractions ### TypeScript * **Prettier 3.8+**: single quotes, trailing commas, 120 char width, 4-space indent, `objectWrap: "collapse"` * **ESLint 9** with `@typescript-eslint` * `type` over `interface` (`consistent-type-definitions: "type"`) * Functional React components, Redux Toolkit patterns ## Module Dependencies Strict dependency rules ensure framework-agnosticism: ``` Adapter → API → Kernel │ ↑ └───────────────┘ Cli → Kernel Frontend → API (HTTP only) ``` | Module | Can depend on | Cannot depend on | |--------|--------------|-----------------| | Kernel | PSR interfaces, generic PHP libs | API, Cli, Adapter | | API | Kernel, PSR interfaces | Adapter, Cli | | Cli | Kernel, Symfony Console | API, Adapter | | Adapter | Kernel, API, framework packages | Other adapters | ## Testing Conventions * One test class per source class: `src/Foo/Bar.php` → `tests/Unit/Foo/BarTest.php` * Inline mocks only (`$this->createMock()`, anonymous classes) * No shared test utilities, no test environment classes * `assertSame()` over `assertEquals()` * Data providers via `#[DataProvider('name')]` attribute * Collectors extend `AbstractCollectorTestCase` ## Development Workflow 1. Create a feature branch 2. Write code and tests for your changes 3. Run checks: `make fix && make test` 4. Verify everything: `make all` (checks + tests combined) 5. Submit a pull request All checks must pass before merging. ## Adding a Collector 1. Create a class implementing AppDevPanel\Kernel\Collector\CollectorInterface in `libs/Kernel/src/Collector/` 2. Implement `startup()`, `shutdown()`, `getCollected()` 3. Optionally implement AppDevPanel\Kernel\Collector\SummaryCollectorInterface for entry list metadata 4. Write a test extending `AbstractCollectorTestCase` 5. Register in adapter configs (e.g., `libs/Adapter/Yii3/config/params.php`) See [Collectors](/guide/collectors) for the interface contract. ## Adding an Inspector Page ### Backend 1. Create a controller in `libs/API/src/Inspector/Controller/` 2. Add a route in `libs/API/config/routes.php` 3. Write a controller test extending `ControllerTestCase` ### Frontend 1. Create a page component in `packages/panel/src/Module/Inspector/Pages/` 2. Add an RTK Query endpoint in `packages/panel/src/Module/Inspector/API/` 3. Add a route to the inspector module's route config ## Project Structure | Directory | Contents | |-----------|----------| | `libs/Kernel/` | Core engine: debugger, collectors, storage, proxies | | `libs/API/` | HTTP API: REST endpoints, SSE, middleware | | `libs/McpServer/` | MCP server for AI assistant integration | | `libs/Cli/` | CLI commands | | `libs/Adapter/` | Framework adapters (Yii 3, Symfony, Laravel, Yii 2, Cycle) | | `libs/frontend/` | React frontend (panel, toolbar, SDK packages) | | `playground/` | Demo applications per framework | ## CI Pipeline GitHub Actions runs on every push and PR: * PHP tests on PHP 8.4 and 8.5 (Linux + Windows) * Mago format, lint, and static analysis * Frontend checks and tests * Coverage reports posted as PR comments Run the full CI pipeline locally: ```bash make ci ``` --- --- url: 'https://app-dev-panel.github.io/app-dev-panel/guide/adapters/cycle.md' --- # Cycle ORM Adapter The Cycle ORM adapter is a lightweight adapter that provides database schema inspection only. Unlike full adapters, it has no collectors, event listeners, or lifecycle wiring. ## Installation ```bash composer require app-dev-panel/adapter-cycle ``` ## Usage Register AppDevPanel\Adapter\Cycle\Inspector\CycleSchemaProvider as AppDevPanel\Api\Inspector\Database\SchemaProviderInterface in your framework's DI container: ```php use AppDevPanel\Adapter\Cycle\Inspector\CycleSchemaProvider; use AppDevPanel\Api\Inspector\Database\SchemaProviderInterface; use Cycle\Database\DatabaseProviderInterface; SchemaProviderInterface::class => static fn (DatabaseProviderInterface $db) => new CycleSchemaProvider($db), ``` ## Capabilities | Method | Status | Description | |--------|--------|-------------| | `getTables()` | Implemented | Lists all tables with columns, primary keys, row counts | | `getTable()` | Implemented | Single table schema with paginated records | | `explainQuery()` | Stub | Returns empty array | | `executeQuery()` | Stub | Returns empty array | ## When to Use Use the Cycle adapter when your application uses Cycle ORM and you want database schema inspection in the ADP panel. Combine it with a full framework adapter (Yii 3, Symfony, Laravel) that handles lifecycle, collectors, and API wiring. --- --- url: 'https://app-dev-panel.github.io/app-dev-panel/guide/data-flow.md' --- # Data Flow This page describes how debug data flows from your application to the ADP panel. ## Overview ``` Target App → Adapter → Proxies → Collectors → Debugger → Storage → API → Frontend ``` ## Request Lifecycle ### Phase 1: Startup When your application receives an HTTP request (or CLI command): 1. The adapter's event listener catches the framework's startup event 2. AppDevPanel\Kernel\Debugger`::startup()` is called, which: * Registers a shutdown function * Checks if the request/command should be ignored (via `$ignoredRequests` / `$ignoredCommands` patterns, `X-Debug-Ignore` header) * If not ignored: calls `startup()` on all registered collectors ### Phase 2: Data Collection During request processing, proxies intercept calls and feed data to collectors: ``` Application Code │ ├──▶ Logger::log() ──▶ LoggerInterfaceProxy ──▶ LogCollector ├──▶ EventDispatcher::dispatch() ──▶ EventDispatcherInterfaceProxy ──▶ EventCollector ├──▶ HttpClient::sendRequest() ──▶ HttpClientInterfaceProxy ──▶ HttpClientCollector ├──▶ Container::get() ──▶ ContainerInterfaceProxy ──▶ ServiceCollector ├──▶ VarDumper::dump() ──▶ VarDumperHandlerInterfaceProxy──▶ VarDumperCollector └──▶ throw Exception ──▶ ExceptionHandler ──▶ ExceptionCollector ``` Each collector accumulates data in memory for the duration of the request. Proxies record metadata like timestamps, file:line info (via `debug_backtrace()`), and unique IDs for correlation. ### Phase 3: Shutdown and Flush When the request completes (or a console command finishes), the AppDevPanel\Kernel\Debugger triggers shutdown: 1. Calls `shutdown()` on all collectors (resets internal state) 2. Calls `getCollected()` to retrieve accumulated data 3. Serializes objects using AppDevPanel\Kernel\Dumper (depth-limited to 30 levels, with circular reference detection) 4. Calls `flush()` on storage AppDevPanel\Kernel\Storage\FileStorage writes three JSON files per debug entry: | File | Contents | |------|----------| | `{id}/summary.json` | Entry metadata (timestamp, URL, status, collector summaries) | | `{id}/data.json` | Full collector payloads | | `{id}/objects.json` | Extracted unique PHP objects for deep inspection | All writes use `file_put_contents()` with `LOCK_EX` for atomicity. ### Phase 4: Garbage Collection After each flush, storage runs garbage collection: * Acquires a non-blocking lock on `.gc.lock` (skips if another process holds it) * Deletes entries beyond `historySize` (default 50), sorted by modification time ## Storage Format ``` runtime/debug/ ├── YYYY-MM-DD/ │ ├── {entryId}/ │ │ ├── summary.json │ │ ├── data.json │ │ └── objects.json │ ├── {entryId}/ │ │ └── ... │ └── .gc.lock └── .services.json ``` ### Summary Format ```json { "id": "1710520800123456", "collectors": ["LogCollector", "EventCollector", "RequestCollector"], "logger": {"total": 5}, "event": {"total": 12}, "http": {"count": 2, "totalTime": 0.45}, "request": {"url": "/api/users", "method": "GET", "status": 200}, "exception": null } ``` Collectors that implement AppDevPanel\Kernel\Collector\SummaryCollectorInterface contribute their summary keys (e.g., `logger`, `event`, `http`) for display in the entry list without loading full data. ## API Serving The API serves stored data through a middleware chain: ``` Frontend Request │ ▼ API Middleware Chain ┌────────────────────────────┐ │ 1. CorsAllowAll │ │ 2. IpFilter │ │ 3. TokenAuthMiddleware │ │ 4. FormatDataResponseAsJson│ │ 5. ResponseDataWrapper │ └────────────┬───────────────┘ │ ▼ CollectorRepository ├── .getSummary() → summary.json ├── .getDetail(id) → data.json └── .getObject(id) → objects.json │ ▼ JSON Response: {id, data, error, success, status} ``` ## Real-Time Updates (SSE) The frontend uses **SSE** (Server-Sent Events) to detect new entries in real-time: 1. Frontend subscribes to `GET /debug/api/event-stream` 2. API polls storage every second, computing an MD5 hash of current summaries 3. When a new entry appears, the hash changes and an event is emitted: `{"type": "debug-updated"}` 4. Frontend fetches the updated entry list The frontend `ServerSentEventsObserver` uses exponential backoff on connection failures: 1s base delay, doubles per attempt, 30s max, resets on successful connection. ## Ingestion API (External Applications) Non-PHP applications can send debug data via the **Ingestion API**, bypassing the proxy/collector pipeline entirely. Data is written directly to storage and appears in the panel alongside PHP debug entries. | Endpoint | Description | |----------|-------------| | `POST /debug/api/ingest` | Single debug entry with collectors + optional context/summary | | `POST /debug/api/ingest/batch` | Multiple entries (max 100). Returns `{ids: [...], count}` | | `POST /debug/api/ingest/log` | Shorthand for single log entry: `{level, message, context?}` | | `GET /debug/api/openapi.json` | OpenAPI specification for the Ingestion API | Request body format for single entry: ```json { "collectors": { "LogCollector": [{"level": "info", "message": "Hello"}] }, "summary": {}, "context": {} } ``` ## Inspector Proxy (Multi-App) The Inspector proxy enables the frontend to inspect external applications (Python, Node.js, Go, etc.) through a unified API. ``` Frontend: /inspect/api/routes?service=python-app │ ▼ InspectorProxyMiddleware ├── Extract service name from ?service= param ├── Resolve via ServiceRegistry → ServiceDescriptor ├── Check online status (lastSeenAt within 60s) ├── Map path to capability (e.g., /routes → "routes") ├── Verify service supports the capability │ ▼ (all checks pass) Proxy request to: {inspectorUrl}/inspect/api/routes ``` ### Capability Map | Path Prefix | Capability | |-------------|-----------| | `/config`, `/params` | `config` | | `/routes`, `/route/check` | `routes` | | `/files` | `files` | | `/cache` | `cache` | | `/table` | `database` | | `/translations` | `translations` | | `/events` | `events` | | `/command` | `commands` | | `/git` | `git` | | `/composer` | `composer` | | `/phpinfo` | `phpinfo` | | `/opcache` | `opcache` | ### Error Responses | Condition | Status | |-----------|--------| | Service not found | 404 | | Service offline (heartbeat timeout) | 503 | | Capability not supported | 501 | | No inspector URL configured | 502 | | Connection refused / host unresolved | 502 | | Request timeout | 504 | ## Service Registry External applications register with ADP and send periodic heartbeats to appear as online: ``` External App ADP │ │ │ POST /debug/api/services/register │ │ {service, language, inspectorUrl, │ │ capabilities} │ │ ──────────────────────────────────▶ │ │ │ │ POST /debug/api/services/heartbeat │ │ {service} (every <60s) │ │ ──────────────────────────────────▶ │ │ │ │ GET /debug/api/services/ │ │ ◀────────────────────────────────── │ (lists all with online/offline) ``` AppDevPanel\Kernel\Service\ServiceDescriptor contains: `service`, `language`, `inspectorUrl`, `capabilities[]`, `registeredAt`, `lastSeenAt`. A service is considered online if `now() - lastSeenAt < 60s`. ## Console Command Flow Console commands follow the same lifecycle as web requests, with these differences: * AppDevPanel\Kernel\Collector\Console\ConsoleAppInfoCollector replaces AppDevPanel\Kernel\Collector\Web\WebAppInfoCollector * AppDevPanel\Kernel\Collector\Console\CommandCollector replaces AppDevPanel\Kernel\Collector\Web\RequestCollector (both render under the unified "Request" panel in the UI) * No middleware, router, or asset collectors * Events: `ConsoleCommandEvent` triggers startup, `ConsoleTerminateEvent` triggers shutdown ## Debug Server (UDP Socket) The `dev` CLI command starts a UDP socket server for real-time log/dump output in the terminal: ```bash php yii dev -a 0.0.0.0 -p 8890 ``` When your application calls `dump()` or logs a message, the broadcaster: 1. Discovers running server sockets via glob (`/tmp/yii-dev-server-*.sock`) 2. Sends the data as base64-encoded JSON with an 8-byte length header 3. The server displays the message as a formatted block in the terminal Message types: `VarDumper`, `Logger`, and plain text. --- --- url: 'https://app-dev-panel.github.io/app-dev-panel/guide/collectors/database.md' --- # Database Collector Captures SQL queries, parameters, execution time, transactions, and duplicate query detection. ![Database Collector panel](/images/collectors/database.png) ## What It Captures | Field | Description | |-------|-------------| | `sql` | SQL query with parameter placeholders | | `rawSql` | SQL query with parameters inlined | | `params` | Bound parameters array | | `line` | Source file and line of the query call | | `status` | Query status (`success` or `error`) | | `rowsNumber` | Number of affected/returned rows | | `exception` | Exception if query failed | | `transactionId` | Associated transaction ID | ## Data Schema ```json { "queries": [ { "position": 0, "transactionId": null, "sql": "SELECT * FROM users WHERE id = :id", "rawSql": "SELECT * FROM users WHERE id = 42", "params": {":id": 42}, "line": "/app/src/UserRepository.php:35", "status": "success", "rowsNumber": 1, "exception": null, "actions": [] } ], "transactions": {}, "duplicates": { "groups": [], "totalDuplicatedCount": 0 } } ``` **Summary** (shown in debug entry list): ```json { "db": { "queries": {"error": 0, "total": 3}, "transactions": {"error": 0, "total": 1}, "duplicateGroups": 0, "totalDuplicatedCount": 0 } } ``` ## Contract ### Query lifecycle ```php use AppDevPanel\Kernel\Collector\DatabaseCollector; // Option A: start/end pattern $collector->collectQueryStart( id: 'query-1', sql: 'SELECT * FROM users WHERE id = :id', rawSql: 'SELECT * FROM users WHERE id = 42', params: [':id' => 42], line: '/app/src/UserRepository.php:35', ); $collector->collectQueryEnd(id: 'query-1', rowsNumber: 1); // Option B: single record use AppDevPanel\Kernel\Collector\QueryRecord; $collector->logQuery(new QueryRecord( sql: 'SELECT * FROM users WHERE id = :id', rawSql: 'SELECT * FROM users WHERE id = 42', params: [':id' => 42], duration: 0.0023, line: '/app/src/UserRepository.php:35', )); ``` ### Transactions ```php $collector->collectTransactionStart(isolationLevel: 'READ COMMITTED', line: '...'); $collector->collectTransactionEnd(status: 'commit', line: '...'); ``` ::: info \AppDevPanel\Kernel\Collector\DatabaseCollector implements \AppDevPanel\Kernel\Collector\SummaryCollectorInterface, depends on \AppDevPanel\Kernel\Collector\TimelineCollector, and uses \AppDevPanel\Kernel\Collector\DuplicateDetectionTrait for detecting repeated queries. ::: ## How It Works Framework adapters intercept database operations through framework-specific hooks: * **Symfony**: Doctrine DBAL middleware * **Laravel**: DB query listener * **Yii 2**: Log target for DB profiling messages * **Yii 3**: Query event listeners ## Debug Panel * **Query count and total time** — summary header with aggregate stats * **SQL syntax highlighting** — queries displayed with keyword coloring * **Query type badges** — SELECT, INSERT, UPDATE, DELETE color-coded * **Row count and timing** — per-query execution metrics * **Explain plan** — visual EXPLAIN plan tree for SELECT queries * **Duplicate detection** — highlights repeated identical queries * **Transaction grouping** — queries grouped by transaction boundaries --- --- url: 'https://app-dev-panel.github.io/app-dev-panel/guide/inspector/database.md' --- # Database Inspector Browse database tables, view schema and records, execute SQL queries, and analyze query plans. ![Database Inspector — Tables list](/images/inspector/database.png) ## Table Browser Lists all database tables with column count and record count. Click **View** to see the table's schema and records. ![Database Inspector — Table records](/images/inspector/database-table.png) ## What It Shows | Feature | Description | |---------|-------------| | Tables list | All tables with column/record counts | | Table schema | Column names, types, defaults, nullability | | Records | Paginated data rows (default 50, max 1000) | | SQL Query | Execute raw SQL against the database | | EXPLAIN | Analyze query execution plans (with optional `ANALYZE`) | ## SQL Query Executor Execute any SQL query directly from the panel. Supports parameterized queries for safety. ## EXPLAIN Plans Run `EXPLAIN` or `EXPLAIN ANALYZE` on queries to see execution plans, useful for debugging slow queries. ## API Endpoints | Method | Path | Description | |--------|------|-------------| | GET | `/inspect/api/table` | List all tables | | GET | `/inspect/api/table/{name}?limit=50&offset=0` | Table schema + paginated records | | POST | `/inspect/api/table/explain` | EXPLAIN a SQL query | | POST | `/inspect/api/table/query` | Execute a raw SQL query | **EXPLAIN request body:** ```json { "sql": "SELECT * FROM users WHERE id = ?", "params": [1], "analyze": true } ``` ## Adapter Support | Adapter | Provider | |---------|----------| | Symfony | AppDevPanel\Adapter\Symfony\Inspector\DoctrineSchemaProvider (Doctrine DBAL) | | Laravel | AppDevPanel\Adapter\Laravel\Inspector\LaravelSchemaProvider (Eloquent) | | Yii 2 | `Yii2DbSchemaProvider` | | Cycle ORM | AppDevPanel\Adapter\Cycle\Inspector\CycleSchemaProvider | ::: warning SQL queries execute against the live database. Use with care in production environments. ::: --- --- url: 'https://app-dev-panel.github.io/app-dev-panel/guide/debug-panel.md' --- # Debug Panel The ADP debug panel is a React SPA that provides a web UI for inspecting debug data collected from your application. When you install an adapter, the panel is automatically available at `/debug` on your application. ::: tip Live Demo Try the panel without installing anything: [Live Demo](https://app-dev-panel.github.io/app-dev-panel/demo/). Enter your application's URL in the backend field to connect. ::: ## How It Works Each adapter registers a `/debug` route that serves a minimal HTML page. This page: 1. Loads `bundle.js` and `bundle.css` from a static assets source (GitHub Pages by default) 2. Injects runtime configuration — backend URL (auto-detected from the current request), router basename, etc. 3. Mounts the React SPA which communicates with the `/debug/api/*` and `/inspect/api/*` endpoints ``` Browser → GET /debug → Adapter serves HTML → Loads bundle.js from CDN → React SPA mounts → Fetches data from /debug/api/* ``` No separate frontend server is needed. The panel works out of the box after installing an adapter. ## Accessing the Panel After installing an adapter, open your application in the browser and navigate to: ``` http://your-app/debug ``` The panel supports client-side routing, so all sub-paths (e.g., `/debug/logs`, `/debug/inspector/routes`) are handled by the SPA. ::: tip PHP Built-in Server When using PHP's built-in server, set `PHP_CLI_SERVER_WORKERS=3` or higher. ADP makes concurrent requests (SSE + data fetching); single-worker mode causes timeouts. ```bash PHP_CLI_SERVER_WORKERS=3 php -S 127.0.0.1:8080 -t public ``` ::: ## Static Assets Source By default, the panel loads assets from GitHub Pages: ``` https://app-dev-panel.github.io/app-dev-panel/bundle.js https://app-dev-panel.github.io/app-dev-panel/bundle.css ``` You can change the static URL to load assets from a different source: ### Option 1: GitHub Pages (Default) No configuration needed. Assets are automatically served from the latest release on GitHub Pages. ### Option 2: Local Assets from Release Download `panel-dist.tar.gz` from a [GitHub Release](https://github.com/app-dev-panel/app-dev-panel/releases), extract it to a public directory, and configure the static URL: ```bash # Download and extract curl -L https://github.com/app-dev-panel/app-dev-panel/releases/latest/download/panel-dist.tar.gz | tar xz -C public/adp-panel ``` Then configure the adapter to use the local path: :::tabs key:framework \== Symfony ```yaml # config/packages/app_dev_panel.yaml app_dev_panel: panel: static_url: '/adp-panel' ``` \== Laravel ```php // config/app-dev-panel.php 'panel' => [ 'static_url' => '/adp-panel', ], ``` \== Yii 3 ```php // config/params.php 'app-dev-panel/yii3' => [ 'panel' => [ 'staticUrl' => '/adp-panel', ], ], ``` \== Yii 2 ```php // config/web.php 'modules' => [ 'debug-panel' => [ 'class' => \AppDevPanel\Adapter\Yii2\Module::class, 'panelStaticUrl' => '/adp-panel', ], ], ``` ::: ### Option 3: Vite Dev Server During frontend development, you can point the panel to the local Vite dev server: ```bash cd libs/frontend npm start # Starts Vite on http://localhost:3000 ``` Then configure: :::tabs key:framework \== Symfony ```yaml app_dev_panel: panel: static_url: 'http://localhost:3000' ``` \== Laravel ```php 'panel' => [ 'static_url' => 'http://localhost:3000', ], ``` \== Yii 3 ```php 'app-dev-panel/yii3' => [ 'panel' => [ 'staticUrl' => 'http://localhost:3000', ], ], ``` \== Yii 2 ```php 'modules' => [ 'debug-panel' => [ 'class' => \AppDevPanel\Adapter\Yii2\Module::class, 'panelStaticUrl' => 'http://localhost:3000', ], ], ``` ::: ## Panel Modules The panel SPA includes the following modules: | Module | Path | Description | |--------|------|-------------| | Debug | `/debug` | View collected debug entries — logs, database queries, events, exceptions, timeline, HTTP requests, cache, mail, etc. | | Inspector | `/debug/inspector/*` | Live application state — routes, config, database schema, git, cache, files, translations, Composer packages | | LLM | `/debug/llm` | AI-powered chat and analysis of debug data | | MCP | `/debug/mcp` | MCP (Model Context Protocol) server configuration | | OpenAPI | `/debug/openapi` | Swagger UI for the ADP REST API | ## Configuration Reference | Parameter | Default | Description | |-----------|---------|-------------| | `static_url` | `https://app-dev-panel.github.io/app-dev-panel` | Base URL for panel static assets (bundle.js, bundle.css) | | `viewer_base_path` | `/debug` | Route prefix where the panel is mounted | ## Architecture The panel rendering is handled at the API layer (AppDevPanel\Api\Panel\PanelController), which is framework-agnostic. Each adapter simply routes `/debug` and `/debug/*` to the same AppDevPanel\Api\ApiApplication that handles API requests. ``` GET /debug/logs/detail → Adapter catches /debug/* (not /debug/api/*) → ApiApplication routes to PanelController → PanelController renders HTML with: - to bundle.css -