Laravel et PHP
Le paquet honk-me/honk-me fonctionne dans toute app PHP 8.3 et s’intègre pleinement à Laravel 13 : une façade, un canal de notification, le signalement des exceptions, des hooks pour le planificateur et un fake pour les tests.
- Paquet
-
honk-me/honk-mePublié - Code source
- github.com/honk-me/honk-php · Licence MIT
- Prérequis
- PHP 8.3 ou plus récent avec ext-curl (ou tout client PSR-18). L’intégration Laravel est prévue pour Laravel 13.
Installation
Ajoutez le paquet, publiez la configuration, puis indiquez le serveur et la clé d’ingestion d’un projet dans .env (ne commitez jamais la clé). honk:test envoie un Petit coup de klaxon pour vérifier la connexion.
composer require honk-me/honk-me
# publishes config/honk.php and adds HONK_URL and HONK_KEY to .env.example
php artisan honk:install HONK_URL=https://honk-me.app
HONK_KEY=honk_xxxxxxxxxxxx_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx php artisan honk:test # sends a light honk and prints the message id
php artisan about --only=honk # URL, key prefix (never the secret), queue, Context metadata Une clé d’ingestion (honk_…) permet à quiconque la détient d’envoyer des messages dans son projet. Gardez-la sur vos serveurs, dans vos tâches et dans les secrets de votre CI, jamais dans un navigateur ni dans une app mobile ou de bureau.
Envoyer un événement
La façade a un helper pour chaque niveau de l’échelle Honk. Par défaut, source reprend votre APP_NAME et environment votre APP_ENV.
use HonkMe\Laravel\Facades\Honk;
Honk::beep('Backup finished', 'nightly pg_dump took 42 s');
Honk::loud('Disk 91%', '/var on app-01', ['groupKey' => 'disk/app-01/var']); Le canal de notification
Utilisez le canal honk comme mail ou database. Les notifications en file d’attente ne ralentissent pas la requête du client, et la clé d’idempotence est l’identifiant de la notification : les nouvelles tentatives de la file ne notifient jamais deux fois. Gardez toHonk() déterministe : une nouvelle tentative doit envoyer le même contenu.
// app/Notifications/CustomerRequested.php
namespace App\Notifications;
use App\Models\CustomerRequest;
use HonkMe\Laravel\Notifications\HonkMessage;
use HonkMe\Laravel\Notifications\ToHonk;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Notification;
use Illuminate\Queue\Attributes\Backoff;
use Illuminate\Queue\Attributes\Tries;
use Illuminate\Support\Str;
#[Tries(5)]
#[Backoff(10, 60, 300, 900)]
class CustomerRequested extends Notification implements ShouldQueue, ToHonk
{
use Queueable;
public function __construct(public CustomerRequest $request)
{
$this->afterCommit();
}
public function via(object $notifiable): array
{
return ['honk']; // add 'mail', 'database', … as you like
}
public function toHonk(object $notifiable): HonkMessage
{
$r = $this->request;
return HonkMessage::create()
->title(Str::limit("New request: {$r->subject}", 150))
->line("{$r->name} ({$r->company})")
->line(Str::limit($r->body, 2000)) // message: ≤ 8192 bytes
->light() // a light honk (info)
->category('customers')
->groupKey("requests/{$r->id}") // one group per request
->occurredAt($r->created_at)
->url(route('admin.requests.show', $r)) // https only, shown as "Open link"
// buttons, up to 3 (the first is the main one): https://, mailto:, tel: or sms:
->action('Reply by email', "mailto:{$r->email}")
->action('Call', "tel:{$r->phone}")
->meta('request_id', (string) $r->id);
}
} $request = CustomerRequest::create($validated);
$admin->notify(new CustomerRequested($request)); // a User with the Notifiable trait
Notification::route('honk', null)->notify(new CustomerRequested($request)); // or without a user Maintenant, après la réponse ou depuis la file d’attente
Honk::defer() envoie après la réponse HTTP, dans le même processus. Honk::queue() envoie depuis un worker et résiste aux redémarrages. Les helpers simples envoient immédiatement, nouvelles tentatives comprises. defer() comme queue() valident le message tout de suite : les erreurs apparaissent dans votre code.
use HonkMe\Laravel\Facades\Honk;
use HonkMe\Message;
Honk::defer()->beep('New order', "{$order->email} paid {$order->total} €"); // after the response
Honk::queue(Message::make('Imported 1 204 rows')->beep()->channel('imports')); // a queued job
Honk::loud('Disk 91%', '/var on app-01', ['groupKey' => 'disk/app-01/var']); // right now Signaler les exceptions
Une ligne dans bootstrap/app.php transforme chaque exception signalée en Long coup de klaxon. Les répétitions d’une même erreur partagent un groupe (exceptions/<Class>@<file>:<line>), et chaque groupe klaxonne au plus une fois toutes les 5 minutes. Rien ne se passe tant que HONK_URL ou HONK_KEY manque, et aucune exception n’est jamais levée.
// bootstrap/app.php
use HonkMe\Laravel\Facades\Honk;
->withExceptions(function (Exceptions $exceptions): void {
$exceptions->report(Honk::reportable());
}) $exceptions->report(Honk::reportable(severity: 'blast', throttleSeconds: 60)); Tâches planifiées
honkOnFailure() envoie un problème avec le code de sortie et les dernières lignes affichées, puis un rétablissement la prochaine fois que la tâche réussit, uniquement après un échec : une tâche qui va bien ne vous dérange jamais.
// routes/console.php
Schedule::command('backup:run')->daily()->honkOnFailure(); // problem on failure, recovery once it works again
Schedule::command('reports:send')->hourly()->honkOnSuccess(); // a beep after every successful run Tester avec Honk::fake()
Le fake enregistre ce qui serait envoyé ou mis en file d’attente, sans jamais toucher au réseau. Il valide comme le vrai client : un message invalide fait toujours échouer votre test.
use HonkMe\Laravel\Facades\Honk;
use HonkMe\Message;
use HonkMe\Severity;
Honk::fake();
$this->post(route('quote.store'), $data);
Honk::assertSent(fn (Message $m) => $m->groupKey === 'requests/1' && $m->severity === Severity::Light);
Honk::assertSentTimes(1);
Honk::assertNotSent(fn (Message $m) => $m->severity === Severity::Blast);
Honk::assertQueued(fn (Message $m, string $idempotencyKey) => $m->channel === 'imports');
Honk::assertNothingSent(); // also assertNothingQueued(), assertNothingOutgoing() PHP sans framework
Sans Laravel, créez un Client et appelez les mêmes helpers. problem et recovery ouvrent et ferment un incident pour une clé de groupe.
composer require honk-me/honk-me use HonkMe\Client;
$honk = new Client(url: getenv('HONK_URL'), key: getenv('HONK_KEY'));
$honk->beep('Backup finished', 'nightly pg_dump took 42 s'); $client->problem('db/backup', 'Backup failed', 'pg_dump exited with 1'); // a long honk by default
$client->recovery('db/backup', 'Backup OK', 'pg_dump finished in 41 s'); // a beep by default Erreurs
Chaque exception étend HonkException et indique si une nouvelle tentative a du sens. Une ValidationException est un bug à corriger ; une exception temporaire peut être remise en file d’attente avec la même clé d’idempotence.
use HonkMe\Exception\HonkException;
use HonkMe\Exception\ValidationException;
try {
$honk->send($message, "order-{$order->id}-failed");
} catch (ValidationException $e) {
report($e); // a bug: $e->fields says what to fix
} catch (HonkException $e) {
if (!$e->isRetryable()) {
throw $e;
}
// retry later with $e->idempotencyKey, after $e->retryAfter seconds if set
} Des nouvelles tentatives sans doublon
- Chaque envoi porte une
Idempotency-Key: la vôtre, ou un UUIDv7 généré pour l’occasion. La même clé est réutilisée à chaque nouvelle tentative, et pendant 24 heures Honk répond à une requête rejouée avec l’identifiant d’origine etduplicate: true: une réponse perdue ne crée jamais de second message. - Une nouvelle tentative n’a lieu qu’en cas d’erreur réseau, de délai dépassé, de
429ou de5xx, avec un backoff exponentiel et un jitter complet, jamais avant leRetry-Afterdu serveur. Les autres réponses4xxne donnent lieu à aucune nouvelle tentative : corrigez plutôt la requête. - Chaque tentative expire au bout de 5 secondes, et tout s’arrête au bout de 30 secondes au total. Une attente qui dépasserait cette échéance, comme un quota quotidien remis à zéro à minuit, échoue immédiatement et indique quand réessayer.
- Les champs sont vérifiés localement avant l’envoi, et tous les champs invalides sont signalés en une seule fois.