Web推送通知:如何使用Web推送PHP库?

13
我想在我的网站上添加Web通知。我在Google上搜索并找到了一些关于它的教程。根据这些教程,我成功地向访问者展示了订阅框,并且我也可以存储他们的数据。

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/')
  );
});

但是现在我卡住了。无论我如何努力,都很难理解如何从我的服务器发送推送消息:(

我在服务器上启用了SSL。我使用composer require minishlink/web-push命令在服务器上安装了PHP Web Push库及其依赖项。

但接下来呢?我也看不懂他们的文档。https://github.com/web-push-libs/web-push-php

我需要一些帮助。请帮助我理解它是如何工作的以及该如何做。

谢谢你


嗨...你解决了这个问题吗?我也遇到了同样的问题。无法从服务器发送通知。请分享你的解决方案,以便我可以检查。 - Dhirender
3个回答

13
请查看 https://web-push-book.gauntface.com/ 了解 Web Push 的一般介绍,其中第1章如何推送工作和第2章订阅用户对您应该特别感兴趣。总的来说:
  • 在客户端,您需要通过调用pushManager.subscribe来创建订阅。更多信息请点击此处
  • 您应该将此订阅发送到服务器。例如,通过AJAX请求将订阅(终端点,keys.p256dh,keys.auth)发送到服务器。更多信息请点击此处
  • 在服务器上,您使用 PHP Web Push 库发送推送消息。首先,您需要使用密钥对配置此库。然后,您需要使用订阅(endpoint,keys.p256dh,keys.auth)来发送推送消息。更多信息请点击此处

0
  • 安装库 'composer require minishlink/web-push'
  • 您应该生成VAPID密钥。
  • 然后在php脚本中替换“VAPID PUBLIC KEY”和“VAPID PRIVATE KEY”。替换:
    • 'BFrp-TvkuqCeNsytRt...'
    • '9BvI1aN1CR4w4iceMS...'
  • 然后在js脚本中替换“VAPID PUBLIC KEY”。替换:'BFrp-TvkuqCeNsytRt...'
  • 然后将文件('service-worker.js','index.html','send.php')上传到网站
  • 在浏览器中打开'index.html'并允许推送通知。之后您将在控制台中看到'endpoint'、'p256dh'和'auth'。
  • 将这些参数('endpoint'、'p256dh'、'auth')设置为php脚本('send.php')。替换:
    • 'https://fcm.googleapis.com/fcm/send/djRg_IDPtSs:APA91bFwYCC73F4X3cXELK...'
    • 'SPB_NNfRw...'
    • 'BP-WMuJdP7buopSb_HrNX...'
  • 在浏览器中打开'send.php'。推送将被发送。

这段代码在Chrome和Mozilla中运行正常。

generate.php - 用于生成VAPID密钥:

<?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用于请求推送通知

<!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 用于发送推送通知

<?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' => 'support@gmail.com', // 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;
}

-1

3
欢迎来到“开发者学习、分享和构建职业生涯的地方”!虽然这理论上回答了问题,但最好在此处包含答案的核心部分,并提供参考链接。 - moggi
https://developers.google.com/web/fundamentals/getting-started/codelabs/push-notifications/ 的中文版本是您的链接。 - moggi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接