Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Web Notifications made easy
<?php
use WebPush\Payload\AES128GCM;
use WebPush\Payload\AESGCM;
use WebPush\Payload\PayloadExtension;
$payloadExtension = PayloadExtension::create()
->addContentEncoding(AESGCM::create()->maxPadding())
->addContentEncoding(AES128GCM::create()->maxPadding())
;<?php
use WebPush\Payload\AES128GCM;
use WebPush\Payload\AESGCM;
use WebPush\Payload\PayloadExtension;
$aesgcm = new AESGCM();
$aesgcm->maxPadding();
$aes128gcm = new AES128GCM();
$aes128gcm->maxPadding();
$payloadExtension = new PayloadExtension();
$payloadExtension->addContentEncoding($aesgcm);
$payloadExtension->addContentEncoding($aes128gcm);The MIT License (MIT)
<?php
use WebPush\Subscription;
use WebPush\Notification;
use WebPush\WebPushService;
/** @var Notification $notification */
/** @var Subscription $subscription */
/** @var WebPushService $webPushService */
$statusReport = $webPushService->send($notification, $subscription);
if(!$statusReport->isSuccess()) {
//Something went wrong
} else {
$statusReport->getLocation();
$statusReport->getLinks();
}<?php
if($statusReport->isSubscriptionExpired()) {
$this->subscriptionRepository->remove($subscription);
}Voluntary Application Server Identification
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<?php
use WebPush\Notification;
$notification = Notification::create();<?php
use WebPush\Notification;
$notification = Notification::create()
->withPayload('Hello world')
;<?php
use WebPush\Notification;
$notification = Notification::create()
->withTTL(3600)
;<?php
use WebPush\Notification;
$notification = Notification::create()
->withTopic('user-account-updated')
;<?php
use WebPush\Notification;
$notification = Notification::create()
->veryLowUrgency()
->lowUrgency()
->normalUrgency()
->highUrgency()
;<?php
use WebPush\Notification;
$notification = Notification::create()
->async() // Prefer async response
->sync() // Prefer sync response (default)
;<?php
use WebPush\Action;
use WebPush\Message;
use WebPush\Notification;
$message = Message::create('This is the title', null, true)
->mute() // Silent
->unmute() // Not silent (default)
->auto() //Direction = auto (default)
->ltr() //Direction = left to right
->rtl() //Direction = right to left
->addAction(Action::create('alert', 'Click me!'))
->interactionRequired()
->noInteraction()
->renotify()
->doNotRenotify() // Default
->withBody('Hello World!')
->withIcon('https://…')
->withImage('https://…')
->withData(['foo' => 'BAR']) // Arbitrary data
->withBadge('badge1')
->withLang('fr-FR')
->withTimestamp(time())
->withTag('foo')
->vibrate(300, 100, 400)
->toString() // Converts the Message object into a string
;
$notification = Notification::create()
->withPayload($message)
;{
"title":"This is the title",
"options":{
"actions":[
{
"action":"alert",
"title":"Click me!"
}
],
"badge":"badge1",
"body":"Hello World!",
"data":{
"foo":"BAR"
},
"dir":"rtl",
"icon":"https://…",
"image":"https://…",
"lang":"fr-FR",
"renotify":false,
"requireInteraction":false,
"silent":false,
"tag":"foo",
"timestamp":1629145424,
"vibrate":[
300,
100,
400
]
}
} const {title, options} = payload;
const notification = new Notification(title, options);{
"endpoint":"https://updates.push.services.mozilla.com/wpush/v2/AAAAAAAA[…]AAAAAAAAA",
"keys":{
"auth":"XXXXXXXXXXXXXX",
"p256dh":"YYYYYYYY[…]YYYYYYYYYYYYY"
}
}<?php
use WebPush\Subscription;
$subscription = Subscription::createFromString('{"endpoint":"https://updates.push.services.mozilla.com/wpush/v2/AAAAAAAA[…]AAAAAAAAA","keys":{"auth":"XXXXXXXXXXXXXX","p256dh":"YYYYYYYY[…]YYYYYYYYYYYYY"}}');// Retreive the supported content encodings
const supportedContentEncodings = PushManager.supportedContentEncodings || ['aesgcm'];
// Assign the encodings to the subscription object
const jsonSubscription = Object.assign(
subscription.toJSON(),
{ supportedContentEncodings }
);
// Send the subscription object to the application server
fetch('/subscription/add', {
method: 'POST',
body: JSON.stringify(jsonSubscription),
});{
"endpoint":"https://updates.push.services.mozilla.com/wpush/v2/AAAAAAAA[…]AAAAAAAAA",
"keys":{
"auth":"XXXXXXXXXXXXXX",
"p256dh":"YYYYYYYY[…]YYYYYYYYYYYYY",
"supportedContentEncodings":["aes128gcm","aesgcm"]
}
}webpush:
vapid:
enabled: true # Enable the feature
subject: 'https://my-service.com:8000' # An URL or an email address
web_token:
enabled: true # We use web-token in this example
public_key: 'BB4W1qfBi7MF_Lnrc6i2oL-glAuKF4kevy9T0k2vyKV4qvuBrN3T6o9-7-NR3mKHwzDXzD3fe7XvIqIU1iADpGQ'
private_key: 'C40jLFSa5UWxstkFvdwzT3eHONE2FIJSEsVIncSCAqU'webpush:
vapid:
enabled: true
subject: 'https://my-service.com:8000'
lcobucci:
enabled: true # We use web-token in this example
public_key: 'BB4W1qfBi7MF_Lnrc6i2oL-glAuKF4kevy9T0k2vyKV4qvuBrN3T6o9-7-NR3mKHwzDXzD3fe7XvIqIU1iADpGQ'
private_key: 'C40jLFSa5UWxstkFvdwzT3eHONE2FIJSEsVIncSCAqU'webpush:
vapid:
enabled: true
token_lifetime: 'now +2 hours'webpush:
payload:
aesgcm:
padding: 'none' # "none", "recommended", "max" or an integer (0 to 4078)
aes128gcm:
padding: 'none' # "none", "recommended", "max" or an integer (0 to 3993)webpush:
payload:
aesgcm:
cache: Psr\Cache\CacheItemPoolInterface
cache_lifetime: '+1 hour' #Default: now +30min
aes128gcm:
cache: Psr\Cache\CacheItemPoolInterface
cache_lifetime: '+1 hour' #Default: now +30minwebpush:
logger: Psr\Log\LoggerInterface<?php
declare(strict_types=1);
namespace App\MessageHandler;
use App\Message\SubscriptionExpired;
use Symfony\Component\Messenger\Handler\MessageHandlerInterface;
use Symfony\Component\Messenger\MessageBusInterface;
use WebPush\Notification;
use WebPush\WebPush;
final class SendPushNotifications implements MessageHandlerInterface
{
private MessageBusInterface $messageBus;
private SubscriptionRepository $repository;
private WebPush $webPush;
public function __construct(MessageBusInterface $messageBus, SubscriptionRepository $repository, WebPush $webPush)
{
$this->messageBus = $messageBus;
$this->repository = $repository;
$this->webPush = $webPush;
}
public function __invoke(Notification $notification): void
{
// Fetch all subscriptions
$subscriptions = $this->repository->fetchAllSubscriptions();
foreach ($subscriptions as $subscription) {
//Sends the notification to the subscriber
$report = $this->webPush->send($notification, $subscription);
//If the subscription expired
if ($report->subscriptionExpired()) {
//We dispatch a new message and expect for
// the subscription to be deleted
$this->messageBus->dispatch(
new SubscriptionExpired($subscription)
);
}
}
}
}use Symfony\Component\HttpClient\HttpClient;
use WebPush\WebPush;
$client = HttpClient::create();
$service = new WebPush($client, $extensionManager);<?php
use WebPush\Subscription;
use WebPush\Notification;
$subscription = Subscription::createFromString('{"endpoint":"https://updates.push.services.mozilla.com/wpush/v2/AAAAAAAA[…]AAAAAAAAA","keys":{"auth":"XXXXXXXXXXXXXX","p256dh":"YYYYYYYY[…]YYYYYYYYYYYYY"}}');
$notification = Notification::create()
->withPayload('Hello world')
;
$statusReport = $service->send($notification, $subscription);use WebPush\ExtensionManager;
use WebPush\PreferAsyncExtension;
use WebPush\TopicExtension;
use WebPush\TTLExtension;
use WebPush\UrgencyExtension;
$extensionManager = ExtensionManager::create()
->add(TTLExtension::create())
->add(UrgencyExtension::create())
->add(TopicExtension::create())
->add(PreferAsyncExtension::create())
;$clock = //PSR-20 clock
$payloadExtension = PayloadExtension::create()
->addContentEncoding(AESGCM::create($clock))
->addContentEncoding(AES128GCM::create($clock))
;
$extensionManager = ExtensionManager::create()
->add($payloadExtension)
;use WebPush\VAPID\WebTokenProvider;
use WebPush\VAPID\LcobucciProvider;
// Web-Token
$jwsProvider = WebTokenProvider::create(
'BB4W1qfBi7MF_Lnrc6i2oL-glAuKF4kevy9T0k2vyKV4qvuBrN3T6o9-7-NR3mKHwzDXzD3fe7XvIqIU1iADpGQ', // Public key
'C40jLFSa5UWxstkFvdwzT3eHONE2FIJSEsVIncSCAqU' // Private key
);
// lcobucci/jwt
$jwsProvider = LcobucciProvider::create(
'BB4W1qfBi7MF_Lnrc6i2oL-glAuKF4kevy9T0k2vyKV4qvuBrN3T6o9-7-NR3mKHwzDXzD3fe7XvIqIU1iADpGQ',
'C40jLFSa5UWxstkFvdwzT3eHONE2FIJSEsVIncSCAqU'
);
$extensionManager = ExtensionManager::create()
->add(VAPIDExtension::create('http://my-service.com', $jwsProvider)
);webpush:
doctrine_mapping: true<?php
declare(strict_types=1);
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use WebPush\Subscription as WebPushSubscription;
#[ORM\Table(name: 'subscriptions')]
#[ORM\Entity]
class Subscription extends WebPushSubscription
{
#[ORM\Id]
#[ORM\Column(type: 'integer')]
#[ORM\GeneratedValue(strategy: 'AUTO')]
private ?int $id = null;
#[ORM\ManyToOne(targetEntity: User::class, cascade: ['persist'], inversedBy: 'subscriptions')]
#[ORM\JoinColumn(name: 'user_id', referencedColumnName: 'id', nullable: true)]
private ?User $user;
public function getId(): ?int
{
return $this->id;
}
public function getUser(): ?User
{
return $this->user;
}
public function setUser(?User $user): self
{
$this->user = $user;
return $this;
}
// We need to override this method as it returns a WebPush\Subscription and we want an entity
public static function createFromString(string $input): self
{
$base = BaseSubscription::createFromString($input);
$object = new self($base->getEndpoint());
$object->withContentEncodings($base->getSupportedContentEncodings());
foreach ($base->getKeys()->all() as $k => $v) {
$object->getKeys()->set($k, $v);
}
return $object;
}
}<?php
declare(strict_types=1);
namespace App\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Table(name="users")
* @ORM\Entity
*/
#[ORM\Table(name: 'users')]
#[ORM\Entity]
class User //Usual interface here
{
//Usual user stuff here
#[ORM\OneToMany(targetEntity: Subscription::class, mappedBy: 'user')]
private Collection $subscriptions;
public function __construct()
{
$this->notifications = new ArrayCollection();
}
/**
* @return Notification[]
*/
public function getSubscriptions(): array
{
return $this->notifications->toArray();
}
public function addSubscription(Subscription $subscription): self
{
$subscription->setUser($this);
$this->subscriptions->add($subscription);
return $this;
}
public function removeSubscription(Subscription $subscription): self
{
$child->setUser(null);
$this->subscriptions->removeElement($subscription);
return $this;
}
}$subscriptions = $user->getSubscriptions();
foreach ($subscriptions as $subscription) {
$report = $this->webPush->send($notification, $subscription);
if ($report->isSubscriptionExpired()) {
//...Remove this subscription
}
}