Notifications push du navigateur en Javascript et PHP

Avant-propos

Pour tenter de trouver un bon article sur la configuration des notifications dans le navigateur, je n'ai reçu que des articles décrivant principalement l'utilisation en conjonction avec Firebase, mais cette option ne me convenait pas particulièrement.





Dans cet article, les principes de fonctionnement et les subtilités des notifications Push ne seront pas « brouillés », uniquement le code, uniquement le hardcore.





Notes IMPORTANTES





Les notifications push ne fonctionnent qu'avec HTTPS .

D'ailleurs, en plus du HTTPS, un certificat SSL valide doit être présent, Let's Encrypt fera l'affaire.





Localhost est parfait pour le développement. Il ne devrait pas y avoir de problèmes, mais si cela se produit, cet article vous aidera à les résoudre.





Qu'il y ait du code

Autorisation (VAPID)

Tout d'abord, cela vaut la peine d'installer la bibliothèque WebPush dans votre projet php :





$ composer require minishlink/web-push
      
      



Ensuite, pour autoriser votre serveur avec un navigateur (VAPID), vous devez générer des clés ssh publiques et privées. Ces clés seront nécessaires à la fois sur le serveur et sur le client (sauf que seule la publique est nécessaire sur le client) .





Pour générer une clé publique et privée codée en Base64 non compressée, entrez ce qui suit dans votre bash Linux :





$ 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
      
      



De plus, l'auteur de la bibliothèque fournit la génération de clés vapid en utilisant la méthode intégrée :





$vapidKeysInBase64 = VAPID::createVapidKeys();
      
      



Abonnement

Étape 1 (JS)

ServiceWorker, PushManager, showNotification :





app.js





function checkNotificationSupported() {
	return new Promise((fulfilled, reject) => {
  	if (!('serviceWorker' in navigator)) {
      reject(new Error('Service workers are not supported by this browser'));
      return;
    }

    if (!('PushManager' in window)) {
      reject(new Error('Push notifications are not supported by this browser'));
      return;
    }

    if (!('showNotification' in ServiceWorkerRegistration.prototype)) {
      reject(new Error('Notifications are not supported by this browser'));
    	return;
    }
    
    fulfilled();
  })
}
      
      



sw.js :





app.js





navigator.serviceWorker.register('sw.js').then(() => {
      console.log('[SW] Service worker has been registered');
    }, e => {
      console.error('[SW] Service worker registration failed', e);
    }
  );
      
      



:





app.js





function checkNotificationPermission() {
    return new Promise((fulfilled, reject) => {
        if (Notification.permission === 'denied') {
            return reject(new Error('Push messages are blocked.'));
        }
        if (Notification.permission === 'granted') {
            return fulfilled();
        }
        if (Notification.permission === 'default') {
            return Notification.requestPermission().then(result => {
                if (result !== 'granted') {
                    reject(new Error('Bad permission result'));
                } else {
                    fulfilled();
                }
            });
        }
        return reject(new Error('Unknown permission'));
    });
}
      
      



ssh :





<script>
	window.applicationServerKey = '<?= $yourPublicKeyFromServer ?>'
</script>
      
      



, . 10 .





app.js





document.addEventListener('DOMContentLoaded', documentLoadHandler);


function documentLoadHandler() {
    checkNotificationSupported()
        .then(() => {
          setTimeout(() => {
            serviceWorkerRegistration.pushManager.subscribe({
                    userVisibleOnly: true,
                    applicationServerKey: urlBase64ToUint8Array(window.applicationServerKey),
                })
                .then(successSubscriptionHandler, errorSubscriptionHandler)
          }, 10000);
         }, 
        	console.error
      	);
}


function urlBase64ToUint8Array(base64String) {
    const padding = '='.repeat((4 - (base64String.length % 4)) % 4);
    const base64 = (base64String + padding).replace(/\-/g, '+').replace(/_/g, '/');
    const rawData = window.atob(base64);
    const outputArray = new Uint8Array(rawData.length);
    for (let i = 0; i < rawData.length; ++i) {
        outputArray[i] = rawData.charCodeAt(i);
    }
    return outputArray;
}

function errorSubscriptionHandler(err) {
    if (Notification.permission === 'denied') {
        console.warn('Notifications are denied by the user.');
    } else {
        console.error('Impossible to subscribe to push notifications', err);
    }
}
      
      



successSubscriptionHandler





.





app.js





function successSubscriptionHandler(subscriptionData) {
    const key = subscription.getKey('p256dh');
    const token = subscription.getKey('auth');
    const contentEncoding = (PushManager.supportedContentEncodings || ['aesgcm'])[0];
    const body = new FormData();

    body.set('endpoint', subscription.endpoint);
    body.set('publicKey', key ? btoa(String.fromCharCode.apply(null, new Uint8Array(key))) : null);
    body.set('authToken', token ? btoa(String.fromCharCode.apply(null, new Uint8Array(token))) : null);
    body.set('contentEncoding', contentEncoding);

    return fetch('src/push_subscription.php', {
      method,
      body,
    }).then(() => subscription);
  }
      
      







Post Message API





self.addEventListener('push', function (event) {
    if (!(self.Notification && self.Notification.permission === 'granted')) {
        return;
    }

    const sendNotification = body => {
        const title = " ";

        return self.registration.showNotification(title, {
            body,
        });
    };

    if (event.data) {
        const message = event.data.text();
        event.waitUntil(sendNotification(message));
    }
});
      
      



2 (PHP)

php 7+





subscribeUserToPushNotifications ,





subscribeUserToPushNotifications.php





<?php 

$subscription = $_POST;
if (!isset($subscription['endpoint'])) {
    echo 'Error: not a subscription';
    return;
}

// save subscription from => $subscription 
      
      



( ), .









, :





pushNotificationToClient.php





<?php 

use Minishlink\WebPush\WebPush;
use Minishlink\WebPush\Subscription;

$subscription = Subscription::create($subscriptionData);
      
      



VAPID :





pushNotificationToClient.php





<?php 

$auth = array(
    'VAPID' => array(
        'subject' => 'https://your-project-domain.com',
        'publicKey' => file_get_contents(__DIR__ . '/your_project/keys/public_key.txt'),
        'privateKey' => file_get_contents(__DIR__ . '/your_project/keys/private_key.txt'), 
    )
);
      
      



, WebPush:





pushNotificationToClient.php





<?php

$webPush = new WebPush($auth);
      
      



! Push





<?php

$report = $webPush->sendOneNotification(
  $subscription,
  "  ,     sw.js"
);
      
      



Note importante





Pour envoyer des notifications par itération, vous devez utiliser une fonction avec les mêmes paramètres que dans la fonction ci-dessus :





$webPush->queueNotification







Sources utiles

  1. À propos de la technologie push





  2. À propos de WebPush de Khabrovchanin





  3. Bibliothèque WebPush





  4. Un exemple d'utilisation d'un développeur de bibliothèque








All Articles