Sending push notifications to Android with PHP and FCM

M
6 min readApr 25, 2020

Send timely alerts to users even when your app isn’t running. In this example, we will send a daily notification showing the most-read story of the day on the Novella app using Firebase Cloud Messaging.

Photo by Bruno Reyna on Unsplash

We are going to be using the Firebase SDK for PHP which you can download here: https://github.com/kreait/firebase-php/. The first step will be to install this using Composer.

Installing Firebase SDK

The easiest way to install the Firebase SDK on your server is using Composer. You will need to log-in to your server using an SSH client; the leading free client is PuTTY which you can download here: https://www.chiark.greenend.org.uk/~sgtatham/putty/

Once you are connected to your server using your log-in credentials you will need to install Composer if it is not already set-up. Below is the recommended install script for Composer to be used in your SSH terminal or by saving as a batch script in your chosen directory. Navigate to your website directory and enter the below, or run as a batch script.

php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
php -r "if (hash_file('sha384', 'composer-setup.php') === 'e0012edf3e80b6978849f5eff0d4b4e4c79ff1609dd1e613307e16318854d24ae64f26d17af3ef0bf7cfb710ca74755a') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
php composer-setup.php
php -r "unlink('composer-setup.php');"

Once Composer is installed, you can use it to automatically install the Firebase SDK as below:

composer require kreait/firebase-php

This will install all the components you need to use Firebase on your server.

Setting up Firebase Account

If you don’t have a Firebase Account you will need to create one. If you already use Firebase in your project you can skip to the next section.

Within Android Studio you can click Tools->Firebase and follow the semi-automated process to set up your account. Otherwise, you can visit https://console.firebase.google.com/ and register your app on this website.

After entering your project details you will need to provide your app’s SHA fingerprint. You can get this easily from Gradle by clicking the ‘Gradle’ button on the right-hand side of Android Studio. Then click on your app’s name to open the sub-folders, then Tasks->android->SigningReport.

Running this will show your SHA-1and SHA-256 fingerprint in the build console at the bottom of the screen. On the Firebase Console, go to your project settings and scroll to “SHA certificate fingerprints” and add one of these fingerprints. SHA-256 is a much more secure hashing algorithm than SHA-1 so it makes sense to use that.

Now, from the Firebase Console click on Service Accounts and Generate New Private Key. You will need to keep this safe and secure as it provides admin access to your Firebase set-up. Upload this to your server in a folder that cannot be accessed by HTTP, e.g. one level above your webpage’s main directory.

Getting user’s device tokens

Before we can send a message, we need to know who we are sending it to. For this, we are going to simply store device tokens in an SQL database however you will likely want to capture additional user information for a more targeted approach. Within your app put the below code to capture a device token from a user.

// Set up Firebase Cloud Messaging
FirebaseInstanceId.getInstance().instanceId.addOnSuccessListener(
this
) { instanceIdResult: InstanceIdResult ->
val newToken = instanceIdResult.token
updateToken(newToken)

}

The above code loads the token from Firebase and when it is ready uses the callback to the updateToken() function.

Within updateToken() you will want to send the token to your server, below I have sent the token along with the user’s identifying details from Firebase Auth.

fun updateToken(token: String){
user?.getIdToken(true)
?.addOnCompleteListener { task ->
if (task.isSuccessful) {
val idToken: String? = task.result?.token
val uid = FirebaseAuth.getInstance().currentUser?.uid
Fuel.post(
"./saveToken.php",
listOf("token" to token, "uid" to uid, "idToken" to idToken)
).responseString { request, response, result ->
Log.e(
TAG,
"Sent token to server with response: ${result.component1()}"
)
}
} else {
Log.e(
TAG,
"Error sending token: ${task.getException()}"
)
}
}
}

You can then store this token in a database to use later for sending messages to that user.

Here is an example SQL query but you will also want to perform user validation if you are updating details for a logged-in user.

$stmt = $conn->prepare("INSERT INTO fcm_tokens (user_id, token) VALUES (:uid, :token) ON DUPLICATE KEY UPDATE token=:token");
$stmt->bindParam(':uid', $uid);
$stmt->bindParam(':token', $token);
$stmt->execute();

If you are using FirebaseAuth to manage your users, you can use the below code to validate the credentials (uid and idToken) sent with the updateToken() request.

use Kreait\Firebase\Factory;
use Kreait\Firebase\ServiceAccount;

$serviceAccount = ServiceAccount::fromJsonFile('../YOUR PRIVATE KEY.json');
$firebase = (new Factory)
->withServiceAccount($serviceAccount)
->create();


$idTokenString = $_REQUEST["idToken"];
$uid = $_REQUEST["uid"];



try {
$verifiedIdToken = $firebase->getAuth()->verifyIdToken($idTokenString);
if ($verifiedIdToken->getClaim('sub') != $uid) {
die("Error UID mismatch");
}
} catch (InvalidToken $e) {
die($e->getMessage());
}

$uid = $verifiedIdToken->getClaim('sub');
$user = $firebase->getAuth()->getUser($uid);

Sending a user message

Finally, we can actually send a user message. For this tutorial, we are going to take the most read story from the last 24 hours from the Novella app and broadcast it to all users. In practice, you will probably want to create separate groups for different readers to make sure you only send relevant content.

Firstly, create your new PHP file on your server and load the Firebase SDK. Make sure this file is somewhere that requires authentication to access, otherwise any user could visit this page and send a notification to your users!

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

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


$serviceAccount = ServiceAccount::fromJsonFile('../YOUR PRIVATE KEY.json');
$firebase = (new Factory)
->withServiceAccount($serviceAccount)
->create();

Next, I am selecting the data from my database that I want to send to users, namely the number one most-read story of the past 24 hours:

$stmt = $conn->prepare("SELECT s.title, s.id, s.author, count(action) as actions FROM actions a JOIN stories s ON a.story=s.id WHERE DATE(a.date)=DATE(NOW() - INTERVAL 1 DAY) GROUP BY story ORDER BY actions DESC LIMIT 1");
$stmt->execute();

$result = $stmt->fetch(PDO::FETCH_ASSOC);

Now we use this information to build our notification. I am providing a title, message, and some data for this notification — we can use the data to load the correct story within the app.

$messaging = $firebase->getMessaging();
// Format Notification
$title = "Most Read Story: " . $result["title"];
$message = "Click here to read the most read story on Novella today.";
$data = [
'story' => $result["id"]
];

$notification = Notification::fromArray([
'title' => $title,
'body' => $message,
'data' => $data
]);

Now we have built the message, all that remains to be done is to retrieve our users’ device tokens and send them the message.

$stmt = $conn->prepare("SELECT token FROM fcm_tokens");
$stmt->execute();

$result = $stmt->fetchAll(PDO::FETCH_ASSOC);

foreach ($result as $row) {
$message = CloudMessage::withTarget('token', $row[token])
->withNotification($notification);
$messaging->send($message);
}

When you now visit this page with your browser it will send the notification with your chosen message!

The finished notification.

Automating the sending of messages

You don’t want to have to visit this page every time you want to send a message. There are many ways to automate this but an easy one is to set up a Cron Job on your server. Depending on which host or platform you are using this will be done slightly differently but fundamentally you will want to set up a Cron Job to run an HTTP request every day (or whatever your chosen period of time is). The Cron Job should visit the web page you have just created and you should provide the authentication details so that it can load the page.

Conclusion

That is the notification process complete. You have now automated a daily notification to all your users sharing a piece of timely and relevant information. You can further customise this by tailoring the message for each user, or dividing your users into groups to receive different pieces of information at different times — perhaps based on their time zone or interests.

Signup for notifications about the Novella app at https://novella.kickoffpages.com/.

You can follow me on Twitter @mgrint or read more of my articles at https://grint.tech. Email me at matt@grint.tech.

--

--