weldist/laravel-pulse-md5-patch
最新稳定版本:v1.0.0
Composer 安装命令:
composer require weldist/laravel-pulse-md5-patch
包简介
Laravel Pulse patch for MySQL servers without a usable MD5() function — uses a plain key_hash column hashed in PHP.
README 文档
README
A compatibility layer that makes Laravel Pulse run on MySQL servers where the built-in MD5() function is unavailable — most notably MySQL 9.6+ (which removed it from the server core) on managed services where you cannot install the Legacy Hashing component.
A weld.ist project.
Unofficial. Not affiliated with Laravel.
The Problem
Laravel Pulse's default migration defines the key_hash column on every pulse_* table as a generated column built from md5():
$table->char('key_hash', 16)->storedAs('unhex(md5(`key`))');
As of MySQL 9.6 (released January 2026), the MD5() and SHA1() SQL functions have been removed from the server core and relocated to a separate, optional Legacy Hashing component (component_legacy_hashing). On a server where that component is not installed, md5() simply does not exist — which breaks Pulse in two ways:
- Migration fails — or the server won't start.
php artisan migratedies on the firstpulse_*table because the generated-column expression references a missing function. Worse, in our case thepulse_*tables already existed: after the server was upgraded to 9.6 without the component, MySQL itself refused to start until we dropped them. - Storage layer assumes a generated column. Even if you hand-patch the schema, Pulse's
DatabaseStorageonly writeskeyand expects the database to derivekey_hash. Drop the generated-column expression and the hash is silentlyNULL→ unique constraints and upserts break.
The official remedy is to install the Legacy Hashing component:
INSTALL COMPONENT 'file://component_legacy_hashing';
But on many managed/hosted MySQL offerings you can't. They don't expose INSTALL COMPONENT or the filesystem path it reads from, so there's no way to restore MD5(). (Some of these services also reject md5() inside generated-column expressions outright — ERROR 3758 (HY000): Expression of generated column 'key_hash' contains a disallowed function: md5. — so the generated-column approach never worked there, even on older MySQL versions where MD5() itself was still present.)
- Patching by hand is brittle: every Pulse upgrade re-publishes the original migration and you re-fight the same error.
The Solution
This package swaps both halves of the assumption:
- Migration — ships a replacement for Pulse's default in which
key_hashis a plain, non-generated column (char(32)on MySQL/MariaDB,uuidon PostgreSQL,stringon SQLite). - Storage — binds a
PulseDatabaseStoragethat extends Pulse'sDatabaseStorageand only flips one switch:requiresManualKeyHash()returnstrue, so themd5()ofkeyis computed in PHP and written alongside it.
Pulse default: INSERT (key) → DB derives key_hash via unhex(md5(key)) ✗ when MD5() is unavailable
This package: INSERT (key, key_hash) → key_hash = md5(key) computed in PHP ✓
Nothing else in Pulse changes — dashboards, recorders, aggregation, trimming all behave exactly as before.
Requirements
- PHP
^8.1 laravel/pulse ^1.0
No MySQL version constraint — it works on 8.x and on 9.6+ (with or without the Legacy Hashing component), since the hash never touches a server-side function.
Installation
composer require weldist/laravel-pulse-md5-patch
The service provider is auto-discovered — no manual registration.
Setup
Publish this package's migration and run it instead of Pulse's own:
php artisan vendor:publish --tag=pulse-md5-patch-migrations php artisan migrate
Do not publish or run
php artisan vendor:publish --tag=pulse-migrations. The two migrations create the same tables and cannot coexist. If you previously ran Pulse's migration, drop thepulse_values,pulse_entries, andpulse_aggregatestables before migrating this one.
That's it.
How It Works
| Default Pulse | This package | |
|---|---|---|
key_hash column (MySQL/MariaDB) |
char(16) charset binary — generated via unhex(md5(key)) |
char(32) — populated by PHP md5() |
key_hash column (PostgreSQL) |
uuid — generated via md5("key")::uuid |
uuid — md5("key")::uuid (unchanged) |
key_hash column (SQLite) |
string |
string |
| Storage class | DatabaseStorage |
PulseDatabaseStorage (extends DatabaseStorage) |
key_hash source |
database expression | PHP md5() (requiresManualKeyHash() === true) |
| Storage binding | PulseServiceProvider::register() |
overridden in boot(), always wins |
Why bind in boot() instead of register()?
PulseServiceProvider binds Storage::class → DatabaseStorage::class inside its register() method. Re-binding in boot() — which always runs after every provider's register() — guarantees the override takes effect regardless of provider load order.
Why a plain char(32) instead of a generated column?
When MD5() isn't available server-side (MySQL 9.6+ without the Legacy Hashing component) — and on managed services that additionally forbid md5() inside generated-column expressions — there is no generated-column variant that works. Storing the 32-char hex output of PHP's md5() directly side-steps database-level computation entirely. (char(16) BINARY would also work for raw bytes, but char(32) keeps the value human-readable and matches what requiresManualKeyHash() emits.)
Minimal override
PulseDatabaseStorage overrides exactly one method — requiresManualKeyHash(). Every other line of Pulse's storage logic is inherited untouched, so upgrades to laravel/pulse carry over with no extra work.
Testing
The suite runs against a real MySQL server — the schema and storage behaviour this package changes only matter on MySQL, so SQLite would prove nothing. docker compose brings up a mysql:8.4 container alongside the PHP container automatically; you don't need a local MySQL.
# Build first (once per PHP version) DOCKER_BUILDKIT=0 docker compose --profile php81 build # Run tests (each profile starts its own MySQL) docker compose --profile php81 up --abort-on-container-exit --exit-code-from php81 # PHP 8.1 docker compose --profile php82 up --abort-on-container-exit --exit-code-from php82 # PHP 8.2 docker compose --profile php83 up --abort-on-container-exit --exit-code-from php83 # PHP 8.3 docker compose --profile php84 up --abort-on-container-exit --exit-code-from php84 # PHP 8.4 docker compose --profile php85 up --abort-on-container-exit --exit-code-from php85 # PHP 8.5 # Run a single test class or method docker compose --profile php81 run --rm php81 vendor/bin/phpunit --filter PulseStorageMysqlTest docker compose --profile php81 run --rm php81 vendor/bin/phpunit --filter test_value_is_persisted_with_php_computed_key_hash
Running vendor/bin/phpunit directly (outside Docker) works too — point it at a MySQL instance via DB_HOST, DB_PORT, DB_DATABASE, DB_USERNAME, DB_PASSWORD (defaults: 127.0.0.1:3306, db/user/pass all testing).
CI runs PHP 8.1–8.5 via .github/workflows/tests.yml.
License
This package is open-sourced software licensed under the MIT license.
统计信息
- 总下载量: 0
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 0
- 点击次数: 3
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2026-05-12