Core engine of ADP. Framework-independent. Manages the debugger lifecycle, data collectors, storage, PSR proxy system, and object serialization.
Kernel depends on PSR interfaces and these core infrastructure helpers:
yiisoft/strings—CombinedRegexp(hot path in stream proxies),WildcardPatternfor ignore globsyiisoft/files—FileHelper::ensureDirectory/removeDirectory/isEmptyDirectoryforFileStorage+ GCyiisoft/var-dumper—ClosureExporter+VarDumper::create()at the core of the object dumpersymfony/console— Console event types forCommandCollectorsymfony/var-dumper— Variable dumper integrationguzzlehttp/psr7— PSR-7 HTTP message implementation
Core infra policy: the remaining yiisoft/* packages are pure utility libraries with no
framework coupling. Despite the yiisoft/ vendor prefix, they are not Yii framework
dependencies. Each kept dep carries real logic (regex compilation, recursive directory
cleanup, closure export, pretty dumping) that would cost more to re-implement than it
saves. yiisoft/json was dropped in favour of an internal AppDevPanel\Kernel\Helper\Json
wrapping native json_encode/decode with JSON_THROW_ON_ERROR.
Note: yiisoft/proxy was removed from Kernel. Container proxying (ContainerInterfaceProxy,
ServiceProxy, ServiceMethodProxy, ContainerProxyConfig, ProxyLogTrait) and
VarDumperHandlerInterfaceProxy now live in the Yii adapter (libs/Adapter/Yii3).
LoggerDecorator and VarDumperHandler remain in Kernel (DebugServer/) since they
only depend on yiisoft/var-dumper (core infra).
- Composer:
app-dev-panel/kernel - Namespace:
AppDevPanel\Kernel\ - PHP: 8.4+
| Class | Purpose |
|---|---|
Debugger |
Central orchestrator: startup → collect → shutdown → flush |
DebuggerIdGenerator |
Generates unique IDs for debug entries |
Dumper |
Serializes PHP objects with depth control and circular ref detection |
StorageInterface |
Abstraction for persisting debug data |
FileStorage |
JSON file-based storage with garbage collection |
SqliteStorage |
SQLite-backed storage with WAL journaling and prepared statements |
BroadcastingStorage |
Decorator that broadcasts entry-created UDP notifications via Broadcaster |
StorageFactory |
Resolves a driver name (file, sqlite) or class name into a StorageInterface instance |
MemoryStorage |
In-memory storage for testing |
CollectorInterface |
Interface all collectors must implement |
ServiceRegistryInterface |
Registry for external app service descriptors |
FileServiceRegistry |
JSON file-based service registry |
ServiceDescriptor |
Value object: service identity, URL, capabilities, heartbeat |
Inspector\Primitives |
Primitives::dump($value, $depth) — canonical serializer for inspector HTTP responses. Walks arrays, replaces every Closure with a ClosureDescriptor marker, then delegates to VarDumper::asPrimitives() |
Inspector\ClosureDescriptor |
ClosureDescriptor::describe($closure) — emits {__closure: true, source, file, startLine, endLine} for frontend code rendering |
Inspector\ClosureDescriptorTrait |
Sugar for adapter ConfigProviders — exposes self::describeClosure() delegating to ClosureDescriptor::describe() |
src/
├── Debugger.php # Main debugger class
├── DebuggerIdGenerator.php # ID generation
├── DebuggerIgnoreConfig.php # Ignore patterns for debugger
├── Dumper.php # Object serialization with depth/circular-ref control
├── DumpContext.php # Context for dump operations
├── FlattenException.php # Serializable exception representation
├── IgnoreConfig.php # General ignore configuration
├── ProxyDecoratedCalls.php # Trait for proxy delegation (__call, __get, __set)
├── StartupContext.php # Debugger startup context
├── Collector/ # Collectors and colocated PSR proxies
│ ├── CollectorInterface.php
│ ├── CollectorTrait.php
│ ├── SummaryCollectorInterface.php
│ ├── DuplicateDetectionTrait.php # Shared utility for duplicate entry detection
│ ├── LogCollector.php
│ ├── LoggerInterfaceProxy.php # PSR-3 proxy (feeds LogCollector)
│ ├── EventCollector.php
│ ├── EventDispatcherInterfaceProxy.php # PSR-14 proxy (feeds EventCollector)
│ ├── ServiceCollector.php
│ ├── ExceptionCollector.php
│ ├── HttpClientCollector.php
│ ├── HttpClientInterfaceProxy.php # PSR-18 proxy (feeds HttpClientCollector)
│ ├── SpanProcessorInterfaceProxy.php # OpenTelemetry proxy (feeds OpenTelemetryCollector)
│ ├── VarDumperCollector.php
│ ├── TimelineCollector.php
│ ├── CacheCollector.php # Cache operations: get/set/delete (fed by adapter hooks)
│ ├── CacheOperationRecord.php # Value object for cache operation
│ ├── CodeCoverageCollector.php # Per-request PHP line coverage (pcov/xdebug)
│ ├── CodeCoverageHelper.php # Shared coverage utilities (driver detection, processing)
│ ├── DatabaseCollector.php # SQL queries + transactions (fed by adapter hooks)
│ ├── QueryRecord.php # Value object for DB query
│ ├── ElasticsearchCollector.php # Elasticsearch requests (fed by adapter hooks)
│ ├── ElasticsearchRequestRecord.php # Immutable DTO for logRequest() pattern
│ ├── MessageRecord.php # Value object for mailer message
│ ├── MailerCollector.php # Email messages (fed by adapter hooks)
│ ├── AssetBundleCollector.php # Asset bundles (fed by adapter hooks)
│ ├── AuthorizationCollector.php # Auth: user, token, guards, role hierarchy, access decisions, auth events
│ ├── DeprecationCollector.php # PHP deprecation notices
│ ├── EnvironmentCollector.php # PHP environment info (extensions, ini settings)
│ ├── MiddlewareCollector.php # HTTP middleware stack execution
│ ├── OpenTelemetryCollector.php # OpenTelemetry spans (fed by OTLP ingestion)
│ ├── OtlpTraceParser.php # OTLP trace data parser
│ ├── SpanRecord.php # Value object for OTel span
│ ├── QueueCollector.php # Queue messages: dispatched/handled/failed (fed by adapter hooks)
│ ├── RedisCollector.php # Redis commands (fed by adapter hooks)
│ ├── RedisCommandRecord.php # Value object for Redis command
│ ├── RouterCollector.php # Route matching data (fed by adapter hooks)
│ ├── TemplateCollector.php # Template/view rendering with timing, output, params, duplicate detection
│ ├── TranslatorCollector.php # Translation lookups + missing detection
│ ├── TranslationRecord.php # Value object for translation lookup
│ ├── ValidatorCollector.php # Validation results (fed by adapter hooks)
│ ├── Web/
│ │ ├── RequestCollector.php
│ │ └── WebAppInfoCollector.php
│ ├── Console/
│ │ ├── CommandCollector.php
│ │ └── ConsoleAppInfoCollector.php
│ └── Stream/
│ ├── FilesystemStreamCollector.php
│ ├── FilesystemStreamProxy.php
│ ├── HttpStreamCollector.php
│ ├── HttpStreamProxy.php
│ └── StreamProxyTrait.php # Shared delegation for stream wrappers
├── Service/ # Service registry for multi-app inspection
│ ├── ServiceDescriptor.php
│ ├── ServiceRegistryInterface.php
│ └── FileServiceRegistry.php
├── Storage/
│ ├── StorageInterface.php
│ ├── StorageFactory.php # Resolves driver name ('file' | 'sqlite') or class name
│ ├── FileStorage.php # JSON file-based (default)
│ ├── SqliteStorage.php # SQLite (WAL + prepared statements)
│ ├── BroadcastingStorage.php # Decorator: broadcasts ENTRY_CREATED via UDP
│ ├── FileStorageGarbageCollector.php # Automatic cleanup of old entries
│ ├── GarbageCollector.php # GC interface
│ └── MemoryStorage.php
├── Event/ # Debugger lifecycle events
│ ├── ProxyMethodCallEvent.php
│ └── MethodCallRecord.php
├── Inspector/ # Shared serialization helpers for the inspector layer
│ ├── ClosureDescriptor.php # Converts Closure → {__closure: true, source, file, startLine, endLine}
│ ├── ClosureDescriptorTrait.php # Thin trait wrapper for adapter ConfigProviders
│ └── Primitives.php # `Primitives::dump()` — VarDumper::asPrimitives + deep closure walk
├── Helper/ # Utilities
│ ├── BacktraceIgnoreMatcher.php
│ └── StreamWrapper/
└── DebugServer/ # UDP socket server for real-time streaming
├── Broadcaster.php
├── Connection.php
├── LoggerDecorator.php # PSR-3 logger decorator that broadcasts via UDP
├── SocketReader.php
└── VarDumperHandler.php # VarDumper handler that broadcasts via UDP
startup() → [proxies feed collectors during request] → shutdown() → flush to storage
startup(): Generate entry ID, check ignore patterns, callstartup()on all collectors- Collection: Proxies intercept PSR calls and feed data to collectors transparently
shutdown(): Callshutdown()on all collectors, serialize data via Dumper- Storage: Write summary, data, and objects as three separate entries
- Create a class implementing
CollectorInterface - Implement
startup(),shutdown(),getCollected()methods - Register the collector in the adapter's configuration
Anywhere the Kernel or API serialises data that may contain PHP Closure values — storage
dumps (Dumper / DumpContext), live inspector responses (Inspector\Primitives), adapter
ConfigProviders normalising event listeners — the closure is replaced with a structured
descriptor:
{"__closure": true, "source": "static fn(...) => ...", "file": "/path", "startLine": 10, "endLine": 12}
Generated by Inspector\ClosureDescriptor::describe() (wraps Yiisoft\VarDumper\ClosureExporter).
The frontend's JsonRenderer detects the marker at any nesting level and renders source as a
syntax-highlighted PHP code block. Do not stringify closures manually — always route through
ClosureDescriptor::describe() or Primitives::dump().
Kernel provides PSR interface proxies that are colocated with their corresponding collectors
in src/Collector/. These proxies wrap PSR interfaces and feed intercepted data to collectors.
The application code is completely unaware of the interception.
Proxies in Kernel (framework-independent PSR proxies):
LoggerInterfaceProxy(PSR-3) — feedsLogCollectorEventDispatcherInterfaceProxy(PSR-14) — feedsEventCollectorHttpClientInterfaceProxy(PSR-18) — feedsHttpClientCollectorSpanProcessorInterfaceProxy(OpenTelemetry) — feedsOpenTelemetryCollector(optional, requiresopen-telemetry/sdk)
Stream proxies (PHP stream wrapper interception):
FilesystemStreamProxy— wrapsfile://stream wrapper, feedsFilesystemStreamCollectorHttpStreamProxy— wrapshttp://andhttps://stream wrappers, feedsHttpStreamCollectorStreamProxyTrait— shared delegation logic for both stream wrappers
Proxies moved to Yii adapter (libs/Adapter/Yii3/src/Proxy/):
ContainerInterfaceProxy(PSR-11),ContainerProxyConfig,ProxyLogTraitServiceProxy,ServiceMethodProxy(generic service interception)VarDumperHandlerInterfaceProxy
The ProxyDecoratedCalls trait remains in Kernel as shared infrastructure for all proxies.
Lives under src/Project/. Holds team-wide panel configuration that travels with the
codebase via VCS (so every developer gets the same Frames + OpenAPI specs after git pull).
| Class | Purpose |
|---|---|
ProjectConfig |
Immutable VO with frames and openapi (both array<string, string> of name → url). fromArray() / toArray() round-trip JSON; unknown keys ignored. withFrames() / withOpenApi() for safe mutation. |
ProjectConfigStorageInterface |
load() / save() / getConfigDir() |
FileProjectConfigStorage |
JSON-file impl. Atomic save via temp + rename. Auto-creates the config directory and writes/updates a .gitignore next to project.json with secrets.json pre-listed (anticipates a future companion file for API keys). |
Adapters wire FileProjectConfigStorage to a framework-appropriate path:
- Yii 3:
<root>/config/adp(alias@root/config/adp, override viaapp-dev-panel/yii3.projectConfigPath) - Symfony:
%kernel.project_dir%/config/adp(override viaapp_dev_panel.project_config_path) - Laravel:
base_path('config/adp')(override viaapp-dev-panel.project_config_path) - Yii 2:
@app/config/adp(override via the module'sprojectConfigPathproperty)
The API endpoint that surfaces this config is GET/PUT /debug/api/project/config (see API module).
The frontend keeps a localStorage cache so the panel works offline; the backend file is the
source of truth and overwrites local state on every successful GET.
Tracks external application instances that register with ADP for multi-app inspector proxying.
| Class | Purpose |
|---|---|
ServiceDescriptor |
Immutable value object: service name, language, inspector URL, capabilities, timestamps |
ServiceRegistryInterface |
register(), deregister(), heartbeat(), resolve(), all() |
FileServiceRegistry |
JSON file-based implementation (.services.json in storage dir), uses LOCK_EX |
ServiceDescriptor::isOnline() returns true if lastSeenAt is within 60 seconds (default timeout).
ServiceDescriptor::supports(string $capability) checks if the service declares a given capability, or * for all.
| Type | Class | Description |
|---|---|---|
TYPE_SUMMARY |
Summary metadata | Timestamp, URL, status, collector names |
TYPE_DATA |
Full data | Complete collector payloads |
TYPE_OBJECTS |
Object dumps | Serialized objects for deep inspection |
| Implementation | Use case |
|---|---|
FileStorage |
Default. One JSON file per entry under the storage path; per-entry GC. |
SqliteStorage |
Single .db file with entries table (WAL journaling, PRAGMA synchronous=NORMAL, indexed created_at). Prepared statements only; unknown storage types throw InvalidArgumentException instead of falling through into SQL. |
BroadcastingStorage |
Decorator around any concrete storage. Emits MESSAGE_TYPE_ENTRY_CREATED over UDP via Broadcaster so SSE listeners and CLI tail react in real time. |
MemoryStorage |
Unit-test helper. |
StorageFactory |
create(string $driver, array $config, DebuggerIdGenerator $idGenerator) — file / sqlite / any FQCN implementing StorageInterface. |
Storage receives data from two sources:
- Debugger flush — PHP collectors write via
StorageInterfaceafter request/command completion - Ingestion API —
IngestionControllerwrites directly to FileStorage for external (non-PHP) apps
FileStorage uses LOCK_EX for atomic writes and flock for GC mutual exclusion.