Skip to content

Instantly share code, notes, and snippets.

@jubayerarefin
Forked from efreed/ApnsPushService.php
Created September 1, 2023 06:11
Show Gist options
  • Save jubayerarefin/7aa1ce462d42f589d98396737692552b to your computer and use it in GitHub Desktop.
Save jubayerarefin/7aa1ce462d42f589d98396737692552b to your computer and use it in GitHub Desktop.
PHP NotificationPusher to mobile devices (namespace is ready to work with Symfony)
<?php
namespace App\Service\Notification\PushNotification;
use Exception;
// composer require edamov/pushok
// https://github.com/edamov/pushok
use Pushok\AuthProvider;
use Pushok\Client;
use Pushok\Notification;
use Pushok\Payload;
use Pushok\Payload\Alert;
class ApnsPushService {
private $options;
private $production;
/**
* Looks for these vars in $_ENV, unless they are part of $envOverrides. Only provide a P8 or P12 config.
* APNS_P8_PATH An absolute path to the .cert file
* APNS_P8_PASSPHRASE Optionally provide if the cert file needs a passphrase
* APNS_P8_KEY_ID Provide the Key ID from Apple
* APNS_P8_TEAM_ID Provide The Team ID from Apple
* APNS_P12_PATH An absolute path to the P12 .cert file
* APNS_P12_PASSPHRASE Optionally provide if the cert file needs a passphrase
* APNS_BUNDLE_ID Optionally provide if the cert file needs a passphrase
* APNS_ENVIRONMENT Optional as it will default to using $_ENV['APP_ENV']
* @param array $envOverrides
*/
public function __construct($envOverrides=[]) {
$this->options = [
'app_bundle_id' => isset($envOverrides['APNS_BUNDLE_ID']) ? $envOverrides['APNS_BUNDLE_ID'] : ($_ENV['APNS_BUNDLE_ID']??null)
];
$p12Path = isset($envOverrides['APNS_P12_PATH']) ? $envOverrides['APNS_P12_PATH'] : ($_ENV['APNS_P12_PATH']??null);
if ($p12Path) {
$this->options['certificate_path'] = $p12Path;
$this->options['certificate_secret'] = isset($envOverrides['APNS_P12_PASSPHRASE']) ? $envOverrides['APNS_P12_PASSPHRASE'] : ($_ENV['APNS_P12_PASSPHRASE']??null);
} else {
$this->options['private_key_path'] = isset($envOverrides['APNS_P8_PATH']) ? $envOverrides['APNS_P8_PATH'] : ($_ENV['APNS_P8_PATH']??null);
$this->options['private_key_secret'] = isset($envOverrides['APNS_P8_PASSPHRASE']) ? $envOverrides['APNS_P8_PASSPHRASE'] : ($_ENV['APNS_P8_PASSPHRASE']??null);
$this->options['key_id'] = isset($envOverrides['APNS_P8_KEY_ID']) ? $envOverrides['APNS_P8_KEY_ID'] : ($_ENV['APNS_P8_KEY_ID']??null);
$this->options['team_id'] = isset($envOverrides['APNS_P8_TEAM_ID']) ? $envOverrides['APNS_P8_TEAM_ID'] : ($_ENV['APNS_P8_TEAM_ID']??null);
}
// Input validation
// if (!$this->options['...']) {
// throw new Exception('ApnsPushService is missing $_ENV["..."]');
// }
$defaultEnvironment = (($_ENV['APP_ENV']??null) == 'production');
$environment = isset($envOverrides['APNS_ENVIRONMENT']) ? $envOverrides['APNS_ENVIRONMENT'] : ($_ENV['APNS_ENVIRONMENT']??$defaultEnvironment);
$this->production = ($environment == 'prod' || $environment == 'production');
}
/**
* Send the message now
* @param string|array[string] $deviceTokens Message(s) will be sent to these one or many devices
* @param string|array[string] $messages Send one or many messages to all deivces
* @param array $params provide a ['title'] to be shared across all messages, and or specify a ['sound']
* @return array Truthy values confirming each message was sent. The value is the number of bytes sent.
*/
public function push($deviceTokens, $messages, $params=[]) {
if (!is_array($deviceTokens)) {
$deviceTokens = [$deviceTokens];
}
if (!is_array($messages)) {
$messages = [$messages];
}
// Be aware of thing that Token will stale after one hour, but ok to reuse it for a whole page request
if (!($GLOBALS['ApnsPushService_authProvider']??null)) {
if ($this->options['certificate_path']??null) {
$GLOBALS['ApnsPushService_authProvider'] = AuthProvider\Certificate::create($this->options);
} else {
$GLOBALS['ApnsPushService_authProvider'] = AuthProvider\Token::create($this->options);
}
}
$notifications = [];
foreach ($messages as $message) {
$alert = Alert::create();//->setTitle('Hello!');
$alert = $alert->setBody($message);
$payload = Payload::create();
$payload->setAlert($alert);
$payload->setSound('default');
$payload->setBadge(1);
// Add custom value to your notification, needs to be customized
//$payload->setCustomValue('key', 'value');
foreach ($deviceTokens as $deviceToken) {
$notifications[] = new Notification($payload,$deviceToken);
}
}
// If you have issues with ssl-verification, you can temporarily disable it. Please see attached note.
// Disable ssl verification
// $client = new Client($authProvider, $production = false, [CURLOPT_SSL_VERIFYPEER=>false] );
$client = new Client($GLOBALS['ApnsPushService_authProvider'], $this->production);
$client->addNotifications($notifications);
$responses = $client->push(); // returns an array of ApnsResponseInterface (one Response per Notification)
$results = [];
foreach ($responses as $response) {
// dump([
// // The device token
// 'device' => $response->getDeviceToken(),
// // A canonical UUID that is the unique ID for the notification. E.g. 123e4567-e89b-12d3-a456-4266554400a0
// // 'notification_id' => $response->getApnsId(),
// // Status code. E.g. 200 (Success), 410 (The device token is no longer active for the topic.)
// 'status' => $response->getStatusCode(),
// // E.g. The device token is no longer active for the topic.
// 'reason' => $response->getReasonPhrase(),
// // E.g. Unregistered
// 'error' => $response->getErrorReason(),
// // E.g. The device token is inactive for the specified topic.
// 'error_message' => $response->getErrorDescription(),
// 'timestamp' => $response->get410Timestamp(),
// ]);
$error = $response->getErrorReason();
$results[] = $error ?: true;
}
return $results;
}
}
<?php
namespace App\Service\NotificationPusher;
use Exception;
class GcmPushService {
private $apiKey;
private $environment;
/**
* Looks for these 2 vars in $_ENV, unless they are part of $envOverrides
* GCM_PUSH_KEY The GCM key
* GCM_PUSH_ENVIRONMENT Optional as it will default to using $_ENV['APP_ENV']
* @param array $envOverrides
*/
public function __construct($envOverrides=[]) {
$this->apiKey = $envOverrides['GCM_PUSH_KEY'] ?: ($_ENV['GCM_PUSH_KEY']??null);
if (!$this->apiKey) {
throw new Exception('GcmPushService is missing $_ENV["GCM_PUSH_KEY"]');
}
$defaultEnvironment = (($_ENV['APP_ENV']??null) == 'production') ? 'prod' : 'dev';
$environment = $envOverrides['GCM_PUSH_ENVIRONMENT'] ?: ($_ENV['GCM_PUSH_ENVIRONMENT']??$defaultEnvironment);
$this->environment = $environment == 'prod' ? 'prod' : 'dev';
}
/**
* Send the message now
* @param string|array[string] $deviceTokens Message(s) will be sent to these one or many devices
* @param string|array[string] $messages Send one or many messages to all deivces
* @param array $params provide a ['title'] to be shared across all messages, and or specify a ['sound']
* @return array Responses from Gcm
*/
public function push($registrationIds, $messages, $params=[]) {
if (!is_array($registrationIds)) {
$registrationIds = [$registrationIds];
}
if (!is_array($messages)) {
$messages = [$messages];
}
$url = 'https://fcm.googleapis.com/fcm/send';
// TODO What if $this->environment is not 'prod' ?
$headers = array(
'Authorization: key=' . $this->apiKey,
'Content-Type: application/json'
);
$results = [];
foreach ($messages as $message) {
$fields = array(
'registration_ids' => $registrationIds,
'data' => array(
'title' => $params['title'] ?? '',
'message' => $message,
'subtitle' => $params['subtitle'] ?? '',
'tickerText' => $params['ticker'] ?? '',
'msgcnt' => $params['messagecount'] ?? 1,
'vibrate' => $params['vibrate'] ?? 1
)
);
$results[] = $this->useCurl($url, $headers, json_encode($fields));
}
return $results;
}
private function useCurl($url, $headers, $fields = null) {
if (!$url) {
return;
}
// Open connection
$ch = curl_init();
// Set the url, number of POST vars, POST data
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
// Disabling SSL Certificate support temporarly
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
if ($fields) {
curl_setopt($ch, CURLOPT_POSTFIELDS, $fields);
}
// Execute post
$result = curl_exec($ch);
if ($result === FALSE) {
die('Curl failed: ' . curl_error($ch));
}
// Close connection
curl_close($ch);
return $result;
}
}
// To send notifications to Windows Phones, start with a copy of GcmPushService.php and merge in this example logic for the push() function.
// Source: https://gist.github.com/joashp/b2f6c7e24127f2798eb2
public function pushExample($data, $uri) {
$delay = 2;
$msg = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" .
"<wp:Notification xmlns:wp=\"WPNotification\">" .
"<wp:Toast>" .
"<wp:Text1>".htmlspecialchars($data['mtitle'])."</wp:Text1>" .
"<wp:Text2>".htmlspecialchars($data['mdesc'])."</wp:Text2>" .
"</wp:Toast>" .
"</wp:Notification>";
$sendedheaders = array(
'Content-Type: text/xml',
'Accept: application/*',
'X-WindowsPhone-Target: toast',
"X-NotificationClass: $delay"
);
$response = $this->useCurl($uri, $sendedheaders, $msg);
$result = array();
foreach(explode("\n", $response) as $line) {
$tab = explode(":", $line, 2);
if (count($tab) == 2)
$result[$tab[0]] = trim($tab[1]);
}
return $result;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment