- Notifications
You must be signed in to change notification settings - Fork308
web-push-libs/web-push-php
Folders and files
| Name | Name | Last commit message | Last commit date | |
|---|---|---|---|---|
Repository files navigation
Web Push library for PHP
WebPush can be used to send push messages to endpoints as described in theWeb Push protocol.
This push message is then received by the browser, which can then create a notification using theservice worker and theNotifications API.
PHP 8.1+ and the following extensions:
- bcmath and/or gmp (optional but better for performance)
- mbstring
- curl
- openssl (with elliptic curve support)
There is no support and maintenance for older PHP versions, however you are free to use the following compatible versions:
- PHP 5.6 or HHVM:
v1.x - PHP 7.0:
v2.x - PHP 7.1:
v3.x-v5.x - PHP 7.2:
v6.x - PHP 7.3 7.4:
v7.x - PHP 8.0 / Openssl without elliptic curve support:
v8.x
This README is only compatible with the latest version. Each version of the library has a git tag where the corresponding README can be read.
Usecomposer to download and install the library and its dependencies.
composer require minishlink/web-push
A complete example with html+JS frontend and php backend usingweb-push-php can be found here:Minishlink/web-push-php-example
<?phpuseMinishlink\WebPush\WebPush;useMinishlink\WebPush\Subscription;// store the client-side `PushSubscription` object (calling `.toJSON` on it) as-is and then create a WebPush\Subscription from it$subscription = Subscription::create(json_decode($clientSidePushSubscriptionJSON,true));// array of notifications$notifications = [ ['subscription' =>$subscription,'payload' =>'{"message":"Hello World!"}', ], [// current PushSubscription format (browsers might change this in the future)'subscription' => Subscription::create(["endpoint" =>"https://example.com/other/endpoint/of/another/vendor/abcdef...","keys" => ['p256dh' =>'(stringOf88Chars)','auth' =>'(stringOf24Chars)' ], ]),'payload' =>'{"message":"Hello World!"}', ], [// old Firefox PushSubscription format'subscription' => Subscription::create(['endpoint' =>'https://updates.push.services.mozilla.com/push/abc...',// Firefox 43+,'publicKey' =>'BPcMbnWQL5GOYX/5LKZXT6sLmHiMsJSiEvIFvfcDvX7IZ9qqtq68onpTPEYmyxSQNiH7UD/98AUcQ12kBoxz/0s=',// base 64 encoded, should be 88 chars'authToken' =>'CxVX6QsVToEGEcjfYPqXQw==',// base 64 encoded, should be 24 chars ]),'payload' =>'hello !', ], [// old Chrome PushSubscription format'subscription' => Subscription::create(['endpoint' =>'https://fcm.googleapis.com/fcm/send/abcdef...', ]),'payload' =>null, ], [// old PushSubscription format'subscription' => Subscription::create(['endpoint' =>'https://example.com/other/endpoint/of/another/vendor/abcdef...','publicKey' =>'(stringOf88Chars)','authToken' =>'(stringOf24Chars)','contentEncoding' =>'aesgcm',// one of PushManager.supportedContentEncodings ]),'payload' =>'{"message":"test"}', ]];$webPush =newWebPush();// send multiple notifications with payloadforeach ($notificationsas$notification) {$webPush->queueNotification($notification['subscription'],$notification['payload']// optional (defaults null) );}/** * Check sent results * @var MessageSentReport $report */foreach ($webPush->flush()as$report) {$endpoint =$report->getRequest()->getUri()->__toString();if ($report->isSuccess()) {echo"[v] Message sent successfully for subscription{$endpoint}."; }else {echo"[x] Message failed to sent for subscription{$endpoint}:{$report->getReason()}"; }}/** * send one notification and flush directly * @var MessageSentReport $report */$report =$webPush->sendOneNotification($notifications[0]['subscription'],$notifications[0]['payload'],// optional (defaults null));
Browsers need to verify your identity. A standard called VAPID can authenticate you for all browsers. You'll need to create and provide a public and private key for your server. These keys must be safely stored and should not change.
You can specify your authentication details when instantiating WebPush. The keys can be passed directly (recommended), or you can load a PEM file or its content:
<?phpuseMinishlink\WebPush\WebPush;$endpoint ='https://fcm.googleapis.com/fcm/send/abcdef...';// Chrome$auth = ['VAPID' => ['subject' =>'mailto:me@website.com',// can be a mailto: or your website address'publicKey' =>'~88 chars',// (recommended) uncompressed public key P-256 encoded in Base64-URL'privateKey' =>'~44 chars',// (recommended) in fact the secret multiplier of the private key encoded in Base64-URL'pemFile' =>'path/to/pem',// if you have a PEM file and can link to it on your filesystem'pem' =>'pemFileContent',// if you have a PEM file and want to hardcode its content ],];$webPush =newWebPush($auth);$webPush->queueNotification(...);
In order to generate the uncompressed public and secret key, encoded in Base64, enter the following in your Linux bash:
$ openssl ecparam -genkey -name prime256v1 -out private_key.pem$ openssl ec -in private_key.pem -pubout -outform DER|tail -c 65|base64|tr -d'='|tr'/+''_-'>> public_key.txt$ openssl ec -in private_key.pem -outform DER|tail -c +8|head -c 32|base64|tr -d'='|tr'/+''_-'>> private_key.txt
If you can't access a Linux bash, you can print the output of thecreateVapidKeys function:
var_dump(VAPID::createVapidKeys());// store the keys afterwards
On the client-side, don't forget to subscribe with the VAPID public key as theapplicationServerKey: (urlBase64ToUint8Array sourcehere)
serviceWorkerRegistration.pushManager.subscribe({userVisibleOnly:true,applicationServerKey:urlBase64ToUint8Array(vapidPublicKey)})
VAPID headers make use of a JSON Web Token (JWT) to verify your identity. That token payload includes the protocol and hostname of the endpoint included in the subscription and an expiration timestamp (usually between 12-24h), and it's signed using your public and private key. Given that, two notifications sent to the same push service will use the same token, so you can reuse them for the same flush session to boost performance using:
$webPush->setReuseVAPIDHeaders(true);
Each notification can have a specific Time To Live, urgency, and topic.The WebPush standard states thaturgency is optional but some users reports that Safari throws errors when it is not specified. This might be fixed in the future.You can change the default options withsetDefaultOptions() or in the constructor:
<?phpuseMinishlink\WebPush\WebPush;$defaultOptions = ['TTL' =>300,// defaults to 4 weeks'urgency' =>'normal',// protocol defaults to "normal". (very-low, low, normal, or high)'topic' =>'newEvent',// not defined by default. Max. 32 characters from the URL or filename-safe Base64 characters sets'batchSize' =>200,// defaults to 1000];// for every notification$webPush =newWebPush([],$defaultOptions);$webPush->setDefaultOptions($defaultOptions);// or for one notification$webPush->sendOneNotification($subscription,$payload, ['TTL' =>5000]);
Time To Live (TTL, in seconds) is how long a push message is retained by the push service (eg. Mozilla) in case the user browseris not yet accessible (eg. is not connected). You may want to use a very long time for important notifications. The default TTL is 4 weeks.However, if you send multiple nonessential notifications, set a TTL of 0: the push notification will be delivered onlyif the user is currently connected. For other cases, you should use a minimum of one day if your users have multiple timezones, and if they don't several hours will suffice.
Urgency can be either "very-low", "low", "normal", or "high". If the browser vendor has implemented this feature, it will save battery life on mobile devices (cf.protocol).
This string will make the vendor show to the user only the last notification of this topic (cf.protocol).
If you send tens of thousands notifications at a time, you may get memory overflows due to how endpoints are called in Guzzle.In order to fix this, WebPush sends notifications in batches. The default size is 1000. Depending on your server configuration (memory), you may wantto decrease this number. Do this while instantiating WebPush or callingsetDefaultOptions. Or, if you want to customize this for a specific flush, giveit as a parameter :$webPush->flush($batchSize).
You can see what the browser vendor's server sends back in case it encountered an error (push subscription expiration, wrong parameters...).
sendOneNotification()returns aMessageSentReportflush()returns a\GeneratorwithMessageSentReportobjects. To loop through the results, just pass it intoforeach. You can also useiterator_to_arrayto check the contents while debugging.
<?php/** @var \Minishlink\WebPush\MessageSentReport $report */foreach ($webPush->flush()as$report) {$endpoint =$report->getEndpoint();if ($report->isSuccess()) {echo"[v] Message sent successfully for subscription{$endpoint}."; }else {echo"[x] Message failed to sent for subscription{$endpoint}:{$report->getReason()}";// also available (to get more info)/** @var \Psr\Http\Message\RequestInterface $requestToPushService */$requestToPushService =$report->getRequest();/** @var \Psr\Http\Message\ResponseInterface $responseOfPushService */$responseOfPushService =$report->getResponse();/** @var string $failReason */$failReason =$report->getReason();/** @var bool $isTheEndpointWrongOrExpired */$isTheEndpointWrongOrExpired =$report->isSubscriptionExpired(); }}
PLEASE NOTE: You can only iterateonce over the\Generator object.
Firefox errors are listed in theautopush documentation.
Payloads are encrypted by the library. The maximum payload length is theoretically 4078 bytes (or ASCII characters).Forcompatibility reasons (archived) though, your payload should be less than 3052 bytes long.
The library pads the payload by default. This is more secure but it decreases performance for both your server and your user's device.
When you encrypt a string of a certain length, the resulting string will always have the same length,no matter how many times you encrypt the initial string. This can make attackers guess the content of the payload.In order to circumvent this, this library adds some null padding to the initial payload, so that all the input of the encryption processwill have the same length. This way, all the output of the encryption process will also have the same length and attackers won't be able toguess the content of your payload.
Encrypting more bytes takes more runtime on your server, and also slows down the user's device with decryption. Moreover, sending and receiving the packet will take more time.It's also not very friendly with users who have limited data plans.
You can customize automatic padding in order to better fit your needs.
Here are some ideas of settings:
- (default)
Encryption::MAX_COMPATIBILITY_PAYLOAD_LENGTH(2820 bytes) for compatibility purposes with Firefox for Android (See#108) Encryption::MAX_PAYLOAD_LENGTH(4078 bytes) for maximum securityfalsefor maximum performance- If you know your payloads will not exceed
Xbytes, then set it toXfor the best balance between security and performance.
<?phpuseMinishlink\WebPush\WebPush;$webPush =newWebPush();$webPush->setAutomaticPadding(false);// disable automatic padding$webPush->setAutomaticPadding(512);// enable automatic padding to 512 bytes (you should make sure that your payload is less than 512 bytes, or else an attacker could guess the content)$webPush->setAutomaticPadding(true);// enable automatic padding to default maximum compatibility length
WebPush usesGuzzle. It will use the most appropriate client it finds,and most of the time it will beMultiCurl, which allows to send multiple notifications in parallel.
You can customize the default request options and timeout when instantiating WebPush:
<?phpuseMinishlink\WebPush\WebPush;$timeout =20;// seconds$clientOptions = [ \GuzzleHttp\RequestOptions::ALLOW_REDIRECTS =>false,];// see \GuzzleHttp\RequestOptions$webPush =newWebPush([], [],$timeout,$clientOptions);
The following are available:
- Symfony:
- MinishlinkWebPushBundle
- bentools/webpush-bundle (associate your Symfony users to WebPush subscriptions)
- Laravel:laravel-notification-channels/webpush
- WordPress plugin:Perfecty Push Notifications
Feel free to add your own!
Payload is encrypted according to theMessage Encryption for Web Push standard,using the user public key and authentication secret that you can get by following theWeb Push API specification.
Internally, WebPush uses theWebToken framework and OpenSSL to handle encryption keys generation and encryption.
Here are some ideas:
- Make sure MultiCurl is available on your server
- Find the right balance for your needs between security and performance (see above)
- Find the right batch size (set it in
defaultOptionsor as parameter toflush()) - Use
flushPooled()instead offlush(). The former uses concurrent requests, accelerating the process and often doubling the speed of the requests.
Your installation lacks some certificates.
- Downloadcacert.pem.
- Edit your
php.ini: after[curl], typecurl.cainfo = /path/to/cacert.pem.
You can also force using a client without peer verification.
Make sure to require Composer'sautoloader.
require__DIR__ .'/path/to/vendor/autoload.php';
Make sure to have database fields that are large enough for the length of the data you are storing (#233). For the endpoint, users have reported that the URL length does not exceed 500 characters, but this can evolve so you can set it to the 2048 characters limit of most browsers.
Seeissue #58.
This service does not exist anymore.It has been superseded by Google's Firebase Cloud Messaging (FCM) on May 29, 2019.
This library does not support Firebase Cloud Messaging (FCM).Old Chrome subscriptions (prior 2018 and VAPID) do use Legacy HTTP protocol by Firebase Cloud Messaging (FCM) which isdeprecated since 2023 and will stop working in June 2024.The support for this outdated subscription is removed.
Please do not be confused as Legacy HTTP protocol and Web Push with VAPID use the identical endpoint URL:
Web Push with VAPID will remain available at this URL.No further action is currently required.
The browser vendors do not allow to send data using the Push API without creating a notification.Use some alternative APIs like WebSocket/WebTransport or Background Synchronization.
WebPush is for web apps.You need something likeRMSPushNotificationsBundle (Symfony).
This library was inspired by the Node.jsweb-push-libs/web-push library.
- Google'sintroduction to push notifications
- MozillaPush API (browser side)
- AppleSafari
- (Archive) Matthew Gaunt'sWeb Push Book
- (Archive) Mozilla'sServiceWorker Cookbooks (don't mind the
server.jsfile: it should be replaced by your PHP server code with this library)
- Generic Event Delivery Using HTTP PushRFC8030
- Message Encryption for Web PushRFC8291
- Voluntary Application Server Identification (VAPID) for Web PushRFC8292
- Working DraftPush API
About
Web Push library for PHP
Topics
Resources
License
Contributing
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Packages0
Uh oh!
There was an error while loading.Please reload this page.