JavaScript および PHP でのブラウザ プッシュ通知

序文

ブラウザでの通知の設定に関する良い記事を見つけようとしたところ、Firebase との併用を中心に説明している記事しかありませんでしたが、このオプションは私には特に適していませんでした。





この記事では、プッシュ通知の操作の原則と微妙な点は「ごちゃごちゃ」ではなく、コードのみ、ハードコアのみを扱います。





重要事項





プッシュ通知はHTTPS でのみ機能します

ところで、HTTPS に加えて、有効な SSL 証明書が存在する必要があります。Let's Encrypt が実行します。





Localhost は開発に適しています。問題はありませんが、問題が発生した場合は、この記事がそれらに対処するのに役立ちます。





コードがありますように

許可 (VAPID)

まず、WebPush ライブラリを php プロジェクトにインストールする価値があります。





$ composer require minishlink/web-push
      
      



次に、ブラウザ (VAPID) を使用してサーバーを認証するには、公開および秘密の ssh キー生成する必要があります。これらのキーはサーバーとクライアントの両方で必要になります(ただし、クライアントでは公開キーのみが必要です)





圧縮されていない Base64 でエンコードされた公開鍵と秘密鍵を生成するには、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
      
      



また、ライブラリの作成者は、組み込みメソッドを使用して vapid キーの生成を提供します。





$vapidKeysInBase64 = VAPID::createVapidKeys();
      
      



購読

ステージ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"
);
      
      



重要な注意点





繰り返しで通知を送信するには、上記の関数と同じパラメーターを持つ関数を使用する必要があります。





$webPush->queueNotification







役立つ情報源

  1. プッシュテクノロジーについて





  2. Khabrovchanin の WebPush について





  3. WebPush ライブラリ





  4. ライブラリ開発者の使用例








All Articles