Skip to content

Commit a49c317

Browse files
authored
Merge pull request #10 from redjanym/feature/httpv1-migration
HTTP V1 Support
2 parents e7e63fb + 34543b5 commit a49c317

17 files changed

+209
-874
lines changed

.gitignore

+4
Original file line numberDiff line numberDiff line change
@@ -1 +1,5 @@
11
vendor
2+
.php-version
3+
.idea
4+
/composer.lock
5+
examples/service-account.json

README.md

+17-74
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
11
# PHP Firebase Cloud Messaging
22

3-
[![SensioLabsInsight](https://insight.sensiolabs.com/projects/83a88985-9752-463b-ae62-7abb06aea791/big.png)](https://insight.sensiolabs.com/projects/83a88985-9752-463b-ae62-7abb06aea791)
43
<a href='https://www.paypal.me/ymerajredjan' target='_blank'><img height='36' style='border:0px;height:36px;' src='https://az743702.vo.msecnd.net/cdn/kofi2.png?v=0' border='0' alt='Buy Me a Coffee at ko-fi.com' /></a>
54

6-
PHP API for Firebase Cloud Messaging from Google.
7-
8-
Currently this app server library only supports sending Messages/Notifications via HTTP.
5+
PHP SDK for Firebase Cloud Messaging from Google, supporting HTTP V1.
96

107
See original Firebase docs: https://firebase.google.com/docs/
118

@@ -19,87 +16,33 @@ Or add this to your composer.json and run "composer update":
1916

2017
```
2118
"require": {
22-
"redjanym/php-firebase-cloud-messaging": "1.*"
19+
"redjanym/php-firebase-cloud-messaging": "2.*"
2320
}
2421
```
2522

26-
# Send message to **one or multiple** Devices
27-
```
28-
use sngrl\PhpFirebaseCloudMessaging\Client;
29-
use sngrl\PhpFirebaseCloudMessaging\Message;
30-
use sngrl\PhpFirebaseCloudMessaging\Recipient\Device;
31-
use sngrl\PhpFirebaseCloudMessaging\Notification;
32-
33-
$server_key = '_YOUR_SERVER_KEY_';
34-
$client = new Client();
35-
$client->setApiKey($server_key);
36-
37-
$message = new Message();
38-
$message->setPriority('high');
39-
$message->addRecipient(new Device('_YOUR_DEVICE_TOKEN_'));
40-
$message
41-
->setNotification(new Notification('some title', 'some body'))
42-
->setData(['key' => 'value'])
43-
;
44-
45-
$response = $client->send($message);
46-
var_dump($response->getStatusCode());
47-
var_dump($response->getBody()->getContents());
48-
```
49-
50-
# Send message to Topic
51-
Currently sending to topics only supports a single topic as recipient. Mutliple topic as outlined
52-
in the google docs don't seem to work, yet.
23+
# Send message to a Device
5324
```
54-
use sngrl\PhpFirebaseCloudMessaging\Client;
55-
use sngrl\PhpFirebaseCloudMessaging\Message;
56-
use sngrl\PhpFirebaseCloudMessaging\Recipient\Topic;
57-
use sngrl\PhpFirebaseCloudMessaging\Notification;
25+
use RedjanYm\FCM\Client;
26+
use RedjanYm\FCM\Notification;
27+
use RedjanYm\FCM\Recipient\Device;
5828
59-
$server_key = '_YOUR_SERVER_KEY_';
60-
$client = new Client();
61-
$client->setApiKey($server_key);
29+
$serviceAccountPath = '/path/to/service-account.json';
30+
$testToken = 'this-is-a-token';
6231
63-
$message = new Message();
64-
$message->setPriority('high');
65-
$message->addRecipient(new Topic('_YOUR_TOPIC_'));
66-
$message
67-
->setNotification(new Notification('some title', 'some body'))
68-
->setData(['key' => 'value'])
69-
;
32+
$client = new Client($serviceAccountPath);
33+
$recipient = new Device($testToken);
34+
$notification = new Notification($recipient, 'Title', 'Body', ['key' => 'value']);
7035
71-
$response = $client->send($message);
72-
var_dump($response->getStatusCode());
73-
var_dump($response->getBody()->getContents());
36+
$client->send($notification);
7437
```
7538

76-
# Subscribe user to the topic
77-
```
78-
use sngrl\PhpFirebaseCloudMessaging\Client;
39+
# Topic Support
40+
The current version does not have support for Topics. We are going to add it on v2.1.
7941

80-
$server_key = '_YOUR_SERVER_KEY_';
81-
$client = new Client();
82-
$client->setApiKey($server_key);
83-
84-
$response = $client->addTopicSubscription('_SOME_TOPIC_ID_', ['_FIRST_TOKEN_', '_SECOND_TOKEN_']);
85-
var_dump($response->getStatusCode());
86-
var_dump($response->getBody()->getContents());
87-
```
88-
89-
# Remove user subscription to the topic
90-
```
91-
use sngrl\PhpFirebaseCloudMessaging\Client;
92-
93-
$server_key = '_YOUR_SERVER_KEY_';
94-
$client = new Client();
95-
$client->setApiKey($server_key);
96-
97-
$response = $client->removeTopicSubscription('_SOME_TOPIC_ID_', ['_FIRST_TOKEN_', '_SECOND_TOKEN_']);
98-
var_dump($response->getStatusCode());
99-
var_dump($response->getBody()->getContents());
100-
```
42+
# Migrating from V1.
43+
Unfortunately V2 of this package introduces breaking changes. But the new structure of the SDK is still simple and very similar to the previous one. We are sure the migration is going to be very fast and easy.
10144

10245
# Interpreting responses
10346
Responses given on the HTTP requests are standard according to the FCM documentations. You may find detailed specifications in this links:
10447
* https://firebase.google.com/docs/cloud-messaging/http-server-ref#interpret-downstream
105-
* https://firebase.google.com/docs/cloud-messaging/http-server-ref#error-codes
48+
* https://firebase.google.com/docs/cloud-messaging/http-server-ref#error-codes

composer.json

+8-17
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,26 @@
11
{
22
"name": "redjanym/php-firebase-cloud-messaging",
3-
"description": "PHP API for Firebase Cloud Messaging from Google",
3+
"description": "PHP SDK for Firebase Cloud Messaging from Google",
44
"license": "MIT",
5-
"keywords": ["PHP","FCM","Google","Firebase","Cloud","Notifications","Android","iOS","Chrome"],
5+
"keywords": ["PHP","FCM","Google","Firebase","Cloud","Notifications","Android","iOS"],
66
"homepage": "https://github.com/redjanym/php-firebase-cloud-messaging",
77
"authors": [
8-
{
9-
"name": "Sngrl",
10-
"email": "[email protected]"
11-
},
128
{
139
"name": "Redjan Ymeraj",
14-
"email": "ymerajr@yahoo.com"
10+
"email": "ymerajredjan@gmail.com"
1511
}
1612
],
1713
"require": {
1814
"guzzlehttp/guzzle": "*",
19-
"php": ">=5.5"
20-
},
21-
"require-dev": {
22-
"phpunit/phpunit": "*",
23-
"mockery/mockery" : "*"
15+
"php": ">=7.4",
16+
"google/apiclient": "^2.18"
2417
},
2518
"autoload": {
2619
"psr-4": {
27-
"sngrl\\PhpFirebaseCloudMessaging\\": "src/"
20+
"RedjanYm\\FCM\\": "src/"
2821
}
2922
},
30-
"autoload-dev": {
31-
"psr-4": {
32-
"sngrl\\PhpFirebaseCloudMessaging\\Tests\\": "tests/"
33-
}
23+
"require-dev": {
24+
"symfony/var-dumper": "^7.2"
3425
}
3526
}

examples/messaging.php

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
include_once '../vendor/autoload.php';
4+
5+
use RedjanYm\FCM\Client;
6+
use RedjanYm\FCM\Notification;
7+
use RedjanYm\FCM\Recipient\Device;
8+
9+
$serviceAccountPath = 'service-account.json';
10+
$testToken = '123456789';
11+
12+
$client = new Client($serviceAccountPath);
13+
$recipient = new Device($testToken);
14+
$notification = new Notification($recipient, 'Title', 'Body', ['key' => 'value']);
15+
16+
$response = $client->send($notification);
17+
dump($response->getBody()->getContents());

phpunit.xml.dist

-17
This file was deleted.

src/Client.php

+39-89
Original file line numberDiff line numberDiff line change
@@ -1,130 +1,80 @@
11
<?php
2-
namespace sngrl\PhpFirebaseCloudMessaging;
2+
3+
namespace RedjanYm\FCM;
34

45
use GuzzleHttp;
6+
use Psr\Http\Message\ResponseInterface;
57

6-
/**
7-
* @author sngrl
8-
*/
98
class Client implements ClientInterface
109
{
11-
const DEFAULT_API_URL = 'https://fcm.googleapis.com/fcm/send';
12-
const DEFAULT_TOPIC_ADD_SUBSCRIPTION_API_URL = 'https://iid.googleapis.com/iid/v1:batchAdd';
13-
const DEFAULT_TOPIC_REMOVE_SUBSCRIPTION_API_URL = 'https://iid.googleapis.com/iid/v1:batchRemove';
14-
15-
private $apiKey;
16-
private $proxyApiUrl;
17-
private $guzzleClient;
10+
const DEFAULT_API_URL = 'https://fcm.googleapis.com/v1/projects/{PROJECT_ID}/messages:send';
11+
private string $projectId;
12+
private string $serviceAccountPath;
13+
private string $accessToken;
14+
private GuzzleHttp\Client $guzzleClient;
1815

19-
public function __construct()
16+
public function __construct(string $serviceAccountPath)
2017
{
18+
if (file_exists($serviceAccountPath) === false) {
19+
throw new \InvalidArgumentException('Service account file does not exist!');
20+
}
2121

22+
$this->serviceAccountPath = $serviceAccountPath;
2223
$this->guzzleClient = new \GuzzleHttp\Client();
24+
$this->applyCredentials();
2325
}
2426

25-
/**
26-
* add your server api key here
27-
* read how to obtain an api key here: https://firebase.google.com/docs/server/setup#prerequisites
28-
*
29-
* @param string $apiKey
30-
*
31-
* @return \sngrl\PhpFirebaseCloudMessaging\Client
32-
*/
33-
public function setApiKey($apiKey)
27+
public function setServiceAccountPath(string $serviceAccountPath): self
3428
{
35-
$this->apiKey = $apiKey;
36-
return $this;
37-
}
29+
if (file_exists($serviceAccountPath) === false) {
30+
throw new \InvalidArgumentException('Service account file does not exist!');
31+
}
3832

39-
/**
40-
* people can overwrite the api url with a proxy server url of their own
41-
*
42-
* @param string $url
43-
*
44-
* @return \sngrl\PhpFirebaseCloudMessaging\Client
45-
*/
46-
public function setProxyApiUrl($url)
47-
{
48-
$this->proxyApiUrl = $url;
33+
$this->serviceAccountPath = $serviceAccountPath;
34+
$this->applyCredentials();
4935
return $this;
5036
}
5137

52-
/**
53-
* sends your notification to the google servers and returns a guzzle repsonse object
54-
* containing their answer.
55-
*
56-
* @param Message $message
57-
*
58-
* @return \Psr\Http\Message\ResponseInterface
59-
* @throws \GuzzleHttp\Exception\RequestException
60-
*/
61-
public function send(Message $message)
38+
private function applyCredentials(): self
6239
{
63-
return $this->guzzleClient->post(
64-
$this->getApiUrl(),
65-
[
66-
'headers' => [
67-
'Authorization' => sprintf('key=%s', $this->apiKey),
68-
'Content-Type' => 'application/json'
69-
],
70-
'body' => json_encode($message)
71-
]
72-
);
73-
}
40+
$this->projectId = \json_decode(file_get_contents($this->serviceAccountPath), true)['project_id'];
41+
$this->accessToken = $this->getAccessToken();
7442

75-
/**
76-
* @param integer $topic_id
77-
* @param array|string $recipients_tokens
78-
*
79-
* @return \Psr\Http\Message\ResponseInterface
80-
*/
81-
public function addTopicSubscription($topic_id, $recipients_tokens)
82-
{
83-
return $this->processTopicSubscription($topic_id, $recipients_tokens, self::DEFAULT_TOPIC_ADD_SUBSCRIPTION_API_URL);
43+
return $this;
8444
}
8545

86-
87-
/**
88-
* @param integer $topic_id
89-
* @param array|string $recipients_tokens
90-
*
91-
* @return \Psr\Http\Message\ResponseInterface
92-
*/
93-
public function removeTopicSubscription($topic_id, $recipients_tokens)
46+
private function getAccessToken(): string
9447
{
95-
return $this->processTopicSubscription($topic_id, $recipients_tokens, self::DEFAULT_TOPIC_REMOVE_SUBSCRIPTION_API_URL);
96-
}
48+
$client = new \Google\Client();
49+
$client->setAuthConfig($this->serviceAccountPath);
50+
$client->addScope('https://www.googleapis.com/auth/firebase.messaging');
51+
$client->useApplicationDefaultCredentials();
52+
$token = $client->fetchAccessTokenWithAssertion();
9753

54+
return $token['access_token'];
55+
}
9856

9957
/**
100-
* @param integer $topic_id
101-
* @param array|string $recipients_tokens
102-
* @param string $url
103-
*
104-
* @return \Psr\Http\Message\ResponseInterface
58+
* @throws \GuzzleHttp\Exception\RequestException
10559
*/
106-
protected function processTopicSubscription($topic_id, $recipients_tokens, $url)
60+
public function send(Notification $message): ResponseInterface
10761
{
108-
if (!is_array($recipients_tokens))
109-
$recipients_tokens = [$recipients_tokens];
110-
11162
return $this->guzzleClient->post(
112-
$url,
63+
$this->getApiUrl(),
11364
[
11465
'headers' => [
115-
'Authorization' => sprintf('key=%s', $this->apiKey),
66+
'Authorization' => sprintf('Bearer %s', $this->accessToken),
11667
'Content-Type' => 'application/json'
11768
],
118-
'body' => json_encode([
119-
'to' => '/topics/' . $topic_id,
120-
'registration_tokens' => $recipients_tokens,
69+
'body' => \json_encode([
70+
'message' => $message,
12171
])
12272
]
12373
);
12474
}
12575

12676
private function getApiUrl()
12777
{
128-
return isset($this->proxyApiUrl) ? $this->proxyApiUrl : self::DEFAULT_API_URL;
78+
return str_replace('{PROJECT_ID}', $this->projectId, self::DEFAULT_API_URL);
12979
}
13080
}

0 commit comments

Comments
 (0)