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 Bundle Laravel Package

js/mysqlnd-bundle

A bundle that wires the MySQL native driver (mysqlnd) into your PHP stack, giving you a simple way to configure and use mysqlnd-backed database connections. Small, focused, and easy to drop into projects already running PHP's native MySQL driver.

View on GitHub
Deep Wiki
Context7

Getting Started

Read this first: you cannot install this in a Laravel app. js/mysqlnd-bundle is a Symfony2 bundle (last release 2013-10-15, BSD-3-Clause, ~80 stars). It registers a DataCollector via the data_collector DI tag and renders a panel in the Symfony web_profiler toolbar. Laravel has no data_collector tag, no profiler service, and the bundle's composer.json pins Symfony 2.x components that will conflict with a modern Laravel install. composer require js/mysqlnd-bundle will either fail resolution or install dead code you can't boot.

So treat this as a reference implementation, not a dependency. The valuable part is roughly one class: the collector that calls PHP's mysqlnd statistics API and merges the result into the profiler timeline.

Step 1 — confirm your stack is actually mysqlnd-backed.

php -i | grep -i "mysqlnd"
php -r 'var_dump(function_exists("mysqli_get_client_stats"));'
php -r 'var_dump(mysqli_get_client_stats());'   # after connecting, returns counters

If mysqli_get_client_stats() is undefined, your PHP is linked against libmysqlclient (common on older distro packages) and nothing from this bundle's approach applies.

Step 2 — read the collector. Open the bundle's DataCollector/*.php. The shape is: collect(Request $request, Response $response) calls the mysqlnd stats functions, stores the array, and getName()/getLabel() return the toolbar label. That's the whole trick — ~100 lines of array wrangling.

Step 3 — first use case. The reason you care: Laravel's DB::listen only sees queries that go through Eloquent/Query Builder. mysqlnd counters see the driver. If you bind a snapshot helper and diff it around a request, the mismatch between result_set_queries and your own query count tells you something is issuing queries outside your listener — or that prepared statements are being re-prepared per request.

// app/Support/MysqlndStats.php
namespace App\Support;

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

    public function delta(array $before, array $after): array
    {
        return collect($after)
            ->mapWithKeys(fn ($v, $k) => [$k => is_int($v) ? $v - ($before[$k] ?? 0) : $v])
            ->filter(fn ($v, $k) => is_int($v) && $v !== 0)
            ->all();
    }
}

Bind it as a singleton in a service provider and gate it behind app()->isLocal().

Implementation Patterns

1. Middleware delta capture + Debugbar collector. The direct port of the bundle is a Debugbar collector, because barryvdh/laravel-debugbar uses the same collect()/getName() contract as Symfony's DataCollector.

use DebugBar\DataCollector\DataCollector;
use DebugBar\DataCollector\Renderable;

class MysqlndCollector extends DataCollector implements Renderable
{
    public function __construct(private MysqlndStats $stats) {}

    public function collect(): array
    {
        return $this->stats->lastDelta(); // set by the middleware
    }

    public function getName(): string { return 'mysqlnd'; }

    public function getWidgets(): array
    {
        return [
            'mysqlnd' => ['icon' => 'database', 'widget' => 'PhpDebugBar.Widgets.VariableListWidget', 'map' => 'mysqlnd', 'default' => '{}'],
        ];
    }
}

Register with Debugbar::addCollector(new MysqlndCollector(app(MysqlndStats::class))) in a provider's boot().

The contract mapping you'll do once and forget:

Symfony2 bundle Laravel equivalent
DataCollectorInterface::collect() DataCollector::collect()
getName() / getLabel() getName() (label comes from the widget def)
reset() (required from 2.8+) re-snapshot at request start in middleware
data_collector DI tag Debugbar::addCollector()
web_profiler toolbar laravel-debugbar
config.yml bundle tree config/mysqlnd.php + env flags

2. Diff, never absolute. mysqlnd counters are cumulative for the life of the connection/process. Always snapshot-before / snapshot-after. Store the delta on the request-scoped singleton so the collector just reads it.

3. Pair driver stats with application stats. The useful view is a two-column table per request:

  • result_set_queries (mysqlnd) vs count($queries) from DB::listen
  • prepared_statements / prepared_statements_to_be_closed vs how many select/insert calls you made
  • bytes_sent / bytes_received to spot oversized result sets that aren't a query-count problem
  • connection_... counters to spot connection churn

4. Artisan command for queued/CLI work. Profiling middleware never fires in queue workers, which is where mysqlnd stats are most useful (long-lived processes, persistent connections). Wrap the same snapshot/delta in a mysqlnd:stats command, or a JobProcessing/JobProcessed listener.

5. Config-driven enablement. Mirror the bundle's toggle with a config file:

// config/mysqlnd.php
return [
    'enabled' => env('MYSQLND_PROFILING', false),
    'collect_memory' => env('MYSQLND_MEMORY', false),
];

Only register the middleware/collector when enabled, so production pays zero cost.

Gotchas and Tips

mysqlnd is not guaranteed. Distro PHP and some Docker images link against libmysqlclient. Check before you build anything; there is no fallback API for the client-side counters.

PDO hides the handle — this is the big one. The bundle was written for apps talking to MySQL via mysqli, where you can call mysqli_get_connection_stats($link) on the live connection. Laravel's DB facade is PDO, and PDO deliberately does not expose the underlying mysqli object. So:

  • mysqli_get_client_stats() still works (it's process-global, not per-connection) — fine for aggregate deltas.
  • mysqli_get_connection_stats() is unusable on your real connection. Opening a separate mysqli just to read stats gives you a different connection's numbers, which is worse than nothing.

This mismatch is the primary reason this bundle doesn't transfer to Laravel; lean on DB::listen for attribution and mysqlnd for the aggregate sanity check.

Counters are per-process, and persistent connections break the delta. With php-fpm and non-persistent connections, each request gets fresh counters. With PDO::ATTR_PERSISTENT => true — or in Octane/queue workers — the connection (and its counters) survive across requests, so your "before" snapshot is already inflated. Also there is no reset API; the only way to zero the counters is to reconnect.

Prepared statement counters are the sleeper feature. prepared_statements_to_be_closed growing means you're preparing without closing — a real leak signal that no query log will show you. prepared_statements climbing every request means the prepared statement cache is being thrown away (typical when connections are re-created or ATTR_EMULATE_PREPARES config flips).

Enable ini flags if you want memory numbers. mysqlnd.collect_statistics is on by default; mysqlnd.collect_memory_statistics is off by default and needs an ini change plus a PHP restart. The bundle's panel silently shows an empty memory section without it.

The bundle's Symfony API is stale in three ways if you try to lift the class verbatim: getLabel() was removed/renamed in later Symfony, reset() became mandatory on DataCollector, and constructor injection signatures changed. Strip the class down to its array-building logic and re-home it.

mysqlnd plugins inflate your numbers. If mysqlnd_qc (query cache) or mysqlnd_ms (master/slave splitting) are loaded, some queries never reach MySQL and some are served from cache — your result_set_queries won't line up with SHOW GLOBAL STATUS LIKE 'Com_select'. Cross-check with performance_schema or SHOW SESSION STATUS before concluding you have "hidden queries."

Never enable this in production. Aggregate counters are low-risk, but combined with a Debugbar panel you're exposing query volume, connection topology and timing to anyone who can trigger it. Guard with app()->isLocal() && config('mysqlnd.enabled'), and prefer an explicit route or command over always-on middleware.

Extension point that actually exists in 2024: the Debugbar collector interface plus DB::listen. Build the mysqlnd delta as a supplement to your existing query timeline, not a replacement — mysqlnd can tell you how much traffic happened, only your query log can tell you which query did it.

If you just want query profiling in Laravel, barryvdh/laravel-debugbar + laravel/telescope + DB::listen cover 95% of this bundle's panel. The remaining 5% — driver-level bytes, prepared statement lifecycle, memory per result set — is the only thing worth porting, and it's maybe 150 lines of your own code.

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.
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons