# DbCollector

> Captures every database query emitted during the request for the Database panel.

[API reference](/docs/api/index.md) › [Debug](/docs/extensions/debug/reference/index.md) › [collectors](/docs/extensions/debug/reference/collectors/index.md) › DbCollector

| class | `yii\debug\collectors\DbCollector` |
| --- | --- |
| Extends | [`Collector`](/docs/extensions/debug/reference/collectors/collector/index.md) |
| Implements | [`CollectorInterface`](https://github.com/php-forge/debug/blob/e4fbb8c3238a4d011ceef5e0094344abd50deaf4/src/CollectorInterface.php) |
| Source | [`src/collectors/DbCollector.php`](https://github.com/yii2-extensions/debug/blob/main/src/collectors/DbCollector.php#L43) |

Captures every database query emitted during the request for the Database panel.

Hooks every DB connection so each prepared statement records its row count, calculates per-query timings from the
profile log, and exposes the totals the exported summary adopts (query count, excessive callers).

The hook installs [`PDO::ATTR_STATEMENT_CLASS`](https://www.php.net/manual/en/class.pdo.php) on each profiled connection, an attribute PDO rejects on
persistent instances, so persistent PDO connections are not supported while the collector is active.

## Properties

| Property | Type | Description | Defined by |
| --- | --- | --- | --- |
| <span id="property-db"></span>`$db` | `string` | Application component id of the DB connection whose prepared statements are instrumented. Default: `'db'`. | `DbCollector` |
| <span id="property-dbEventNames"></span>`$dbEventNames` | `list<string>` | Profile log categories scanned for query timings. Default: `['yii\db\Command::query', 'yii\db\Command::execute']`. | `DbCollector` |
| <span id="property-excessiveCallerThreshold"></span>`$excessiveCallerThreshold` | `int\|null` | Number of DB calls the same backtrace can make before being flagged as an "Excessive Caller". `null` disables the check. Default: `null`. | `DbCollector` |
| [`$ignoredPathsInBacktrace`](#property-ignoredPathsInBacktrace) | `list<string>` | Paths whose backtrace frames are skipped when determining the "Caller". Default: `[]`. | `DbCollector` |
| <span id="property-module"></span>[`$module`](/docs/extensions/debug/reference/collectors/collector/index.md#property-module) | [`yii\debug\Module`](/docs/extensions/debug/reference/module/index.md)`\|null` | Debug module supplying the log target and sibling collectors, assigned by the module on registration. Default: `null`. | [`Collector`](/docs/extensions/debug/reference/collectors/collector/index.md) |

## Methods

| Method | Description | Defined by |
| --- | --- | --- |
| [`calculateTimings()`](#method-calculateTimings) | Calculates and caches the per-query timings for the request, dropping backtrace frames that match [`$ignoredPathsInBacktrace`](/docs/extensions/debug/reference/collectors/dbcollector/index.md#property-ignoredPathsInBacktrace) and tagging each timing with a stable hash of its remaining trace. | `DbCollector` |
| <span id="method-capture"></span>[`capture()`](/docs/extensions/debug/reference/collectors/collector/index.md#method-capture) final | Encodes the typed snapshot built by the subclass into the payload persisted for the panel. | [`Collector`](/docs/extensions/debug/reference/collectors/collector/index.md) |
| [`countCallerCals()`](#method-countCallerCals) | Counts how many times the same backtrace originated a DB query. | `DbCollector` |
| [`getExcessiveCallers()`](#method-getExcessiveCallers) | Returns the call counts for backtraces that issued at least [`$excessiveCallerThreshold`](/docs/extensions/debug/reference/collectors/dbcollector/index.md#property-excessiveCallerThreshold) queries. | `DbCollector` |
| [`getExcessiveCallersCount()`](#method-getExcessiveCallersCount) | Returns the number of distinct backtraces flagged as excessive callers. | `DbCollector` |
| [`getProfileLogs()`](#method-getProfileLogs) | Returns the profile log entries scanned for query timings (categories listed in [`$dbEventNames`](/docs/extensions/debug/reference/collectors/dbcollector/index.md#property-dbEventNames)). | `DbCollector` |
| [`id()`](#method-id) | Returns the stable ID pairing this collector with the Database panel. | `DbCollector` |
| [`instrument()`](#method-instrument) | Installs the [`DebugPdoStatement`](/docs/extensions/debug/reference/db/debugpdostatement/index.md) class on every DB connection so each prepared statement records its `rowCount()`. | `DbCollector` |
| <span id="method-shutdown"></span>[`shutdown()`](/docs/extensions/debug/reference/collectors/collector/index.md#method-shutdown) final | Deactivates the collector once, releasing any subscription created by [`start()`](/docs/extensions/debug/reference/collectors/collector/index.md#method-start). | [`Collector`](/docs/extensions/debug/reference/collectors/collector/index.md) |
| <span id="method-startup"></span>[`startup()`](/docs/extensions/debug/reference/collectors/collector/index.md#method-startup) final | Activates the collector once at the start of the request lifecycle. | [`Collector`](/docs/extensions/debug/reference/collectors/collector/index.md) |
| <span id="method-getLogMessages"></span>[`getLogMessages()`](/docs/extensions/debug/reference/collectors/collector/index.md#method-getLogMessages) protected | Returns the accumulated log messages, optionally filtered by level, category, and exclusion lists. | [`Collector`](/docs/extensions/debug/reference/collectors/collector/index.md) |
| <span id="method-getLogTarget"></span>[`getLogTarget()`](/docs/extensions/debug/reference/collectors/collector/index.md#method-getLogTarget) protected | Returns the initialized log target of the owning debug module. | [`Collector`](/docs/extensions/debug/reference/collectors/collector/index.md) |
| <span id="method-isStarted"></span>[`isStarted()`](/docs/extensions/debug/reference/collectors/collector/index.md#method-isStarted) protected | Returns whether the collector is active for the current request. | [`Collector`](/docs/extensions/debug/reference/collectors/collector/index.md) |
| [`snapshot()`](#method-snapshot) protected | Resolves the logger timings into typed query rows and snapshots them. | `DbCollector` |
| [`start()`](#method-start) protected | Resets the per-request caches, binds the row-count window to the current request tag, and installs the statement hook through [`instrument()`](/docs/extensions/debug/reference/collectors/dbcollector/index.md#method-instrument) when the module has not already done so. | `DbCollector` |
| [`stop()`](#method-stop) protected | Detaches the class-level after-open listener and clears the per-request caches, so a reused worker process starts clean. | `DbCollector` |

## Property details

### $ignoredPathsInBacktrace <span id="property-ignoredPathsInBacktrace"></span>

```php
public list<string> $ignoredPathsInBacktrace = []
```

Paths whose backtrace frames are skipped when determining the "Caller".

Yii framework files are ignored by default. Path aliases are resolved through [`Yii::getAlias()`](/docs/api/yii/baseyii/index.md#method-getAlias).

## Method details

### calculateTimings() <span id="method-calculateTimings"></span>

```php
public function calculateTimings(): array<int, array{info: string, category: string, timestamp: float, trace: array<int, array<string, mixed>>, level: int, duration: float, memory: int, memoryDiff: int, traceHash: string}>
```

Calculates and caches the per-query timings for the request, dropping backtrace frames that match
[`$ignoredPathsInBacktrace`](/docs/extensions/debug/reference/collectors/dbcollector/index.md#property-ignoredPathsInBacktrace) and tagging each timing with a stable hash of its remaining trace.

**Returns**

`array<int, array{info: string, category: string, timestamp: float, trace: array<int, array<string, mixed>>, level: int, duration: float, memory: int, memoryDiff: int, traceHash: string}>`: Timings indexed sequentially, each carrying the SQL token, category, capture timestamp, trace, nesting level, duration, memory snapshots, and trace hash.

### countCallerCals() <span id="method-countCallerCals"></span>

```php
public function countCallerCals(): array<string, int>
```

Counts how many times the same backtrace originated a DB query.

**Returns**

`array<string, int>`: Call counts indexed by the backtrace hash of the caller.

### getExcessiveCallers() <span id="method-getExcessiveCallers"></span>

```php
public function getExcessiveCallers(): array<string, int>
```

Returns the call counts for backtraces that issued at least [`$excessiveCallerThreshold`](/docs/extensions/debug/reference/collectors/dbcollector/index.md#property-excessiveCallerThreshold) queries.

**Returns**

`array<string, int>`: Call counts indexed by the backtrace hash of each excessive caller; empty when the check is disabled.

### getExcessiveCallersCount() <span id="method-getExcessiveCallersCount"></span>

```php
public function getExcessiveCallersCount(): int
```

Returns the number of distinct backtraces flagged as excessive callers.

**Returns**

`int`: Number of distinct backtraces that issued at least the threshold number of queries.

### getProfileLogs() <span id="method-getProfileLogs"></span>

```php
public function getProfileLogs(): list<yii\debug\collectors\LogTuple>
```

Returns the profile log entries scanned for query timings (categories listed in [`$dbEventNames`](/docs/extensions/debug/reference/collectors/dbcollector/index.md#property-dbEventNames)).

**Returns**

`list<yii\debug\collectors\LogTuple>`: Profile log entries in capture order.

### id() <span id="method-id"></span>

```php
public function id(): string
```

Returns the stable ID pairing this collector with the Database panel.

**Returns**

`string`: Stable collector ID.

### instrument() <span id="method-instrument"></span>

```php
public function instrument(): void
```

Installs the [`DebugPdoStatement`](/docs/extensions/debug/reference/db/debugpdostatement/index.md) class on every DB connection so each prepared statement records its
`rowCount()`.

The hook is applied through [`PDO::ATTR_STATEMENT_CLASS`](https://www.php.net/manual/en/class.pdo.php) rather than `Connection::$commandClass`, since the
latter is not exposed by every Yii 2 fork. Connections already instantiated and open are hooked immediately; a
class-level [`Connection::EVENT_AFTER_OPEN`](/docs/api/yii/db/connection/index.md#event-EVENT_AFTER_OPEN) listener covers every connection opened later in the request,
whatever its component id. Instrumenting them all keeps the recorded counts aligned with the profile timings,
which the logger collects from every profiled connection.

[`Module::initCollectors()`](/docs/extensions/debug/reference/module/index.md#method-initCollectors) calls this while the debugger bootstraps, ahead of the panels, so
queries issued by a panel constructor are counted as well; that first call also opens the row-count window.
Reinstallation is skipped while the hook is active, so the counts recorded so far survive [`start()`](/docs/extensions/debug/reference/collectors/dbcollector/index.md#method-start).

### snapshot() <span id="method-snapshot"></span>

```php
protected function snapshot(): PHPForge\Debug\Panel\Db\DbSnapshot|null
```

Resolves the logger timings into typed query rows and snapshots them.

**Returns**

`PHPForge\Debug\Panel\Db\DbSnapshot|null`: Captured query payload; `null` when the collector never started.

### start() <span id="method-start"></span>

```php
protected function start(): void
```

Resets the per-request caches, binds the row-count window to the current request tag, and installs the statement
hook through [`instrument()`](/docs/extensions/debug/reference/collectors/dbcollector/index.md#method-instrument) when the module has not already done so.

The counts are discarded only when they belong to another request, so the collector survives being restarted
inside one request: a handled exception makes the logger flush twice, and each flush drives
[`LogTarget::export()`](/docs/extensions/debug/reference/logtarget/index.md#method-export) through a full shutdown/startup cycle.

### stop() <span id="method-stop"></span>

```php
protected function stop(): void
```

Detaches the class-level after-open listener and clears the per-request caches, so a reused worker process starts
clean.

The recorded row counts stay in place: they are discarded by the next [`start()`](/docs/extensions/debug/reference/collectors/dbcollector/index.md#method-start) that belongs to a different
request, which keeps them readable when the same request captures more than once.