Rocky Sena
Rocky Sena

Reputation: 481

Web Push Notification: How to use Web Push PHP Library?

I Want to add Web Notification to my website. I searched on Google and found some tutorials about it. As described in these tutorials I manage to show subscription box to the visitors and I can store their data also.

Main.js

'use strict';

const applicationServerPublicKey = 'BBw_opB12mBhg66Dc94m7pOlTTHb5oqFAafbhN-BNeazWk8woAcSeHdgbmQaroCYssUkqFfoHqEJyCKw';

const pushButton = document.querySelector('.js-push-btn');

let isSubscribed = false;
let swRegistration = null;

function urlB64ToUint8Array(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;
}



if ('serviceWorker' in navigator && 'PushManager' in window) {
  console.log('Service Worker and Push is supported');

  navigator.serviceWorker.register('sw.js')
  .then(function(swReg) {
    console.log('Service Worker is registered', swReg);

    swRegistration = swReg;
  })
  .catch(function(error) {
    console.error('Service Worker Error', error);
  });
} else {
  console.warn('Push messaging is not supported');
  pushButton.textContent = 'Push Not Supported';
}


function initialiseUI() {
  // Set the initial subscription value
  swRegistration.pushManager.getSubscription()
  .then(function(subscription) {
    isSubscribed = !(subscription === null);

    if (isSubscribed) {
      console.log('User IS subscribed.');
    } else {
      console.log('User is NOT subscribed.');
    }

    updateBtn();
  });
}

function updateBtn() {
  if (isSubscribed) {
    pushButton.textContent = 'Disable Push Messaging';
  } else {
    pushButton.textContent = 'Enable Push Messaging';
  }

  pushButton.disabled = false;
}

sw.js

'use strict';

self.addEventListener('push', function(event) {
  console.log('[Service Worker] Push Received.');
  console.log(`[Service Worker] Push had this data: "${event.data.text()}"`);

  const title = 'Motoroids Lab';
  const options = { 
    body: 'Motoroids',
    icon: 'images/icon.png',
    badge: 'images/badge.png'
  };

  event.waitUntil(self.registration.showNotification(title, options));
});


self.addEventListener('notificationclick', function(event) {
  console.log('[Service Worker] Notification click Received.');

  event.notification.close();

  event.waitUntil(
    clients.openWindow('https://developers.google.com/web/')
  );
});

But now I'm stuck. No matter how much I am trying, It's hard to understand how to send push messages from my server :(

I enabled SSL on my server. I installed PHP Web Push library and its dependencies on the server using composer require minishlink/web-push command.

But what's next? I can't understand their documentation also. https://github.com/web-push-libs/web-push-php

I need some help here. Please help me to understand how it works and how to it.

Thank You

Upvotes: 13

Views: 42118

Answers (3)

Ikenitenine
Ikenitenine

Reputation: 41

  • Install library 'composer require minishlink/web-push'
  • You should generate VAPID keys.
  • Than replace "VAPID PUBLIC KEY" and "VAPID PRIVATE KEY" inside php script. Replace:
    • 'BFrp-TvkuqCeNsytRt...'
    • '9BvI1aN1CR4w4iceMS...'
  • Than replace "VAPID PUBLIC KEY" inside js script. Replace: 'BFrp-TvkuqCeNsytRt...'
  • Than upload files ('service-worker.js', 'index.html', 'send.php') to web site
  • Open 'index.html' in browser and allow push notification. After that you will see 'endpoint', 'p256dh', 'auth' in console.
  • Set these params ('endpoint', 'p256dh', 'auth') to php script ('send.php'). Replace:
    • 'https://fcm.googleapis.com/fcm/send/djRg_IDPtSs:APA91bFwYCC73F4X3cXELK...'
    • 'SPB_NNfRw...'
    • 'BP-WMuJdP7buopSb_HrNX...'
  • Open 'send.php' in browser. And push will be sent.

This code is working in Chrome and Mozilla.

generate.php - to generate VAPID keys:

<?php

error_reporting(E_ALL);
ini_set("display_errors", 1);
header('Content-Type: text/html; charset=utf-8');

require __DIR__ . '/vendor/autoload.php';

use Minishlink\WebPush\VAPID;

print_r(VAPID::createVapidKeys());

service-worker.js

'use strict';

/**
 * Received push
 */
self.addEventListener('push', function (event) {
    let pushMessageJSON = event.data.json();
    self.registration.showNotification(pushMessageJSON.title, {
        body: pushMessageJSON.body,
        icon: pushMessageJSON.icon,
        vibrate: pushMessageJSON.data.vibrate,
        data: {
            additionalData: pushMessageJSON.data.additionalData,
            url: pushMessageJSON.data.url,
        },
    });

    console.info("**** Recv'd a push message::", event);
});

/**
 * Click by push
 */
self.addEventListener('notificationclick', function(event) {
    let url = event.notification.data.url;
    event.notification.close(); // Android needs explicit close.
    if (!url) return;
    event.waitUntil(
        clients.matchAll({type: 'window'}).then( windowClients => {
            // Check if there is already a window/tab open with the target URL
            for (var i = 0; i < windowClients.length; i++) {
                var client = windowClients[i];
                // If so, just focus it.
                if (client.url === url && 'focus' in client) {
                    return client.focus();
                }
            }
            // If not, then open the target URL in a new window/tab.
            if (clients.openWindow) {
                return clients.openWindow(url);
            }
        })
    );
});

self.addEventListener('message', function (event) {
    // A message has been sent to this service worker.
    console.log("sw Handling message event:", event);
});

self.addEventListener('pushsubscriptionchange', function (event) {
    // The Push subscription ID has changed. The App should send this
    // information back to the App Server.
    console.log("sw Push Subscription Change", event);
    event.waitUntil(
        self.clients.matchAll()
            .then(clientList => {
                let sent = false;
                console.debug("Service worker found clients",
                    JSON.stringify(clients));
                clientList.forEach(client => {
                    console.debug("Service worker sending to client...", client);
                    sent = true;
                    client.postMessage({'type': 'update'});
                });
                if (sent == false) {
                    throw new Error("No valid client to send to.");
                }
            })
            .catch(err => {
                console.error("Service worker couldn't send message: ", err);
            })
    );

});

self.addEventListener('registration', function (event) {
    // The service worker has been registered.
    console.log("sw Registration: ", event);
});


self.addEventListener('install', function (event) {
    // The serivce worker has been loaded and installed.
    // The browser aggressively caches the service worker code.
    console.log("sw Install: ", JSON.stringify(event));
    // This replaces currently active service workers with this one
    // making this service worker a singleton.
    event.waitUntil(self.skipWaiting());
    console.log("sw Installed: ", JSON.stringify(event));

});

self.addEventListener('activate', function (event) {
    // The service worker is now Active and functioning.
    console.log("sw Activate : ", JSON.stringify(event));
    // Again, ensure that this is the only active service worker for this
    // page.
    event.waitUntil(self.clients.claim());
    console.log("sw Activated: ", JSON.stringify(event));
    navigator.serviceWorker
});

index.html to request push notification

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1"/>
</head>
<body>

<script>
    if (!Notification) {
        console.log('*Browser does not support Web Notification');
    }

    if ('serviceWorker' in navigator) {
        navigator.serviceWorker
            .register('service-worker.js?v=1', {scope: './'})
            .then(function (registration) {
                console.log("Service Worker Registered");
            })
            .catch(function (err) {
                console.log("Service Worker Failed to Register", err);
            })
    }

    navigator.serviceWorker.ready.then((reg) => {
        const subscribeOptions = {
            userVisibleOnly: true,
            applicationServerKey: 'BFrp-TvkuqCeNsytRt...'
        };
        reg.pushManager.subscribe(subscribeOptions).then((subscription) => {
            //send endpoint, p256dh and auth to backend
            console.log('endpoint is: ' + subscription.endpoint);
            console.log('p256dh is: ' + subscription.toJSON().keys.p256dh);
            console.log('auth is: ' + subscription.toJSON().keys.auth);

            document.write('<p>endpoint is: ' + subscription.endpoint + '</p>');
            document.write('<p>p256dh is: ' + subscription.toJSON().keys.p256dh + '</p>');
            document.write('<p>auth is: ' + subscription.toJSON().keys.auth + '</p>');
        });
    });
</script>
</body>
</html>

send.php to send push notification

<?php

error_reporting(E_ALL);
ini_set("display_errors", 1);
header('Content-Type: text/html; charset=utf-8');

require __DIR__ . '/vendor/autoload.php';

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

$push = [
    'subscription' => Subscription::create([
        'endpoint' => 'https://fcm.googleapis.com/fcm/send/djRg_IDPtSs:APA91bFwYCC73F4X3cXELK...',
        'keys' => [
            'auth' => 'SPB_NNfRw...',
            'p256dh' => 'BP-WMuJdP7buopSb_HrNX...'
        ]
    ]),
    'payload' => json_encode([
        'title' => "Hello",
        'body' => "How are you?",
        'icon' => "https://cdn-icons-png.flaticon.com/512/3884/3884851.png",
        'data' => [
            'vibrate' => [100, 200],
            'additionalData' => [],
            'url' => "https://google.com",
        ],
    ]),
];

$auth = [
    'VAPID' => [
        'subject' => '[email protected]', // can be a mailto: or your website address
        'publicKey' => 'BFrp-TvkuqCeNsytRt...', // (recommended) uncompressed public key P-256 encoded in Base64-URL
        'privateKey' => '9BvI1aN1CR4w4iceMS...', // (recommended) in fact the secret multiplier of the private key encoded in Base64-URL
    ],
];

$webPush = new WebPush($auth);

try {
    $webPush->queueNotification(
        $push['subscription'],
        $push['payload']
    );
    $report = $webPush->flush()->current();
    $is_success = $report->isSuccess();
    $response = $report->getResponseContent();
} catch (\Throwable $th) {
    $is_success = false;
    $response = $th->getMessage();
}

if ($is_success) {
    echo "Push was sent";
} else {
    echo "Push was not sent. Error message: " . $response;
}

Upvotes: 1

Marco Abarca
Marco Abarca

Reputation: 1

you have to add this code to your project https://developers.google.com/web/fundamentals/getting-started/codelabs/push-notifications/ sometimes you will have to add code to parts that you have already created

Upvotes: -1

Martijn
Martijn

Reputation: 5621

Take a look at https://web-push-book.gauntface.com/ for a general introduction to Web Push. The Chapter How Push Works and Subscribing a User should be particularly interesting to you. In summary:

  • At the client side you need to create a subscription by calling pushManager.subscribe. More info here.
  • You should send this subscription to your server. For example, make an AJAX request to send the subscription (endpoint, keys.p256dh, keys.auth) to the server. More info here.
  • On the server you send a push message using the PHP Web Push library. First, you need to configure this library with your keypair. Then, you need to use the subscription (endpoint, keys.p256dh, keys.auth) to send a push message. More info here.

Upvotes: 14

Related Questions