Firebase Cloud Messaging with API V1 using JS and PHP

This tutorial is about Firebase Cloud Messaging, which is one of the Firebase Products

Tutorials :

https://firebase.google.com/docs/cloud-messaging/js/client?hl=en&authuser=0

Steps:

  1. Get Credentials
    Firebase Console URL : https://console.firebase.google.com/

    1. Create project
    2. Create App
    3. Get Required Credentials
      Go to Firebase Console → Project Settings → General → Your apps → Web app (or create one).
      Frontend :

      1. firebaseConfig : forinitializeApp
        Project Settings → Genenal & Scroll down and copy from sample code.
      2. vapidKey : for getToken
        Project Settings → Cloud Messanging → Web Push Certificates → Key pair
      3. fcmToken: received in getToken. Use this to send test notification from console. Click Send test Message. Add fcmToken
        Backend :
      4. $serviceAccountPath : for getAccessToken
        Project Settings → Service accounts & Click Generate new private key
      5. $projectId : to use in $url = 'https://fcm.googleapis.com/v1/projects/' . $projectId . '/messages:send';
        Project Settings → Genenal
  2. Add accounts in Google console. This is optional.
    Project Settings → Service accounts & Click Manage Service account permissions
  3. Service worker js to handle notification in background firebase-messaging-sw.js. ie save as C:\wamp64\www\firebase-messaging-sw.js
  4. Frontend script to subscribe http://localhost/pjtsreehp/easelex/OSOLMVC/tests/firebase/topic/copilotRecieveMessage4Topic.js
  5. Send Test message from Console
  6. install dependencies with composer
    composer  --with-all-dependencies require kreait/firebase-php
  7. Backend script to send mesage with fcmToken. test with php, min PHP 8.2

Notifications to Topics

Tutorials : 
https://firebase.google.com/docs/cloud-messaging/js/send-multiple
https://firebase.google.com/docs/cloud-messaging/js/topic-messaging
  1. Backend script to subscribe to topic http://localhost/pjtsreehp/easelex/OSOLMVC/tests/firebase/topic/subscribe_to_topic2.php
  2. Frontend page to subscribe to topic http://localhost/pjtsreehp/easelex/OSOLMVC/tests/firebase/topic/copilotRecieveMessage4Topic.php
  3. Backend Script to send push message http://localhost/pjtsreehp/easelex/OSOLMVC/tests/firebase/topic/copilotSendMessage2Topics.php

Codes

  1. Service Worker js
  2. Frontend JS to register and receive message
  3. HTML to receive FCM
  4. Backend script to send message to registered devices
  5. Backend PHP script to subscribe to topic
  6. Frontend JS to subscribe to topic
  7. Alternate method using OAuthToken from server using getAccessToken($serviceAccountPath)
  8. Backend PHP to send push message to topic
  9. Send message with Kreait

Service worker js

Back to Codes

firebase-messaging-sw.js

FCM requires a firebase-messaging-sw.js file. Unless you already have a firebase-messaging-sw.js file, create an empty file with that name and place it in the root of your domain before retrieving a token. You can add meaningful content to the file later in the client setup process.

FirebaseError: Messaging: We are unable to register the default service worker. ServiceWorker script at http://localhost/firebase-messaging-sw.js for scope http://localhost/firebase-cloud-messaging-push-scope encountered an error during installation. (messaging/failed-service-worker-registration).

/*
FCM requires a firebase-messaging-sw.js file. Unless you already have a firebase-messaging-sw.js file, create an empty file with that name and place it in the root of your domain before retrieving a token. You can add meaningful content to the file later in the client setup process.

FirebaseError: Messaging: We are unable to register the default service worker. ServiceWorker script at http://localhost/firebase-messaging-sw.js for scope http://localhost/firebase-cloud-messaging-push-scope encountered an error during installation. (messaging/failed-service-worker-registration).
*/
importScripts("https://www.gstatic.com/firebasejs/10.11.1/firebase-app-compat.js");
importScripts("https://www.gstatic.com/firebasejs/10.11.1/firebase-messaging-compat.js");
const firebaseConfig = {
                  apiKey: "YOUR_API_KEY",
                  authDomain: "YOUR_AUTH_DOMAIN",
                  projectId: "YOUR_PROJECT_ID",
                  storageBucket: "YOUR_STORAGE_BUCKET",
                  messagingSenderId: "YOUR_SENDER_ID",
                  appId: "YOUR_APP_ID"
                };
firebase.initializeApp(firebaseConfig);
const messaging = firebase.messaging();

messaging.onBackgroundMessage((payload) => {
  console.log("Background message:", payload);
  self.registration.showNotification(payload.notification.title, {
    body: "BG \r\n" + payload.notification.body ,
  });
});

Frontend JS to register and receive message

Back to Codes
register2FCMAndReceiveMessages.js

// Import the functions you need from the SDKs you need
import { initializeApp } from "https://www.gstatic.com/firebasejs/11.6.1/firebase-app.js";
import { getAnalytics } from "https://www.gstatic.com/firebasejs/11.6.1/firebase-analytics.js";
import { getMessaging,getToken, onMessage    } from "https://www.gstatic.com/firebasejs/11.6.1/firebase-messaging.js";

// TODO: Add SDKs for Firebase products that you want to use
// https://firebase.google.com/docs/web/setup#available-libraries

// Your web app's Firebase configuration
// For Firebase JS SDK v7.20.0 and later, measurementId is optional
const firebaseConfig = {
                  apiKey: "YOUR_API_KEY",
                  authDomain: "YOUR_AUTH_DOMAIN",
                  projectId: "YOUR_PROJECT_ID",
                  storageBucket: "YOUR_STORAGE_BUCKET",
                  messagingSenderId: "YOUR_SENDER_ID",
                  appId: "YOUR_APP_ID"
                };
const  vapidKey = 'YOUR_VAPID_KEY`;

// Initialize Firebase
const app = initializeApp(firebaseConfig);
const analytics = getAnalytics(app);

//Initialize Firebase Cloud Messaging and get a reference to the service
const messaging = getMessaging(app);//firebase.messaging();

function getFCMToken()
{

    // Add the public key generated from the console here. 
    getToken(messaging, {vapidKey: vapidKey}).then((currentToken) => {
                console.log('getToken called');
              if (currentToken) {
                // Send the token to your server and update the UI if necessary
                // ...
                console.log("currentToken is " + currentToken);
                fcmToken = currentToken;

              } else {
                // Show permission request UI
                console.log('No registration token available. Request permission to generate one.');
                // ...
              }
            }).catch((err) => {
              console.log('An error occurred while retrieving token. ', err);
              // ...
            });

                console.log('calling onMessage');
}
function requestPermission() {
  console.log('Requesting permission...');
  if ('Notification' in window) {
      Notification.requestPermission().then(permission => {
           console.log('permiss', permission);
        if (permission === 'granted') {
          console.log('Notification permission granted.');
        } else if (permission === 'denied') {
          console.warn('Notification permission denied.');
        } else {
          console.log('Notification permission dismissed.');
        }
      });
    } else {
      console.error('Notifications are not supported in this browser.');
    }

}

//requestPermission();
function showNotification(notiTitle, notiBody) {
    const notification = new Notification(notiTitle, {
    body:notiBody 
    })
    notification.onclick =(event) =>{alert("Notification clicked");};
}
if (Notification.permission === "granted") { //granted, denied, default
    getFCMToken();
    showNotification("New Desktop notification","Frontend stuff");
} else if (Notification.permission !== "denied") {
    Notification.requestPermission().then(permission=>{
    if (permission === "granted") {
        getFCMToken();
        showNotification("New Desktop notification","Frontend stuff");
    }
    })
}

    onMessage(messaging, (payload) => {
      console.log("Message received. ", JSON.stringify(payload));
      // ...
      //showNotification("Message received. ",JSON.stringify(payload));
    });

HTML to receive FCM

Back to Codes
fcmRecepientFrontend.php

<!DOCTYPE html>
<html lang="en">
<head>
  <title>Firebase Cloud Messaging Text Example</title>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <!-- Compiled and minified CSS -->
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css">

    <!--  Material I cons from Google Fonts. -->
   <link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
   <script>
   var fcmToken;
   </script>

   <script type="module" src="js/register2FCMAndReceiveMessages.js"></script>

</head>
<body>

<div class="container">
  <h1>Firebase Cloud Messaging Text Example</h1>
  <p>This is some text.</p>
</div>
    <!-- Compiled and minified JavaScript -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js"></script>
  <script>
  </script>
</body>
</html> 

Backend script to send message to registered devices

Back to Codes

<?php
require 'vendor/autoload.php';
use Google\Client;
function getAccessToken($serviceAccountPath) {
   $client = new Client();
   $client->setAuthConfig($serviceAccountPath);
   $client->addScope('https://www.googleapis.com/auth/firebase.messaging');
   $client->useApplicationDefaultCredentials();
   $token = $client->fetchAccessTokenWithAssertion();
   return $token['access_token'];
}

function sendMessage($accessToken, $projectId, $message) {
  $url = 'https://fcm.googleapis.com/v1/projects/' . $projectId . '/messages:send';
  $headers = [
   'Authorization: Bearer ' . $accessToken,
   'Content-Type: application/json',
   ];
  $ch = curl_init();
  curl_setopt($ch, CURLOPT_URL, $url);
  curl_setopt($ch, CURLOPT_POST, true);
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
  curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode(['message' => $message]));
  $response = curl_exec($ch);
   if ($response === false) {
   throw new Exception('Curl error: ' . curl_error($ch));
   }
  curl_close($ch);
  return json_decode($response, true);
}
// Path to your service account JSON key file
$serviceAccountPath = 'path/to/your/service-account-file.json';

// Your Firebase project ID
$projectId = 'your-firebase-project-id';

// Example message payload
$message = [
 'token' => 'device-token',
 'notification' => [
 'title' => 'Hello',
 'body' => 'World',
 ],
];
try {
   $accessToken = getAccessToken($serviceAccountPath);
   $response = sendMessage($accessToken, $projectId, $message);
   echo 'Message sent successfully: ' . print_r($response, true);
} catch (Exception $e) {
   echo 'Error: ' . $e->getMessage();
}

?>

Backend PHP script to subscribe to topic

Back to Codes
subscribe_to_topic.php

<?php
require __DIR__ . '/../vendor/autoload.php';
use Kreait\Firebase\Factory;

$jsonData = file_get_contents("php://input");
$data = json_decode($jsonData,true);
$fcmToken = $data['fcmToken'] ?? ''; // Device token from frontend
$topic = $data['topic'] ?? 'news';   // Topic name (e.g., 'news')

if (empty($fcmToken)) {
    http_response_code(400);
    die(json_encode(['error' => 'FCM token is required']));
}

$serviceAccountPath = 'path/to/your/service-account-file.json';
$factory = (new Factory)->withServiceAccount($serviceAccountPath);
$messaging = $factory->createMessaging();

// Subscribe device to topic
$response = $messaging->subscribeToTopic('news', [$fcmToken]);// may use unSubscribeToTopic as well.
die(json_encode($response));
?>

Frontend JS to subscribe to topic

Back to Codes
recieveFCMMessage4Topic.js

Add the function subscribe2Topic() below and call it inside if (currentToken) {

  var topic = 'news';
  function subscribe2Topic()
  {
      fetch('subscribe_to_topic.php', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ fcmToken:fcmToken, topic:topic })
        })
        .then(res => res.json())
        .then(data => console.log('Subscribed:', data))
        .catch(err => console.error('Error:', err));

    console.log("Subscribed to topic " + topic);
  }

Alternate method using OAuthToken from server using getAccessToken($serviceAccountPath)

Back to Codes

fetch('https://iid.googleapis.com/iid/v1/YOUR_FCM_TOKEN/rel/topics/news', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer YOUR_OAUTH2_TOKEN', // From PHP script
    'Content-Type': 'application/json'
  }
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error(error));

Backend PHP to send push message to topic

Back to Codes

sendMessage2Topics.php

<?php
require  __DIR__ . '/../vendor/autoload.php';
use Google\Client;
function getAccessToken($serviceAccountPath) {
   $client = new Client();
   $client->setAuthConfig($serviceAccountPath);
   $client->addScope('https://www.googleapis.com/auth/firebase.messaging');
   $client->useApplicationDefaultCredentials();
   $token = $client->fetchAccessTokenWithAssertion();
   return $token['access_token'];
}

function sendFCMMessage($title, $body, $topic) {

    $serviceAccountPath = 'path/to/your/service-account-file.json';

    // Your Firebase project ID
    $projectId = 'your-firebase-project-id';
    $serverKey = getAccessToken($serviceAccountPath);

    //$url = 'https://fcm.googleapis.com/v1/projects/YOUR_PROJECT_ID/messages:send';
    $url = 'https://fcm.googleapis.com/v1/projects/' . $projectId . '/messages:send';

    $message = [
        'message' => [
            'topic' => $topic,
            'notification' => [
                'title' => $title,
                'body' => $body
            ],
            'data' => [
                'extraData' => 'Some extra data'
            ]
        ]
    ];

    $headers = [
        'Authorization: Bearer ' . $serverKey,
        'Content-Type: application/json'
    ];

    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($message));

    $response = curl_exec($ch);
    curl_close($ch);

    return $response;
}

// Example usage
$title = "Hello";
$body = "This is a test notification!";
$topic = "news";

$response = sendFCMMessage($title, $body, $topic);
echo $response;

?>

Send message with Kreait

Back to Codes

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

use Kreait\Firebase\Factory;
use Kreait\Firebase\Messaging\CloudMessage;
use Kreait\Firebase\Messaging\Notification;

$serviceAccountPath = 'path/to/your/service-account-file.json';
$factory = (new Factory)->withServiceAccount($serviceAccountPath);
$messaging = $factory->createMessaging();

// Example usage
$title = "Hello";
$body = "This is a test notification!";
$topic = "news";

// Create a CloudMessage object
$message = CloudMessage::withTarget(
    'topic', // Target type: 'topic', 'token', or 'condition'
    $topic   // Target value (e.g., topic name or device token)
)->withNotification(
    Notification::create($title, $body)
)->withData([
    'key' => 'value' // Optional custom data
]);

// 4. Send the message
try {
    $messaging->send($message);
    echo "Message sent successfully!";
} catch (\Kreait\Firebase\Exception\MessagingException $e) {
    echo "Error: " . $e->getMessage();
}
?>