Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Mysqlnd Analytics Laravel Package

js/mysqlnd-analytics

Collect and inspect MySQL Native Driver (mysqlnd) statistics from your PHP or Laravel app. Gain visibility into query performance, connection usage, and database health with simple analytics and reporting.

View on GitHub
Deep Wiki
Context7

Getting Started

Know what you are installing. Despite the js/ vendor prefix, this is a standalone PHP library (Johannes Schlüter's), not a Laravel package and not JavaScript. There is no service provider, facade, or config file. It takes the array returned by mysqli_get_client_stats() and runs rule-based heuristics over it, emitting tuning advice. Nothing on the request path belongs to it.

Step 1 — prove the data source exists. Before touching the library, confirm you can get stats at all. In php artisan tinker or a scratch script:

var_dump(extension_loaded('mysqli'));           // true required
var_dump(function_exists('mysqli_get_client_stats')); // true required
$stats = mysqli_get_client_stats();
print_r(array_slice($stats, 0, 15, true));

If ext-mysqli is absent — common when images only install pdo_mysql — stop here. You need docker-php-ext-install mysqli, and ideally only in a diagnostic image.

Step 2 — read the rule set before you run it. The half-day that pays for itself is reading src/ and skimming the checks: connection reuse, buffered vs. unbuffered result sets, prepared-statement reuse, compression, SSL overhead, query cache behaviour. That tells you which of your access patterns even have a matching heuristic.

Step 3 — run the analyser against captured stats. Do this outside Laravel first, in a scratch script with a saved $stats array, so you can iterate without booting the framework:

require __DIR__ . '/vendor/autoload.php';

$stats = json_decode(file_get_contents(__DIR__ . '/stats.json'), true);
$analyser = new \JS\MysqlndAnalytics\Analyser(); // check src/ for the exact entry point
foreach ($analyser->analyse($stats) as $finding) {
    echo $finding, PHP_EOL;
}

First real use case: a load test on staging. Reset nothing (you can't), note the snapshot, drive traffic, snapshot again, subtract, analyse the delta, and look for connection churn or unbuffered-result-set flags. That is the whole of the package's value in one loop. If it doesn't flag a deliberately planted problem, stop — you have your answer.

Implementation Patterns

The delta pattern is mandatory, not optional. mysqlnd counters are monotonically increasing lifetime totals for the process. Analysing raw totals tells you about history since worker start, not about the traffic you care about. Store a baseline and subtract:

final class MysqlndStatsCollector
{
    public function snapshot(): array
    {
        return function_exists('mysqli_get_client_stats')
            ? mysqli_get_client_stats()
            : [];
    }

    public function delta(array $before, array $after): array
    {
        $delta = [];
        foreach ($after as $key => $value) {
            if (!is_numeric($value)) { continue; }
            $delta[$key] = $value - ($before[$key] ?? 0);
        }
        return $delta;
    }
}

Insulate the third-party types behind a DTO. This is the single most important decision given the maintenance profile. The analyser's return types should never appear in application code:

final class MysqlndReport
{
    public function __construct(
        public readonly array $findings,
        public readonly array $rawCounters,
        public readonly int $servedRequests,
    ) {}
}

final class MysqlndAnalyser
{
    public function analyse(array $delta, int $servedRequests): MysqlndReport
    {
        $findings = $this->library->analyse($delta);   // only place the lib is touched
        return new MysqlndReport($findings, $delta, $servedRequests);
    }
}

Swapping to a homegrown rule set later then becomes a one-class change instead of a grep.

Expose it through an artisan command, never a route. A CLI-only path is the right shape for a per-worker diagnostic and keeps the output off the network:

php artisan mysqlnd:report --baseline --json

Guard it with $this->confirm('This reports process-local counters; continue?') in production, or gate the command registration on !app()->isProduction().

Gate the guidance by server flavour. Query-cache advice was removed in MySQL 8.0 but remains meaningful on MariaDB. Wrap the analyser call in a version check and annotate or drop irrelevant findings; otherwise the tool's most prominent advice is actively wrong on a modern target.

Sampling pattern when you want more than a one-off. Hook terminate() or a queued middleware to capture a snapshot every N requests, write the delta to a dedicated log channel or Redis, and analyse asynchronously. Do not analyse on every request — the cost is small but the output is noise below a few hundred queries.

Under Octane it gets more useful and less attributable. Long-lived workers accumulate heterogeneous history, so the analyser sees a much richer sample. Offset that by snapshotting on an interval (per N requests, not per worker-lifetime) so the delta corresponds to a describable window. Under classic PHP-FPM, treat each worker as an isolated, short sample.

Pair it with ground truth, don't substitute for it. The intended workflow is: Pulse/Telescope or slow query log identifies which query is slow; a deliberate load test with this library plus SHOW GLOBAL STATUS and performance_schema tells you whether the shape of your database access (churn, round-trips, payload) is the contributing cause. Run them in the same session; the combination is what produces a decision.

Gotchas and Tips

  • Counters are per-process, shared across drivers. mysqli_get_client_stats() aggregates all mysqlnd connections in the process — including those opened by PDO. This is the bridge that lets a pdo_mysql Laravel app harvest stats, but it also means you cannot separate PDO traffic from mysqli traffic in the numbers. Fine for a whole-app health check, useless for attributing to a subsystem.
  • There is no reset. No API to zero the counters. Deltas or nothing. Under FPM the "reset" is worker recycle, which also means short-lived workers give you tiny, noisy samples.
  • Below the noise floor, don't analyse. Warn in the command if the process has served fewer than a few hundred requests or if the delta contains zero queries. Analysing 12 queries produces confident-sounding nonsense.
  • ext-mysqli is frequently missing in production images where only pdo_mysql is installed. Feature-detect with function_exists() in every code path, and fail into a clear "diagnostics unavailable" message rather than a fatal.
  • MySQL 8 removed the query cache. Any finding about query-cache hit rate is noise there. Version-gate the output or your reviewers will learn to distrust the tool.
  • It is unmaintained and predates PHP 8. Expect dynamic-property deprecations and loose comparisons to surface as warnings — or fatals under a strict error handler. Validate on your exact production minor version in a throwaway branch first, and expect to vendor a fork. Because the library is small, forking is cheap; because it's unmaintained, forking is likely inevitable.
  • Heuristics, not measurement. It will not tell you which query is slow or how long anything took. It tells you whether connection/result-set/protocol patterns look inefficient. Presenting its output as profiling is the fastest way to lose credibility with the team.
  • Don't expose the output publicly. Findings enumerate server behaviour and connection topology. Useful reconnaissance, no CVE required. CLI or local-only.
  • Extension points are limited and that's the point. There is no plugin registry worth relying on. Fork-and-patch under your own namespace with a documented patch list is more honest than betting on upstream. BSD-3-Clause makes that clean, with attribution.
  • Record the verdict. When you're done, write down the PHP/MySQL envelope you validated against and whether the checks flagged your planted inefficiency. That note is the artifact that saves the next developer the same spike.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource