msouverain/rms-push-notifications-bundle
A Symfony bundle that brings push notification delivery into your application's service container, offering a reusable way to dispatch messages to mobile clients from your backend instead of wiring notification logic into every project.
Read this first: msouverain/rms-push-notifications-bundle is a Symfony bundle, not a Laravel package. It has 0 stars, 0 dependents, and no observable maintenance. The name and structure indicate it is a fork/descendant of the classic Symfony2 RMS\PushNotificationsBundle, which wrapped the (now-retired) Apple binary provider API and Google GCM. Treat this as source material to port, not as a composer require dependency.
Minimal steps:
composer.json and check the require block. Expect symfony/framework-bundle, symfony/config, symfony/dependency-injection with ~2.x/~3.x constraints. If so, Composer will not resolve it inside a Laravel 10/11 app (Laravel already pins symfony/* to 6/7).src/ into app/Support/Push/ (or a packages/ path repository with "type": "path"), then fix the autoload namespace to PSR-4 in your own composer.json. The message/payload classes are usually plain PHP and survive the transplant; the DI extension and services.xml do not.src/Message/ (payload objects), src/Service/ (the senders), DependencyInjection/Configuration.php (what config keys exist: certificates, API keys, sandbox flags), and Resources/config/services.xml (the service ids you'd otherwise autowire).iOSMessage/AndroidMessage, set the device token and a payload array, and dump the serialized payload. That is the only part of this package with durable value. Do not start by wiring the actual socket/HTTP send path; see Gotchas.// Goal of step 4: confirm you can construct a payload without the Symfony container
$message = new \RMS\PushNotificationsBundle\Message\iOSMessage();
$message->setDeviceIdentifier($token);
$message->setMessage('Hello');
$message->setData(['order_id' => 42]);
dump($message->getPayload()); // inspect shape; this is what you keep
1. Bind the library behind your own contract. The single most important pattern: never let RMS\... classes leak into controllers or jobs. Define an interface and adapt.
// app/Contracts/PushTransport.php
interface PushTransport
{
public function send(PushMessage $message): PushResult;
}
// app/Services/Push/LegacyRmsAdapter.php — wraps the vendored classes
class LegacyRmsAdapter implements PushTransport
{
public function send(PushMessage $message): PushResult
{
$rms = $message->platform() === 'ios'
? new iOSMessage()
: new AndroidMessage();
$rms->setDeviceIdentifier($message->token());
$rms->setMessage($message->body());
$rms->setData($message->data());
return $this->senderFor($message->platform())->send($rms);
}
}
2. Port the bundle config into config/push.php. The Symfony tree (rms_push_notifications.ios.certificate, .sandbox, .android.api_key) maps cleanly onto a Laravel config file plus .env entries. Do the mapping once in a service provider rather than scattering env() calls in adapters.
// config/push.php
return [
'ios' => [
'certificate' => env('PUSH_IOS_CERT'),
'passphrase' => env('PUSH_IOS_PASSPHRASE'),
'sandbox' => env('PUSH_IOS_SANDBOX', false),
],
'android' => ['api_key' => env('PUSH_ANDROID_KEY')],
];
3. Queue and fan out. Push is I/O-bound and flaky; always go through a job. One job per device token (not per message) gives you independent retries and clean failure accounting.
class SendPushNotification implements ShouldQueue
{
use Queueable, InteractsWithQueue;
public int $tries = 3;
public array $backoff = [10, 60, 300];
public function __construct(
public int $deviceId,
public string $body,
public array $data = [],
) {}
public function handle(PushTransport $transport): void
{
$device = DeviceToken::find($this->deviceId);
if (! $device || $device->is_stale) {
return; // idempotent no-op
}
$transport->send(new PushMessage(
platform: $device->platform,
token: $device->token,
body: $this->body,
data: $this->data,
));
}
}
4. Persist tokens with a real model. The bundle has no storage layer at all — it just takes an identifier string. You own device_tokens (user_id, platform, token unique, last_seen_at, is_stale) and the registration endpoint. Uniqueness should be on token, not (user_id, token): a token can migrate between users on a shared device.
5. Treat "feedback" as your own pruning loop. Older APNS bundles shipped a feedback-service reader; that endpoint is gone. Replace it with your own rule: mark is_stale on a permanent failure (unregistered/bad token) returned by the transport, and prune on a schedule. This keeps the adapter's error semantics meaningful.
6. Swap-friendly tests. Because everything goes through PushTransport, tests bind a RecordingPushTransport and assert on PushMessage value objects. You never need to boot the vendored senders in CI, which is good — they open raw sockets.
gateway.push.apple.com:2195, retired by Apple in 2021) and GCM (shut down in 2019, superseded by FCM). The message-building and token-management patterns still apply, but the send path will not deliver to modern devices. Plan to replace the socket/HTTP layer with APNs HTTP/2 + JWT (or kreait/firebase-php for FCM) and keep only the payload construction.extra.laravel auto-discovery, no facade. DependencyInjection/RMSPushNotificationsExtension.php only runs under Symfony's kernel. If you copy code in, delete the DI wiring rather than trying to emulate it — a single bind() in AppServiceProvider is the whole story.composer.json pins Symfony 2/3 components, do not force it with --ignore-platform-reqs. That produces two incompatible copies of symfony/config in vendor/ and failures that look unrelated. Vendor the classes.RMS/PushNotificationsBundle/...). Rename/move to PSR-4, or your autoloader will silently miss classes and you'll get "class not found" only at runtime..pem (cert+key concatenated) vs .p12 trips people up; also sandbox vs production host mismatch produces a generic disconnect, not a clear error. Verify the certificate's environment and expiry with openssl x509 -in cert.pem -noout -dates before debugging code.exec/file_get_contents usage before it enters your codebase, and pin the exact commit you vendored so the diff is reviewable later.How can I help you explore Laravel packages today?