{"id":1253,"date":"2024-08-14T16:47:19","date_gmt":"2024-08-14T07:47:19","guid":{"rendered":"https:\/\/shinke1987.net\/?p=1253"},"modified":"2024-08-14T16:47:19","modified_gmt":"2024-08-14T07:47:19","slug":"php%e3%81%a7web%e3%83%96%e3%83%a9%e3%82%a6%e3%82%b6%e3%81%ae%e3%83%97%e3%83%83%e3%82%b7%e3%83%a5%e9%80%9a%e7%9f%a5%e3%81%ae%e5%8b%95%e4%bd%9c%e7%a2%ba%e8%aa%8d","status":"publish","type":"post","link":"https:\/\/shinke1987.net\/?p=1253","title":{"rendered":"PHP\u3067Web\u30d6\u30e9\u30a6\u30b6\u306e\u30d7\u30c3\u30b7\u30e5\u901a\u77e5\u306e\u52d5\u4f5c\u78ba\u8a8d"},"content":{"rendered":"\n<h2 id=\"toc0\" class=\"wp-block-heading\">\u6982\u8981<\/h2>\n\n\n\n<p>minishlink\/web-push \u3092\u5229\u7528\u3057\u305fWeb\u30d6\u30e9\u30a6\u30b6\u306e\u30d7\u30c3\u30b7\u30e5\u901a\u77e5\u306e\u52d5\u4f5c\u78ba\u8a8d\u3092\u884c\u3046\u3002\uff08PC\u306e\u307f\uff09<\/p>\n\n\n\n<p>Docker\u30b3\u30f3\u30c6\u30ca\u3067\u306f\u306a\u304fPHP\u306e\u30d3\u30eb\u30c8\u30a4\u30f3\u958b\u767a\u7528\u30b5\u30fc\u30d0\u3067\u5b9f\u884c\u3059\u308b\u3002<\/p>\n\n\n\n<p>PHP\uff1a8.3.7<\/p>\n\n\n\n<p>OS\uff1aMacOS Sonoma 14.5<\/p>\n\n\n\n<p>Safari\uff1a17.5<\/p>\n\n\n\n<p>Chrome\uff1a127.0.6533.120\uff08Official Build\uff09 \uff08arm64\uff09<\/p>\n\n\n\n<p>Firefox\uff1a129.0.1 (64 \u30d3\u30c3\u30c8)<\/p>\n\n\n\n<h2 id=\"toc1\" class=\"wp-block-heading\">\u53c2\u8003\u8cc7\u6599<\/h2>\n\n\n\n<p><a href=\"https:\/\/developer.chrome.com\/docs\/workbox\/service-worker-overview?hl=ja\">Service Worker \u306e\u6982\u8981 \uff08Chrome for Developers)<\/a><\/p>\n\n\n\n<p><a href=\"https:\/\/developer.mozilla.org\/ja\/docs\/Web\/API\/ServiceWorker\">ServiceWorker &#8211; Web API | MDN<\/a><\/p>\n\n\n\n<p><a href=\"https:\/\/developer.mozilla.org\/ja\/docs\/Web\/API\/Service_Worker_API\" target=\"_blank\" rel=\"noreferrer noopener nofollow\">\u30b5\u30fc\u30d3\u30b9\u30ef\u30fc\u30ab\u30fc API &#8211; Web API | MDN<\/a><\/p>\n\n\n\n<p><a href=\"https:\/\/developer.mozilla.org\/ja\/docs\/Web\/API\/Push_API\" target=\"_blank\" rel=\"noreferrer noopener nofollow\">\u30d7\u30c3\u30b7\u30e5 API &#8211; Web API | MDN<\/a><\/p>\n\n\n\n<p><a href=\"https:\/\/github.com\/mdn\/serviceworker-cookbook\" target=\"_blank\" rel=\"noreferrer noopener nofollow\">mdn\/serviceworker-cookbook\uff08\u30d7\u30c3\u30b7\u30e5\u901a\u77e5\u306e\u4f8b\uff09<\/a><\/p>\n\n\n\n<p><a href=\"https:\/\/github.com\/web-push-libs\/web-push-php?tab=readme-ov-file\" target=\"_blank\" rel=\"noreferrer noopener nofollow\">web-push-libs\/web-push-php: Web Push library for PHP<\/a><\/p>\n\n\n\n<p><a href=\"https:\/\/zenn.dev\/nnahito\/articles\/fd2c8b0ad0d19a\" target=\"_blank\" rel=\"noreferrer noopener nofollow\">JS\u3068PHP\u3067WebPush\u3092\u9001\u4fe1\u3059\u308bWeb\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u4f5c\u3063\u3066\u307f\u308b<\/a><\/p>\n\n\n\n<p><a href=\"https:\/\/www.key-p.com\/blog\/staff\/archives\/108237\" target=\"_blank\" rel=\"noreferrer noopener nofollow\">Web Push\u3092\u8a66\u3057\u3066\u307f\u305f\uff08\u30d5\u30ed\u30f3\u30c8\u30a8\u30f3\u30c9\u7de8\uff09 | KEYPOINT<\/a><\/p>\n\n\n\n<h2 id=\"toc2\" class=\"wp-block-heading\">\u5927\u4e8b\u306a\u3053\u3068<\/h2>\n\n\n\n<p>MDN\u306b\u3088\u308b\u3068\u3001<\/p>\n\n\n\n<p>\u300c\u3059\u3079\u3066\u306e\u30d7\u30c3\u30b7\u30e5\u901a\u77e5\u306f\u6709\u76ca\u3067\u6642\u523b\u306b\u654f\u611f\u306a\u3082\u306e\u3067\u3042\u308b\u3079\u304d\u3067\u3042\u308a\u3001\u30e6\u30fc\u30b6\u30fc\u306b\u306f\u6700\u521d\u306e\u30d7\u30c3\u30b7\u30e5\u901a\u77e5\u3092\u9001\u4fe1\u3059\u308b\u524d\u306b\u5fc5\u305a\u305d\u306e\u8a31\u53ef\u3092\u6c42\u3081\u3001\u4eca\u5f8c\u30d7\u30c3\u30b7\u30e5\u901a\u77e5\u3092\u53d6\u5f97\u3057\u306a\u3044\u3088\u3046\u306b\u3059\u308b\u7c21\u5358\u306a\u65b9\u6cd5\u3092\u63d0\u4f9b\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002\u300d<\/p>\n\n\n\n<p>\u3068\u306e\u3053\u3068\u3002<\/p>\n\n\n\n<h2 id=\"toc3\" class=\"wp-block-heading\">\u899a\u3048\u66f8\u304d<\/h2>\n\n\n\n<p>ServiceWorker\u304c\u5229\u7528\u3067\u304d\u308b\u306e\u306f\u3001localhost\u4ee5\u5916\u3067\u306fhttps\u901a\u4fe1\u306e\u307f\u3002<\/p>\n\n\n\n<h2 id=\"toc4\" class=\"wp-block-heading\">\u30d7\u30c3\u30b7\u30e5\u30b5\u30fc\u30d0\u3068\u901a\u4fe1\u305b\u305aJS\u3060\u3051\u3067\u901a\u77e5\u3092\u8868\u793a\u3059\u308b<\/h2>\n\n\n\n<h3 id=\"toc5\" class=\"wp-block-heading\">\u6982\u8981<\/h3>\n\n\n\n<p>\u5b9f\u969b\u306b\u4f7f\u3046\u3053\u3068\u306f\u306a\u3044\u3060\u308d\u3046\u3051\u308c\u3069\u3082\u3001\u7406\u89e3\u3092\u6df1\u3081\u308b\u305f\u3081\u306b\u5b9f\u884c\u3057\u305f\u3002<\/p>\n\n\n\n<p>\u524d\u3082\u3063\u3066\u6307\u5b9a\u3057\u305f\u901a\u77e5\u304c\u8868\u793a\u3067\u304d\u308b\u3053\u3068\u3092\u78ba\u8a8d\u3067\u304d\u308b\u3002<\/p>\n\n\n\n<h3 id=\"toc6\" class=\"wp-block-heading\">index.html \u306e\u5185\u5bb9<\/h3>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: xml; title: ; notranslate\" title=\"\">\n&lt;!DOCTYPE html&gt;\n&lt;html lang=&quot;ja&quot;&gt;\n&lt;head&gt;\n  &lt;meta charset=&quot;UTF-8&quot;&gt;\n  &lt;title&gt;BasicTest1&lt;\/title&gt;\n  &lt;script src=&quot;app.js&quot;&gt;&lt;\/script&gt;\n&lt;\/head&gt;\n&lt;body&gt;\nBasicTest1&lt;br&gt;\n&lt;button\n  type=&quot;button&quot;\n  onclick=&quot;handleClick()&quot;\n  &gt;\n  \u901a\u77e5\u3092\u8868\u793a\n&lt;\/button&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;\n<\/pre><\/div>\n\n\n<h3 id=\"toc7\" class=\"wp-block-heading\">app.js \u306e\u5185\u5bb9<\/h3>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\nasync function handleClick (event) {\n  const result = await Notification.requestPermission();\n\n  switch (result) {\n    case &#039;default&#039;:\n      \/\/ \u901a\u77e5\u306e\u8a31\u53ef\u3092\u5f97\u308b\u30c0\u30a4\u30a2\u30ed\u30b0\u3067\u00d7\u30dc\u30bf\u30f3\u304c\u62bc\u4e0b\u3055\u308c\u3001\u8a31\u53ef\u3082\u62d2\u5426\u3082\u3055\u308c\u306a\u304b\u3063\u305f\u5834\u5408\u3002\n      \/\/ \u307e\u305f\u306f\u901a\u77e5\u306e\u8a31\u53ef\u3092\u5f97\u308b\u30c0\u30a4\u30a2\u30ed\u30b0\u304c\u8868\u793a\u3055\u308c\u308b\u524d\u3002\n      break;\n    case &#039;denied&#039;:\n      \/\/ \u901a\u77e5\u306e\u8a31\u53ef\u3092\u5f97\u308b\u30c0\u30a4\u30a2\u30ed\u30b0\u3067\u62d2\u5426\u3092\u9078\u629e\u3055\u308c\u305f\u5834\u5408\u3002\n      \/\/ \u307e\u305f\u306f\u904e\u53bb\u306b\u901a\u77e5\u306e\u8a31\u53ef\u3092\u5f97\u308b\u30c0\u30a4\u30a2\u30ed\u30b0\u3067\u62d2\u5426\u304c\u9078\u629e\u3055\u308c\u305f\u5834\u5408\u3002\n      break;\n    case &#039;granted&#039;:\n      \/\/ \u901a\u77e5\u306e\u8a31\u53ef\u3092\u5f97\u308b\u30c0\u30a4\u30a2\u30ed\u30b0\u3067\u8a31\u53ef\u304c\u9078\u629e\u3055\u308c\u305f\u5834\u5408\u3002\n      \/\/ \u307e\u305f\u306f\u904e\u53bb\u306b\u901a\u77e5\u306e\u8a31\u53ef\u3092\u5f97\u308b\u30c0\u30a4\u30a2\u30ed\u30b0\u3067\u8a31\u53ef\u304c\u9078\u629e\u3055\u308c\u305f\u5834\u5408\u3002\n      await navigator.serviceWorker.register(&#039;serviceWorker.js&#039;);\n\n      const swr = await navigator.serviceWorker.ready;\n      await swr.showNotification(&#039;title1&#039;, {\n        body: &#039;\u5185\u5bb9&#039;,\n      });\n      break;\n  }\n}\n<\/pre><\/div>\n\n\n<h3 id=\"toc8\" class=\"wp-block-heading\">serviceWorker.js \u306e\u5185\u5bb9<\/h3>\n\n\n\n<p>\u203b \u30d7\u30c3\u30b7\u30e5\u30b5\u30fc\u30d0\u3068\u901a\u4fe1\u3057\u306a\u3044\u3067\u826f\u3044\u306a\u3089\u3001\u7a7a\u767d\u3067\u826f\u3044\u3002<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n\/\/ \u30b3\u30fc\u30c9\u7121\u3057\u3002\n<\/pre><\/div>\n\n\n<h3 id=\"toc9\" class=\"wp-block-heading\">\u52d5\u4f5c\u78ba\u8a8d<\/h3>\n\n\n\n<p>Chrome, Firefox, Safari \u3067\u52d5\u4f5c\u78ba\u8a8d\u3092\u3059\u308b\u3002<\/p>\n\n\n\n<p>\u4f59\u8ac7\u3067\u3059\u304c\u30b3\u30fc\u30c9\u3092\u8a66\u3057\u3066\u3044\u308b\u9593\u3001\u4e0b\u8a18\u306e\u73fe\u8c61\u304c\u3042\u3063\u305f\u3002<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Chrome\u3060\u3068\u52d5\u304f\u30b3\u30fc\u30c9\u3067\u3082Firefox\u3067\u52d5\u304b\u306a\u3044\u3053\u3068\u304c\u3042\u3063\u305f\u3002<\/li>\n\n\n\n<li>Chrome\u3060\u3068\u52d5\u304f\u30b3\u30fc\u30c9\u3067\u3082Safari\u3067\u52d5\u304b\u306a\u3044\u3053\u3068\u304c\u3042\u3063\u305f\u3002<\/li>\n\n\n\n<li>Firefox\u3060\u3068\u52d5\u304f\u30b3\u30fc\u30c9\u3067\u3082Safari\u3067\u52d5\u304b\u306a\u3044\u3053\u3068\u304c\u3042\u3063\u305f\u3002<\/li>\n<\/ul>\n\n\n\n<h2 id=\"toc10\" class=\"wp-block-heading\">\u30d7\u30c3\u30b7\u30e5\u30b5\u30fc\u30d0\u3068\u901a\u4fe1\u3057\u3066\u901a\u77e5\u3092\u8868\u793a\u3059\u308b<\/h2>\n\n\n\n<h3 id=\"toc11\" class=\"wp-block-heading\">minishlink\/web-push \u3092\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb<\/h3>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\n% composer require minishlink\/web-push\n<\/pre><\/div>\n\n\n<h3 id=\"toc12\" class=\"wp-block-heading\">\u516c\u958b\u9375\u3068\u79d8\u5bc6\u9375\u306e\u30da\u30a2\u3092\u751f\u6210<\/h3>\n\n\n\n<p>\u30b7\u30a7\u30eb\u304b\u3089\u5b9f\u884c\u3059\u308b\u5834\u5408\u3068\u3001PHP\u306e\u30b3\u30fc\u30c9\u304b\u3089\u5b9f\u884c\u3059\u308b\u5834\u5408\u306e\u30012\u3064\u306e\u65b9\u6cd5\u304c\u3042\u308b\u3002<\/p>\n\n\n\n<p>Linux\u306e\u30b7\u30a7\u30eb\u304b\u3089openssl\u30b3\u30de\u30f3\u30c9\u3092\u5229\u7528\u3057\u3066\u751f\u6210\u3059\u308b\u306b\u306f\u4e0b\u8a18\u30b3\u30de\u30f3\u30c9\u3002<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\n$ mkdir keys &amp;&amp; cd keys\n$ openssl ecparam -genkey -name prime256v1 -out private_key.pem\n$ openssl ec -in private_key.pem -pubout -outform DER|tail -c 65|base64|tr -d &#039;=&#039; |tr &#039;\/+&#039; &#039;_-&#039; &gt;&gt; public_key.txt\n$ openssl ec -in private_key.pem -outform DER|tail -c +8|head -c 32|base64|tr -d &#039;=&#039; |tr &#039;\/+&#039; &#039;_-&#039; &gt;&gt; private_key.txt\n\n\u203b public_key.txt \u306b\u516c\u958b\u9375\u304c\u4fdd\u5b58\u3055\u308c\u308b\u3002\n\u203b private_key.txt \u306b\u516c\u958b\u9375\u304c\u4fdd\u5b58\u3055\u308c\u308b\u3002\n<\/pre><\/div>\n\n\n<p>PHP\u306e\u30b3\u30fc\u30c9\u304b\u3089\u5b9f\u884c\u3059\u308b\u5834\u5408\u306b\u306f\u4e0b\u8a18\u30b3\u30fc\u30c9\u3092\u5b9f\u884c\u3059\u308b\u3002<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: php; title: ; notranslate\" title=\"\">\n&lt;?php\n\nrequire_once __DIR__ . &#039;\/vendor\/autoload.php&#039;;\n\nuse Minishlink\\WebPush\\VAPID;\n\n$keys = VAPID::createVapidKeys();\nprintf(&quot;publicKey = %s%s&quot;, $keys&#x5B;&#039;publicKey&#039;], PHP_EOL);\nprintf(&quot;privateKey = %s%s&quot;, $keys&#x5B;&#039;privateKey&#039;], PHP_EOL);\n<\/pre><\/div>\n\n\n<h3 id=\"toc13\" class=\"wp-block-heading\">\u30b3\u30fc\u30c9\u306e\u6e96\u5099<\/h3>\n\n\n\n<h4 id=\"toc14\" class=\"wp-block-heading\">\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u69cb\u6210<\/h4>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nBasicTest2\n\u251c vendor\n\u2502 \u2514 ...\n\u251c app.js\n\u251c composer.json\n\u251c composer.lock\n\u251c index.html\n\u251c manual_send_push_notification.php\n\u251c receive_subscription.php\n\u251c router.php\n\u251c send_push_notification.php\n\u251c serviceWorker.js\n\u2514 subscription.txt\n<\/pre><\/div>\n\n\n<h4 id=\"toc15\" class=\"wp-block-heading\">\u5404\u30d5\u30a1\u30a4\u30eb\u306e\u6982\u8981<\/h4>\n\n\n\n<ul class=\"wp-block-list\">\n<li>router.php<br>PHP\u306e\u30d3\u30eb\u30c8\u30a4\u30f3\u958b\u767a\u7528\u30b5\u30fc\u30d0\u3092\u5229\u7528\u3059\u308b\u5834\u5408\u306e\u3001\u7c21\u6613\u30eb\u30fc\u30c6\u30a3\u30f3\u30b0\u3092\u884c\u3046\u3002<\/li>\n\n\n\n<li>index.php<br>\u8868\u793a\u3059\u308b\u30da\u30fc\u30b8\u3002\u3053\u3053\u304b\u3089\u5404\u7a2e\u52d5\u4f5c\u78ba\u8a8d\u3092\u884c\u3046\u3002<\/li>\n\n\n\n<li>app.js<br>\u30b5\u30fc\u30d3\u30b9\u30ef\u30fc\u30ab\u30fc\u306e\u767b\u9332\u3001\u30d7\u30c3\u30b7\u30e5\u30b5\u30fc\u30d0\u3078\u30b5\u30d6\u30b9\u30af\u30ea\u30d7\u30b7\u30e7\u30f3\u767b\u9332\u3001\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u30b5\u30fc\u30d0\u3078\u767b\u9332\u3057\u305f\u30b5\u30d6\u30b9\u30af\u30ea\u30d7\u30b7\u30e7\u30f3\u306e\u60c5\u5831\u7b49\u3092\u9001\u4fe1\u3059\u308b\u30a4\u30d9\u30f3\u30c8\u30cf\u30f3\u30c9\u30e9\u304c\u3042\u308b\u3002<\/li>\n\n\n\n<li>serviceWorker.js<br>\u30d7\u30c3\u30b7\u30e5\u30b5\u30fc\u30d0\u304b\u3089\u53d7\u4fe1\u3057\u305f\u30c7\u30fc\u30bf\u3092\u3082\u3068\u306b\u901a\u77e5\u7b49\u3092\u884c\u3046\u30a4\u30d9\u30f3\u30c8\u30cf\u30f3\u30c9\u30e9\u304c\u3042\u308b\u3002<\/li>\n\n\n\n<li>receive_subscription.php<br>\u30d7\u30c3\u30b7\u30e5\u30b5\u30fc\u30d0\u3078\u767b\u9332\u3057\u305f\u30b5\u30d6\u30b9\u30af\u30ea\u30d7\u30b7\u30e7\u30f3\u7b49\u306e\u60c5\u5831\u3092\u4fdd\u5b58\u3059\u308b\u3002<\/li>\n\n\n\n<li>send_push_notification.php<br>\u30d7\u30c3\u30b7\u30e5\u901a\u77e5\u3092\u5b9f\u65bd\u3059\u308b\u305f\u3081\u306eAPI\u3002<\/li>\n\n\n\n<li>manual_send_push_notification.php<br>\u624b\u52d5\u3067\u30d7\u30c3\u30b7\u30e5\u901a\u77e5\u3092\u5b9f\u65bd\u3059\u308b\u305f\u3081\u306e\u30b9\u30af\u30ea\u30d7\u30c8\u3002<\/li>\n<\/ul>\n\n\n\n<p><\/p>\n\n\n\n<p><\/p>\n\n\n\n<h4 id=\"toc16\" class=\"wp-block-heading\">router.php \u306e\u5185\u5bb9<\/h4>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: php; title: ; notranslate\" title=\"\">\n&lt;?php\n\nif ($_SERVER&#x5B;&#039;REQUEST_URI&#039;] === &#039;\/&#039;) {\n    require_once &#039;index.html&#039;;\n} else {\n    $fileName = mb_substr($_SERVER&#x5B;&#039;REQUEST_URI&#039;], 1);\n    if (preg_match(&#039;|.js$|u&#039;, $fileName) === 1) {\n        header(&#039;Content-Type: text\/javascript&#039;);\n    }\n    require_once $fileName;\n}\n<\/pre><\/div>\n\n\n<h4 id=\"toc17\" class=\"wp-block-heading\">index.html \u306e\u5185\u5bb9<\/h4>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: xml; title: ; notranslate\" title=\"\">\n&lt;!DOCTYPE html&gt;\n&lt;html lang=&quot;ja&quot;&gt;\n&lt;head&gt;\n  &lt;meta charset=&quot;UTF-8&quot;&gt;\n  &lt;title&gt;BasicTest2&lt;\/title&gt;\n  &lt;script type=&quot;text\/javascript&quot; src=&quot;app.js&quot;&gt;&lt;\/script&gt;\n&lt;\/head&gt;\n&lt;body&gt;\nBasicTest2&lt;br&gt;\n&lt;br&gt;\n&lt;button\n    type=&quot;button&quot;\n    onclick=&quot;getPermission()&quot;\n&gt;\n  \u901a\u77e5\u306e\u8a31\u53ef\u3092\u53d6\u5f97\n&lt;\/button&gt;\n&lt;br&gt;\n&lt;br&gt;\n&lt;button\n    type=&quot;button&quot;\n    onclick=&quot;showNotification()&quot;\n&gt;\n  \u901a\u77e5\u3092\u8868\u793a\n&lt;\/button&gt;\n&lt;br&gt;\n&lt;br&gt;\n&lt;button\n    type=&quot;button&quot;\n    onclick=&quot;updateSw()&quot;\n&gt;\n  \u30b5\u30fc\u30d3\u30b9\u30ef\u30fc\u30ab\u30fc\u3092\u66f4\u65b0\n&lt;\/button&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;\n<\/pre><\/div>\n\n\n<h4 id=\"toc18\" class=\"wp-block-heading\">app.js \u306e\u5185\u5bb9<\/h4>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\n\/\/ \u516c\u958b\u9375\u3002\nconst applicationServerKey = &#039;\u751f\u6210\u3057\u305f\u516c\u958b\u9375\u306e\u6587\u5b57\u5217&#039;;\n\n\/\/ \u516c\u958b\u9375\u3092\u7de8\u96c6\u3059\u308b\u95a2\u6570\u3002minishlink\/web-push\u304b\u3089\u30b3\u30d4\u30da\u3057\u305f\u3002\nfunction urlBase64ToUint8Array (base64String) {\n  const padding = &#039;=&#039;.repeat((4 - (base64String.length % 4)) % 4);\n  const base64 = (base64String + padding).replace(\/\\-\/g, &#039;+&#039;).\n    replace(\/_\/g, &#039;\/&#039;);\n\n  const rawData = window.atob(base64);\n  const outputArray = new Uint8Array(rawData.length);\n\n  for (let i = 0; i &lt; rawData.length; ++i) {\n    outputArray&#x5B;i] = rawData.charCodeAt(i);\n  }\n  return outputArray;\n}\n\n\nasync function getPermission() {\n  \/\/ \u901a\u77e5\u306e\u8a31\u53ef\u3092\u53d6\u5f97\u3002\n  const result = await Notification.requestPermission();\n\n  if (result !== &#039;granted&#039;) {\n    \/\/ \u8a31\u53ef\u4ee5\u5916\u3060\u3063\u305f\u5834\u5408\u306f\u4f55\u3082\u3057\u306a\u3044\u3002\n    return;\n  }\n\n  \/\/ \u30b5\u30fc\u30d3\u30b9\u30ef\u30fc\u30ab\u30fc\u3092\u767b\u9332\u3002\n  await navigator.serviceWorker.register(&#039;serviceWorker.js&#039;);\n\n  \/\/ \u30a2\u30af\u30c6\u30a3\u30d6\u306b\u306a\u3063\u305f\u30b5\u30fc\u30d3\u30b9\u30ef\u30fc\u30ab\u30fc\u3092\u53d6\u5f97\u3002\n  const swr = await navigator.serviceWorker.ready;\n\n  \/\/ \u30d7\u30c3\u30b7\u30e5\u30b5\u30fc\u30d0\u3078\u767b\u9332\u3002\n  const subscription = await swr.pushManager.subscribe({\n    userVisibleOnly: true,\n    applicationServerKey: urlBase64ToUint8Array(applicationServerKey)\n  });\n\n  \/\/ \u6697\u53f7\u5316\u65b9\u5f0f\u3092\u78ba\u8a8d\u3002\n  let contentEncoding = &#039;aesgcm&#039;;\n  if (PushManager.supportedContentEncodings?.includes(&#039;aes128gcm&#039;)) {\n    contentEncoding = &#039;aes128gcm&#039;;\n  }\n\n  \/\/ \u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u30b5\u30fc\u30d0\u3078subscription\u306e\u60c5\u5831\u3092\u4fdd\u5b58\u3002\n  await fetch(&#039;receive_subscription.php&#039;, {\n    method: &#039;POST&#039;,\n    headers: {&#039;Content-Type&#039;: &#039;application\/json&#039;},\n    body: JSON.stringify(Object.assign(subscription.toJSON(), {contentEncoding}))\n  });\n\n  \/\/ \u5b8c\u4e86\u3057\u305f\u65e8\u8868\u793a\u3002\n  alert(&#039;\u30d7\u30c3\u30b7\u30e5\u30b5\u30fc\u30d0\u306e\u767b\u9332\u307e\u3067\u5b8c\u4e86&#039;);\n}\n\n\nasync function showNotification() {\n  \/\/ \u6697\u53f7\u5316\u65b9\u5f0f\u3092\u8a2d\u5b9a\u3002\n  let contentEncoding = &#039;aesgcm&#039;;\n  if (PushManager.supportedContentEncodings?.includes(&#039;aes128gcm&#039;)) {\n    contentEncoding = &#039;aes128gcm&#039;;\n  }\n\n  \/\/ \u65e2\u5b58\u306e\u30d7\u30c3\u30b7\u30e5\u30b5\u30d6\u30b9\u30af\u30ea\u30d7\u30b7\u30e7\u30f3\u3092\u53d6\u5f97\u3002\n  const swr = await navigator.serviceWorker.ready;\n  const jsonSubscription = (await swr.pushManager.getSubscription()).toJSON();\n\n  \/\/ \u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u30b5\u30fc\u30d0\u3078\u30d7\u30c3\u30b7\u30e5\u901a\u77e5\u3092\u3059\u308b\u3088\u3046POST\u3002\n  await fetch(&#039;send_push_notification.php&#039;, {\n    method: &#039;POST&#039;,\n    headers: {&#039;Content-Type&#039;: &#039;application\/json&#039;},\n    body: JSON.stringify(Object.assign(jsonSubscription, { contentEncoding }))\n  });\n\n  \/\/ \u5b8c\u4e86\u3057\u305f\u65e8\u8868\u793a\u3002\n  alert(&#039;\u30d7\u30c3\u30b7\u30e5\u901a\u77e5\u4f9d\u983c\u306ePOST\u307e\u3067\u5b8c\u4e86&#039;)\n}\n\n\nasync function updateSw() {\n  const swr = await navigator.serviceWorker.ready;\n  swr.update();\n}\n<\/pre><\/div>\n\n\n<h4 id=\"toc19\" class=\"wp-block-heading\">serviceWorker.js \u306e\u5185\u5bb9<\/h4>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\n\/\/ \u901a\u77e5\u3092\u30af\u30ea\u30c3\u30af\u3057\u305f\u6642\u306b\u8868\u793a\u3059\u308bURL\u3002\nlet url = null;\n\nself.addEventListener(&#039;push&#039;, async function (event) {\n  const jsonData = event.data.json();\n\n  url = jsonData.url;\n\n  event.waitUntil(\n    self.registration.showNotification(jsonData.title, {\n      body: jsonData.message,\n    }),\n  );\n});\n\n\/\/ \u901a\u77e5\u3092\u30af\u30ea\u30c3\u30af\u3055\u308c\u305f\u6642\u306e\u30a4\u30d9\u30f3\u30c8\u3002\nself.addEventListener(&#039;notificationclick&#039;, function (event) {\n  event.notification.close();\n  clients.openWindow(url);\n});\n<\/pre><\/div>\n\n\n<h4 id=\"toc20\" class=\"wp-block-heading\">receive_subscription.php \u306e\u5185\u5bb9<\/h4>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: php; title: ; notranslate\" title=\"\">\n&lt;?php\n\nrequire_once __DIR__.&#039;\/vendor\/autoload.php&#039;;\n\n$subscription = json_decode(file_get_contents(&#039;php:\/\/input&#039;), true);\n\nfile_put_contents(&#039;subscription.txt&#039;, &#039;$endpoint = &quot;&#039; . $subscription&#x5B;&#039;endpoint&#039;] . &#039;&quot;;&#039; . PHP_EOL, FILE_APPEND);\nfile_put_contents(&#039;subscription.txt&#039;, &#039;$auth = &quot;&#039; . $subscription&#x5B;&#039;keys&#039;]&#x5B;&#039;auth&#039;] . &#039;&quot;;&#039; . PHP_EOL, FILE_APPEND);\nfile_put_contents(&#039;subscription.txt&#039;, &#039;$p256dh = &quot;&#039; . $subscription&#x5B;&#039;keys&#039;]&#x5B;&#039;p256dh&#039;] . &#039;&quot;;&#039; . PHP_EOL, FILE_APPEND);\nfile_put_contents(&#039;subscription.txt&#039;, &#039;$contentEncoding = &quot;&#039; . $subscription&#x5B;&#039;contentEncoding&#039;] . &#039;&quot;;&#039; . PHP_EOL . PHP_EOL, FILE_APPEND);\n\nreturn;\n<\/pre><\/div>\n\n\n<h4 id=\"toc21\" class=\"wp-block-heading\">send_push_notification.php \u306e\u5185\u5bb9<\/h4>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: php; title: ; notranslate\" title=\"\">\n&lt;?php\n\nrequire_once __DIR__.&#039;\/vendor\/autoload.php&#039;;\n\nuse Minishlink\\WebPush\\WebPush;\nuse Minishlink\\WebPush\\Subscription;\n\n$subscription = Subscription::create(json_decode(file_get_contents(&#039;php:\/\/input&#039;), true));\n\n$auth = array(\n    &#039;VAPID&#039; =&gt; array(\n        &#039;subject&#039; =&gt; &#039;mailto:\u30e1\u30fc\u30eb\u30a2\u30c9\u30ec\u30b9&#039;,    \/\/ \u30b5\u30a4\u30c8\u306eURL\u3067\u3082\u826f\u3044\u3002\n        &#039;publicKey&#039; =&gt; &#039;\u751f\u6210\u3057\u305f\u516c\u958b\u9375\u306e\u6587\u5b57\u5217&#039;,\n        &#039;privateKey&#039; =&gt; &#039;\u751f\u6210\u3057\u305f\u79d8\u5bc6\u9375\u306e\u6587\u5b57\u5217&#039;,\n    ),\n);\n\n$webPush = new WebPush($auth);\n\n$payload = json_encode(&#x5B;\n    &#039;title&#039; =&gt; &#039;\u30bf\u30a4\u30c8\u30eb&#039;,\n    &#039;message&#039; =&gt; &#039;\u30e1\u30c3\u30bb\u30fc\u30b8&#039;,\n    &#039;url&#039; =&gt; &#039;https:\/\/google.co.jp&#039;\n]);\n\n$report = $webPush-&gt;sendOneNotification(\n    $subscription,\n    $payload,\n);\n\necho $report-&gt;isSuccess();\necho $report-&gt;getReason();\n<\/pre><\/div>\n\n\n<h4 id=\"toc22\" class=\"wp-block-heading\">manual_send_push_notification.php \u306e\u5185\u5bb9<\/h4>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: php; title: ; notranslate\" title=\"\">\n&lt;?php\n\n\/\/ \u3053\u306e\u30b9\u30af\u30ea\u30d7\u30c8\u306f\u300cphp manual_send_push_notification.php\u300d\u3068\u3044\u3046\u5f62\u3067\n\/\/ \u76f4\u63a5\u5b9f\u884c\u3059\u308b\u5fc5\u8981\u304c\u3042\u308b\u3002\n\nrequire_once __DIR__.&#039;\/vendor\/autoload.php&#039;;\n\nuse Minishlink\\WebPush\\WebPush;\nuse Minishlink\\WebPush\\Subscription;\n\n\/\/ \u3053\u3053\u304b\u3089\n$endpoint = &#039;\u30a8\u30f3\u30c9\u30dd\u30a4\u30f3\u30c8\u3092\u8868\u3059\u6587\u5b57\u5217&#039;;\n$auth = &#039;\u6587\u5b57\u5217&#039;;\n$p256dh = &#039;\u6587\u5b57\u5217&#039;;\n$contentEncoding = &#039;\u6587\u5b57\u5217&#039;;\n\/\/ \u3053\u3053\u307e\u3067\u306e\u60c5\u5831\u306f\u3001\n\/\/ receive_subscription.php \u304c subscription.txt \u306b\u4fdd\u5b58\u3057\u3066\u3044\u308b\u5185\u5bb9\u3092\n\/\/ \u30b3\u30d4\u30da\u3059\u308c\u3070\u826f\u3044\u3002\n\n$subscription = new Subscription($endpoint, $p256dh, $auth, $contentEncoding);\n\n$auth = array(\n    &#039;VAPID&#039; =&gt; array(\n        &#039;subject&#039; =&gt; &#039;mailto:\u30e1\u30fc\u30eb\u30a2\u30c9\u30ec\u30b9&#039;,\n        &#039;publicKey&#039; =&gt; &#039;\u751f\u6210\u3057\u305f\u516c\u958b\u9375\u306e\u6587\u5b57\u5217&#039;,\n        &#039;privateKey&#039; =&gt; &#039;\u751f\u6210\u3057\u305f\u79d8\u5bc6\u9375\u306e\u6587\u5b57\u5217&#039;,\n    ),\n);\n\n$webPush = new WebPush($auth);\n\n$payload = json_encode(&#x5B;\n    &#039;title&#039; =&gt; &#039;\u30bf\u30a4\u30c8\u30eb\u624b\u52d52&#039;,\n    &#039;message&#039; =&gt; &#039;\u30e1\u30c3\u30bb\u30fc\u30b8\u624b\u52d52&#039;,\n    &#039;url&#039; =&gt; &#039;https:\/\/google.co.jp&#039;\n]);\n\n$report = $webPush-&gt;sendOneNotification(\n    $subscription,\n    $payload\n);\n\nprintf(&quot;isSuccess = %s%s&quot;, (string)$report-&gt;isSuccess(), PHP_EOL);\n\/\/ $report-&gt;getReason() \u3067\u30a8\u30e9\u30fc\u8a73\u7d30\u3092\u78ba\u8a8d\u3067\u304d\u308b\u3002\n<\/pre><\/div>\n\n\n<h3 id=\"toc23\" class=\"wp-block-heading\">\u52d5\u4f5c\u78ba\u8a8d<\/h3>\n\n\n\n<p>PHP\u306e\u30d3\u30eb\u30c8\u30a4\u30f3\u958b\u767a\u7528\u30b5\u30fc\u30d0\u3092\u8d77\u52d5\u3059\u308b\u305f\u3081\u306b\u3001\u4e0b\u8a18\u30b3\u30de\u30f3\u30c9\u3092\u5b9f\u884c\u3059\u308b\u3002<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\n% cd BasicTest2\n% php -S localhost:8888 router.php\n<\/pre><\/div>\n\n\n<p>\u305d\u306e\u5f8c\u3001Chrome\u3068Firefox\u3068Safari\u3067 http:\/\/localhost:8888 \u3078\u30a2\u30af\u30bb\u30b9\u3057\u52d5\u4f5c\u78ba\u8a8d\u3092\u3059\u308c\u3070\u826f\u3044\u3002<\/p>\n\n\n\n<p>manual_send_push_notification.php \u306b\u3064\u3044\u3066\u306fphp\u30b3\u30de\u30f3\u30c9\u3092\u5229\u7528\u3057\u3066\u76f4\u63a5\u5b9f\u884c\u3059\u308b\u5fc5\u8981\u304c\u3042\u308b\u3002<\/p>\n\n\n\n<p><\/p>\n\n\n\n<p>\u5404\u30d6\u30e9\u30a6\u30b6\u3067\u6319\u52d5\u304c\u9055\u3046\u7b87\u6240\u304c\u3042\u3063\u305f\u306e\u3067\u3001\u6163\u308c\u308b\u307e\u3067\u306f\u5404Web\u30d6\u30e9\u30a6\u30b6\u3067\u52d5\u4f5c\u78ba\u8a8d\u3059\u308b\u3068\u826f\u3044\u3002<\/p>\n\n\n\n<p>\u9055\u3046\u7b87\u6240\u306b\u3064\u3044\u3066\u306f\u3001\u4f8b\u3048\u3070\u4ee5\u4e0b\u304c\u3042\u3063\u305f\u3002<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Firefox\u3067\u306f\u4e0b\u8a18\u304b\u3089\u4f7f\u7528\u53ef\u80fd\u306a\u6697\u53f7\u5316\u65b9\u5f0f\u3092\u53d6\u5f97\u3067\u304d\u306a\u304b\u3063\u305f\u3002<br>PushManager.supportedContentEncodings<\/li>\n\n\n\n<li>VAPID\u306esubject\u306b\u30e1\u30fc\u30eb\u30a2\u30c9\u30ec\u30b9\u3092\u6307\u5b9a\u3059\u308b\u5834\u5408\u3001Safari\u4ee5\u5916\u306f\u30e1\u30fc\u30eb\u30a2\u30c9\u30ec\u30b9\u306e\u307f\u3067403\u30a8\u30e9\u30fc\u306f\u767a\u751f\u3057\u306a\u304b\u3063\u305f\u304c\u3001Safari\u306e\u5834\u5408\u3001\u300cmailto:\u30e1\u30fc\u30eb\u30a2\u30c9\u30ec\u30b9\u300d\u3068\u3057\u306a\u3044\u3068403\u306e\u30a8\u30e9\u30fc\u304c\u30d7\u30c3\u30b7\u30e5\u30b5\u30fc\u30d0\u304b\u3089\u8fd4\u3063\u3066\u304f\u308b\u3002\uff08\u6700\u521d\u306e\u6570\u56de\u306f\u300cmailto:\u300d\u306f\u7121\u304f\u3066\u3082\u30a8\u30e9\u30fc\u306f\u8fd4\u3063\u3066\u3053\u306a\u304b\u3063\u305f\uff09<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>\u6982\u8981 minishlink\/web-push \u3092\u5229\u7528\u3057\u305fWeb\u30d6\u30e9\u30a6\u30b6\u306e\u30d7\u30c3\u30b7\u30e5\u901a\u77e5\u306e\u52d5\u4f5c\u78ba\u8a8d\u3092\u884c\u3046\u3002\uff08PC\u306e\u307f\uff09 Docker\u30b3\u30f3\u30c6\u30ca\u3067\u306f\u306a\u304fPHP\u306e\u30d3\u30eb\u30c8\u30a4\u30f3\u958b\u767a\u7528\u30b5\u30fc\u30d0\u3067\u5b9f\u884c\u3059\u308b\u3002 PHP\uff1a8.3.7 OS\uff1aMac [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[80],"tags":[81],"class_list":["post-1253","post","type-post","status-publish","format-standard","hentry","category-php","tag-php"],"_links":{"self":[{"href":"https:\/\/shinke1987.net\/index.php?rest_route=\/wp\/v2\/posts\/1253","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/shinke1987.net\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/shinke1987.net\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/shinke1987.net\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/shinke1987.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1253"}],"version-history":[{"count":10,"href":"https:\/\/shinke1987.net\/index.php?rest_route=\/wp\/v2\/posts\/1253\/revisions"}],"predecessor-version":[{"id":1263,"href":"https:\/\/shinke1987.net\/index.php?rest_route=\/wp\/v2\/posts\/1253\/revisions\/1263"}],"wp:attachment":[{"href":"https:\/\/shinke1987.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1253"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/shinke1987.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1253"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/shinke1987.net\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1253"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}