├── .gitignore
├── phpcs.xml
├── .travis.yml
├── src
├── Exception
│ ├── RuntimeException.php
│ └── InvalidArgumentException.php
└── Gcm
│ ├── Client.php
│ ├── Response.php
│ └── Message.php
├── phpunit.xml.dist
├── README.md
├── composer.json
├── LICENSE.md
├── CHANGELOG.md
└── test
└── Gcm
├── ResponseTest.php
├── MessageTest.php
└── ClientTest.php
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_STORE
2 | composer.lock
3 | composer.phar
4 | vendor/
5 |
--------------------------------------------------------------------------------
/phpcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | src
6 | test
7 |
8 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: php
2 |
3 | matrix:
4 | include:
5 | - php: 5.6
6 | - php: 7.0
7 | - php: 7.1
8 | env:
9 | - CS_CHECK=true
10 | - php: 7.2
11 | - php: 7.3
12 |
13 | before_install:
14 | - composer install --no-interaction
15 |
16 | script:
17 | - ./vendor/bin/phpunit
18 | - if [[ $CS_CHECK == 'true' ]]; then ./vendor/bin/phpcs ; fi
19 |
20 | notifications:
21 | email: false
22 |
--------------------------------------------------------------------------------
/src/Exception/RuntimeException.php:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 | ./test
9 |
10 |
11 |
12 |
13 |
14 | ./src
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ZendService\Google\Gcm [](https://travis-ci.org/zendframework/ZendService_Google_Gcm)
2 | ================================
3 |
4 | > ## Repository abandoned 2019-12-05
5 | >
6 | > This repository is no longer maintained.
7 |
8 | Provides support for Google push notifications.
9 |
10 | ## Requirements
11 |
12 | * PHP >= 5.6
13 |
14 | ## Getting Started
15 |
16 | Install this library using [Composer](http://getcomposer.org/):
17 |
18 | ```bash
19 | $ composer require zendframework/zendservice-google-gcm
20 | ```
21 |
22 | ## Documentation
23 |
24 | The documentation can be found at: http://framework.zend.com/manual/current/en/modules/zendservice.google.gcm.html
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "zendframework/zendservice-google-gcm",
3 | "description": "OOP wrapper for Google Cloud Messaging",
4 | "license": "BSD-3-Clause",
5 | "keywords": [
6 | "zf",
7 | "zendframework",
8 | "gcm",
9 | "push",
10 | "notification",
11 | "google"
12 | ],
13 | "support": {
14 | "issues": "https://github.com/zendframework/ZendService_Google_Gcm/issues",
15 | "source": "https://github.com/zendframework/ZendService_Google_Gcm",
16 | "rss": "https://github.com/zendframework/ZendService_Google_Gcm/releases.atom",
17 | "chat": "https://zendframework-slack.herokuapp.com",
18 | "forum": "https://discourse.zendframework.com/c/questions/components"
19 | },
20 | "require": {
21 | "php": "^5.6 || ^7.0",
22 | "zendframework/zend-http": "^2.0",
23 | "zendframework/zend-json": "^2.0 || ^3.0"
24 | },
25 | "require-dev": {
26 | "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.5",
27 | "zendframework/zend-coding-standard": "~1.0.0"
28 | },
29 | "autoload": {
30 | "psr-4": {
31 | "ZendService\\Google\\": "src/"
32 | }
33 | },
34 | "autoload-dev": {
35 | "psr-4": {
36 | "ZendServiceTest\\Google\\": "test/"
37 | }
38 | },
39 | "config": {
40 | "sort-packages": true
41 | },
42 | "extra": {
43 | "branch-alias": {
44 | "dev-master": "2.1.x-dev",
45 | "dev-develop": "2.2.x-dev"
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | Copyright (c) 2005-2018, Zend Technologies USA, Inc.
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without modification,
5 | are permitted provided that the following conditions are met:
6 |
7 | - Redistributions of source code must retain the above copyright notice, this
8 | list of conditions and the following disclaimer.
9 |
10 | - Redistributions in binary form must reproduce the above copyright notice, this
11 | list of conditions and the following disclaimer in the documentation and/or
12 | other materials provided with the distribution.
13 |
14 | - Neither the name of Zend Technologies USA, Inc. nor the names of its
15 | contributors may be used to endorse or promote products derived from this
16 | software without specific prior written permission.
17 |
18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
22 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
25 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | All notable changes to this project will be documented in this file, in reverse chronological order by release.
4 |
5 | ## 2.1.2 - TBD
6 |
7 | ### Added
8 |
9 | - Nothing.
10 |
11 | ### Changed
12 |
13 | - Nothing.
14 |
15 | ### Deprecated
16 |
17 | - Nothing.
18 |
19 | ### Removed
20 |
21 | - Nothing.
22 |
23 | ### Fixed
24 |
25 | - Nothing.
26 |
27 | ## 2.1.1 - 2019-02-07
28 |
29 | ### Added
30 |
31 | - [#37](https://github.com/zendframework/ZendService_Google_Gcm/pull/37) adds support for PHP 7.3.
32 |
33 | - [#38](https://github.com/zendframework/ZendService_Google_Gcm/pull/38) adds support for zend-json v3 releases.
34 |
35 | ### Changed
36 |
37 | - Nothing.
38 |
39 | ### Deprecated
40 |
41 | - Nothing.
42 |
43 | ### Removed
44 |
45 | - Nothing.
46 |
47 | ### Fixed
48 |
49 | - Nothing.
50 |
51 | ## 2.1.0 - 2018-05-08
52 |
53 | ### Added
54 |
55 | - [#35](https://github.com/zendframework/ZendService_Google_Gcm/pull/35) adds support for PHP 7.1 and 7.2.
56 |
57 | - [#13](https://github.com/zendframework/ZendService_Google_Gcm/pull/13) adds constants mapping to common GCM error codes as `ZendService\Gcm\Response::ERROR_*`.
58 |
59 | ### Changed
60 |
61 | - Nothing.
62 |
63 | ### Deprecated
64 |
65 | - Nothing.
66 |
67 | ### Removed
68 |
69 | - [#35](https://github.com/zendframework/ZendService_Google_Gcm/pull/35) removes support for PHP 5.5.
70 |
71 | - [#35](https://github.com/zendframework/ZendService_Google_Gcm/pull/35) removes support for HHVM.
72 |
73 | ### Fixed
74 |
75 | - [#18](https://github.com/zendframework/ZendService_Google_Gcm/pull/18) adds a `Content-Length` header with the message length prior to sending
76 | messages to GCM; this fixes 411 errors previously observed.
77 |
78 | ## 2.0.0 - 2017-01-17
79 |
80 | ### Added
81 |
82 | - [#27](https://github.com/zendframework/ZendService_Google_Gcm/pull/27) PSR-4 schema
83 | - [#27](https://github.com/zendframework/ZendService_Google_Gcm/pull/27) PHP >= 5.5 & 7
84 | - [#20](https://github.com/zendframework/ZendService_Google_Gcm/pull/25) Notification and priority parameters for FCM
85 |
86 | ### Deprecated
87 |
88 | - Nothing.
89 |
90 | ### Removed
91 |
92 | - Nothing.
93 |
94 | ### Fixed
95 |
96 | - [#27](https://github.com/zendframework/ZendService_Google_Gcm/pull/27) Fix travis CI integration
97 | - [#27](https://github.com/zendframework/ZendService_Google_Gcm/pull/27) Fix coding style (use ::class and short arrays)
98 | - [#27](https://github.com/zendframework/ZendService_Google_Gcm/pull/27) Fix docblocks for IDE integration
99 | - [#20](https://github.com/zendframework/ZendService_Google_Gcm/pull/25) Change endpoint to FCM
100 |
101 | ## 1.0.3 - 2015-10-13
102 |
103 | ### Added
104 |
105 | - Nothing.
106 |
107 | ### Deprecated
108 |
109 | - Nothing.
110 |
111 | ### Removed
112 |
113 | - Nothing.
114 |
115 | ### Fixed
116 |
117 | - [#12](https://github.com/zendframework/ZendService_Google_Gcm/pull/12) -
118 | Updated GCM URL.
119 |
--------------------------------------------------------------------------------
/test/Gcm/ResponseTest.php:
--------------------------------------------------------------------------------
1 | m = new Message();
34 | }
35 |
36 | public function testConstructorExpectedBehavior()
37 | {
38 | $response = new Response();
39 | self::assertNull($response->getResponse());
40 | self::assertNull($response->getMessage());
41 |
42 | $message = new Message();
43 | $response = new Response(null, $message);
44 | self::assertEquals($message, $response->getMessage());
45 | self::assertNull($response->getResponse());
46 |
47 | $message = new Message();
48 | $responseArray = [
49 | 'results' => [
50 | ['message_id' => '1:1234'],
51 | ],
52 | 'success' => 1,
53 | 'failure' => 0,
54 | 'canonical_ids' => 0,
55 | 'multicast_id' => 1,
56 | ];
57 | $response = new Response($responseArray, $message);
58 | self::assertEquals($responseArray, $response->getResponse());
59 | self::assertEquals($message, $response->getMessage());
60 | }
61 |
62 | public function testInvalidConstructorThrowsException()
63 | {
64 | if (PHP_VERSION_ID < 70000) {
65 | self::markTestSkipped('PHP 7 required.');
66 | }
67 |
68 | $this->expectException(\TypeError::class);
69 | new Response('{bad');
70 | }
71 |
72 | public function testInvalidConstructorThrowsExceptionOnPhp7()
73 | {
74 | if (PHP_VERSION_ID >= 70000) {
75 | self::markTestSkipped('PHP >=5.5 required.');
76 | }
77 |
78 | $this->expectException(\PHPUnit_Framework_Error::class);
79 | new Response('{bad');
80 | }
81 |
82 | public function testMessageExpectedBehavior()
83 | {
84 | $message = new Message();
85 | $response = new Response();
86 | $response->setMessage($message);
87 | self::assertEquals($message, $response->getMessage());
88 | }
89 |
90 | public function testResponse()
91 | {
92 | $responseArr = [
93 | 'results' => [
94 | ['message_id' => '1:234'],
95 | ],
96 | 'success' => 1,
97 | 'failure' => 0,
98 | 'canonical_ids' => 0,
99 | 'multicast_id' => '123',
100 | ];
101 | $response = new Response();
102 | $response->setResponse($responseArr);
103 | self::assertEquals($responseArr, $response->getResponse());
104 | self::assertEquals(1, $response->getSuccessCount());
105 | self::assertEquals(0, $response->getFailureCount());
106 | self::assertEquals(0, $response->getCanonicalCount());
107 | // test results non correlated
108 | $expected = [['message_id' => '1:234']];
109 | self::assertEquals($expected, $response->getResults());
110 | $expected = [0 => '1:234'];
111 | self::assertEquals($expected, $response->getResult(Response::RESULT_MESSAGE_ID));
112 |
113 | $message = new Message();
114 | $message->setRegistrationIds(['ABCDEF']);
115 | $response->setMessage($message);
116 | $expected = ['ABCDEF' => '1:234'];
117 | self::assertEquals($expected, $response->getResult(Response::RESULT_MESSAGE_ID));
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/src/Gcm/Client.php:
--------------------------------------------------------------------------------
1 | apiKey;
50 | }
51 |
52 | /**
53 | * Set API Key.
54 | *
55 | * @param string $apiKey
56 | *
57 | * @return Client
58 | *
59 | * @throws Exception\InvalidArgumentException
60 | */
61 | public function setApiKey($apiKey)
62 | {
63 | if (! is_string($apiKey) || empty($apiKey)) {
64 | throw new Exception\InvalidArgumentException('The api key must be a string and not empty');
65 | }
66 | $this->apiKey = $apiKey;
67 |
68 | return $this;
69 | }
70 |
71 | /**
72 | * Get HTTP Client.
73 | *
74 | * @throws \Zend\Http\Client\Exception\InvalidArgumentException
75 | *
76 | * @return \Zend\Http\Client
77 | */
78 | public function getHttpClient()
79 | {
80 | if (! $this->httpClient) {
81 | $this->httpClient = new HttpClient();
82 | $this->httpClient->setOptions(['strictredirects' => true]);
83 | }
84 |
85 | return $this->httpClient;
86 | }
87 |
88 | /**
89 | * Set HTTP Client.
90 | *
91 | * @param \Zend\Http\Client
92 | *
93 | * @return Client
94 | */
95 | public function setHttpClient(HttpClient $http)
96 | {
97 | $this->httpClient = $http;
98 |
99 | return $this;
100 | }
101 |
102 | /**
103 | * Send Message.
104 | *
105 | * @param Message $message
106 | *
107 | * @throws \Zend\Json\Exception\RuntimeException
108 | * @throws \ZendService\Google\Exception\RuntimeException
109 | * @throws \Zend\Http\Exception\RuntimeException
110 | * @throws \Zend\Http\Client\Exception\RuntimeException
111 | * @throws \Zend\Http\Exception\InvalidArgumentException
112 | * @throws \Zend\Http\Client\Exception\InvalidArgumentException
113 | * @throws \ZendService\Google\Exception\InvalidArgumentException
114 | *
115 | * @return Response
116 | */
117 | public function send(Message $message)
118 | {
119 | $client = $this->getHttpClient();
120 | $client->setUri(self::SERVER_URI);
121 | $headers = $client->getRequest()->getHeaders();
122 | $headers->addHeaderLine('Authorization', 'key=' . $this->getApiKey());
123 | $headers->addHeaderLine('Content-length', mb_strlen($message->toJson()));
124 |
125 | $response = $client->setHeaders($headers)
126 | ->setMethod('POST')
127 | ->setRawBody($message->toJson())
128 | ->setEncType('application/json')
129 | ->send();
130 |
131 | switch ($response->getStatusCode()) {
132 | case 500:
133 | throw new Exception\RuntimeException('500 Internal Server Error');
134 | break;
135 | case 503:
136 | $exceptionMessage = '503 Server Unavailable';
137 | if ($retry = $response->getHeaders()->get('Retry-After')) {
138 | $exceptionMessage .= '; Retry After: '.$retry;
139 | }
140 | throw new Exception\RuntimeException($exceptionMessage);
141 | break;
142 | case 401:
143 | throw new Exception\RuntimeException('401 Forbidden; Authentication Error');
144 | break;
145 | case 400:
146 | throw new Exception\RuntimeException('400 Bad Request; invalid message');
147 | break;
148 | }
149 |
150 | if (! $response = Json::decode($response->getBody(), Json::TYPE_ARRAY)) {
151 | throw new Exception\RuntimeException('Response body did not contain a valid JSON response');
152 | }
153 |
154 | return new Response($response, $message);
155 | }
156 | }
157 |
--------------------------------------------------------------------------------
/src/Gcm/Response.php:
--------------------------------------------------------------------------------
1 | setResponse($response);
107 | }
108 |
109 | if ($message) {
110 | $this->setMessage($message);
111 | }
112 | }
113 |
114 | /**
115 | * Get Message.
116 | *
117 | * @return Message
118 | */
119 | public function getMessage()
120 | {
121 | return $this->message;
122 | }
123 |
124 | /**
125 | * Set Message.
126 | *
127 | * @param Message $message
128 | *
129 | * @return Response
130 | */
131 | public function setMessage(Message $message)
132 | {
133 | $this->message = $message;
134 |
135 | return $this;
136 | }
137 |
138 | /**
139 | * Get Response.
140 | *
141 | * @return array
142 | */
143 | public function getResponse()
144 | {
145 | return $this->response;
146 | }
147 |
148 | /**
149 | * Set Response.
150 | *
151 | * @param array $response
152 | *
153 | * @return Response
154 | *
155 | * @throws Exception\InvalidArgumentException
156 | */
157 | public function setResponse(array $response)
158 | {
159 | if (! isset(
160 | $response['results'],
161 | $response['success'],
162 | $response['failure'],
163 | $response['canonical_ids'],
164 | $response['multicast_id']
165 | )) {
166 | throw new Exception\InvalidArgumentException('Response did not contain the proper fields');
167 | }
168 |
169 | $this->response = $response;
170 | $this->results = $response['results'];
171 | $this->cntSuccess = (int) $response['success'];
172 | $this->cntFailure = (int) $response['failure'];
173 | $this->cntCanonical = (int) $response['canonical_ids'];
174 | $this->id = (int) $response['multicast_id'];
175 |
176 | return $this;
177 | }
178 |
179 | /**
180 | * Get Success Count.
181 | *
182 | * @return int
183 | */
184 | public function getSuccessCount()
185 | {
186 | return $this->cntSuccess;
187 | }
188 |
189 | /**
190 | * Get Failure Count.
191 | *
192 | * @return int
193 | */
194 | public function getFailureCount()
195 | {
196 | return $this->cntFailure;
197 | }
198 |
199 | /**
200 | * Get Canonical Count.
201 | *
202 | * @return int
203 | */
204 | public function getCanonicalCount()
205 | {
206 | return $this->cntCanonical;
207 | }
208 |
209 | /**
210 | * Get Results.
211 | *
212 | * @return array multi dimensional array of:
213 | * NOTE: key is registration_id if the message is passed.
214 | * 'registration_id' => [
215 | * 'message_id' => 'id',
216 | * 'error' => 'error',
217 | * 'registration_id' => 'id'
218 | * ]
219 | */
220 | public function getResults()
221 | {
222 | return $this->correlate();
223 | }
224 |
225 | /**
226 | * Get Singular Result.
227 | *
228 | * @param int $flag one of the RESULT_* flags
229 | *
230 | * @return array singular array with keys being registration id
231 | * value is the type of result
232 | */
233 | public function getResult($flag)
234 | {
235 | $ret = [];
236 | foreach ($this->correlate() as $k => $v) {
237 | if (isset($v[$flag])) {
238 | $ret[$k] = $v[$flag];
239 | }
240 | }
241 |
242 | return $ret;
243 | }
244 |
245 | /**
246 | * Correlate Message and Result.
247 | *
248 | * @return array
249 | */
250 | protected function correlate()
251 | {
252 | $results = $this->results;
253 | if ($this->message && $results) {
254 | $ids = $this->message->getRegistrationIds();
255 | while ($id = array_shift($ids)) {
256 | $results[$id] = array_shift($results);
257 | }
258 | }
259 |
260 | return $results;
261 | }
262 | }
263 |
--------------------------------------------------------------------------------
/test/Gcm/MessageTest.php:
--------------------------------------------------------------------------------
1 | 'value',
32 | 'key2' => [
33 | 'value',
34 | ],
35 | ];
36 |
37 | /**
38 | * @var Message
39 | */
40 | private $m;
41 |
42 | public function setUp()
43 | {
44 | $this->m = new Message();
45 | }
46 |
47 | public function testExpectedRegistrationIdBehavior()
48 | {
49 | self::assertEquals($this->m->getRegistrationIds(), []);
50 | self::assertNotContains('registration_ids', $this->m->toJson());
51 | $this->m->setRegistrationIds($this->validRegistrationIds);
52 | self::assertEquals($this->m->getRegistrationIds(), $this->validRegistrationIds);
53 | foreach ($this->validRegistrationIds as $id) {
54 | $this->m->addRegistrationId($id);
55 | }
56 | self::assertEquals($this->m->getRegistrationIds(), $this->validRegistrationIds);
57 | self::assertContains('registration_ids', $this->m->toJson());
58 | $this->m->clearRegistrationIds();
59 | self::assertEquals($this->m->getRegistrationIds(), []);
60 | self::assertNotContains('registration_ids', $this->m->toJson());
61 | $this->m->addRegistrationId('1029384756');
62 | self::assertEquals($this->m->getRegistrationIds(), ['1029384756']);
63 | self::assertContains('registration_ids', $this->m->toJson());
64 | }
65 |
66 | public function testInvalidRegistrationIdThrowsException()
67 | {
68 | $this->expectException(\InvalidArgumentException::class);
69 | $this->m->addRegistrationId(['1234']);
70 | }
71 |
72 | public function testExpectedCollapseKeyBehavior()
73 | {
74 | self::assertEquals($this->m->getCollapseKey(), null);
75 | self::assertNotContains('collapse_key', $this->m->toJson());
76 | $this->m->setCollapseKey('my collapse key');
77 | self::assertEquals($this->m->getCollapseKey(), 'my collapse key');
78 | self::assertContains('collapse_key', $this->m->toJson());
79 | $this->m->setCollapseKey(null);
80 | self::assertEquals($this->m->getCollapseKey(), null);
81 | self::assertNotContains('collapse_key', $this->m->toJson());
82 | }
83 |
84 | public function testInvalidCollapseKeyThrowsException()
85 | {
86 | $this->expectException(\InvalidArgumentException::class);
87 | $this->m->setCollapseKey(['1234']);
88 | }
89 |
90 | public function testExpectedDataBehavior()
91 | {
92 | self::assertEquals($this->m->getData(), []);
93 | self::assertNotContains('data', $this->m->toJson());
94 | $this->m->setData($this->validData);
95 | self::assertEquals($this->m->getData(), $this->validData);
96 | self::assertContains('data', $this->m->toJson());
97 | $this->m->clearData();
98 | self::assertEquals($this->m->getData(), []);
99 | self::assertNotContains('data', $this->m->toJson());
100 | $this->m->addData('mykey', 'myvalue');
101 | self::assertEquals($this->m->getData(), ['mykey' => 'myvalue']);
102 | self::assertContains('data', $this->m->toJson());
103 | }
104 |
105 | public function testExpectedNotificationBehavior()
106 | {
107 | $this->assertEquals($this->m->getNotification(), []);
108 | $this->assertNotContains('notification', $this->m->toJson());
109 | $this->m->setNotification($this->validData);
110 | $this->assertEquals($this->m->getNotification(), $this->validData);
111 | $this->assertContains('notification', $this->m->toJson());
112 | $this->m->clearNotification();
113 | $this->assertEquals($this->m->getNotification(), []);
114 | $this->assertNotContains('notification', $this->m->toJson());
115 | $this->m->addNotification('mykey', 'myvalue');
116 | $this->assertEquals($this->m->getNotification(), ['mykey' => 'myvalue']);
117 | $this->assertContains('notification', $this->m->toJson());
118 | }
119 |
120 | public function testInvalidDataThrowsException()
121 | {
122 | $this->expectException(\InvalidArgumentException::class);
123 | $this->m->addData(['1234'], 'value');
124 | }
125 |
126 | public function testDuplicateDataKeyThrowsException()
127 | {
128 | $this->expectException(\RuntimeException::class);
129 | $this->m->setData($this->validData);
130 | $this->m->addData('key', 'value');
131 | }
132 |
133 | public function testExpectedDelayWhileIdleBehavior()
134 | {
135 | self::assertEquals($this->m->getDelayWhileIdle(), false);
136 | self::assertNotContains('delay_while_idle', $this->m->toJson());
137 | $this->m->setDelayWhileIdle(true);
138 | self::assertEquals($this->m->getDelayWhileIdle(), true);
139 | self::assertContains('delay_while_idle', $this->m->toJson());
140 | $this->m->setDelayWhileIdle(false);
141 | self::assertEquals($this->m->getDelayWhileIdle(), false);
142 | self::assertNotContains('delay_while_idle', $this->m->toJson());
143 | }
144 |
145 | public function testExpectedTimeToLiveBehavior()
146 | {
147 | self::assertEquals($this->m->getTimeToLive(), 2419200);
148 | self::assertNotContains('time_to_live', $this->m->toJson());
149 | $this->m->setTimeToLive(12345);
150 | self::assertEquals($this->m->getTimeToLive(), 12345);
151 | self::assertContains('time_to_live', $this->m->toJson());
152 | $this->m->setTimeToLive(2419200);
153 | self::assertEquals($this->m->getTimeToLive(), 2419200);
154 | self::assertNotContains('time_to_live', $this->m->toJson());
155 | }
156 |
157 | public function testExpectedRestrictedPackageBehavior()
158 | {
159 | self::assertEquals($this->m->getRestrictedPackageName(), null);
160 | self::assertNotContains('restricted_package_name', $this->m->toJson());
161 | $this->m->setRestrictedPackageName('my.package.name');
162 | self::assertEquals($this->m->getRestrictedPackageName(), 'my.package.name');
163 | self::assertContains('restricted_package_name', $this->m->toJson());
164 | $this->m->setRestrictedPackageName(null);
165 | self::assertEquals($this->m->getRestrictedPackageName(), null);
166 | self::assertNotContains('restricted_package_name', $this->m->toJson());
167 | }
168 |
169 | public function testInvalidRestrictedPackageThrowsException()
170 | {
171 | $this->expectException(\InvalidArgumentException::class);
172 | $this->m->setRestrictedPackageName(['1234']);
173 | }
174 |
175 | public function testExpectedDryRunBehavior()
176 | {
177 | self::assertEquals($this->m->getDryRun(), false);
178 | self::assertNotContains('dry_run', $this->m->toJson());
179 | $this->m->setDryRun(true);
180 | self::assertEquals($this->m->getDryRun(), true);
181 | self::assertContains('dry_run', $this->m->toJson());
182 | $this->m->setDryRun(false);
183 | self::assertEquals($this->m->getDryRun(), false);
184 | self::assertNotContains('dry_run', $this->m->toJson());
185 | }
186 | }
187 |
--------------------------------------------------------------------------------
/test/Gcm/ClientTest.php:
--------------------------------------------------------------------------------
1 | $id,
51 | 'success' => $success,
52 | 'failure' => $failure,
53 | 'canonical_ids' => $ids,
54 | 'results' => $results,
55 | ]);
56 | }
57 |
58 | public function setUp()
59 | {
60 | $this->httpClient = new HttpClient();
61 | $this->httpAdapter = new Test();
62 | $this->httpClient->setAdapter($this->httpAdapter);
63 | $this->gcmClient = new Client();
64 | $this->gcmClient->setHttpClient($this->httpClient);
65 | $this->gcmClient->setApiKey('testing');
66 | $this->message = new Message();
67 | $this->message->addRegistrationId('testing');
68 | $this->message->addData('testKey', 'testValue');
69 | }
70 |
71 | public function testSetApiKeyThrowsExceptionOnNonString()
72 | {
73 | $this->expectException('InvalidArgumentException');
74 | $this->gcmClient->setApiKey([]);
75 | }
76 |
77 | public function testSetApiKey()
78 | {
79 | $key = 'a-login-token';
80 | $this->gcmClient->setApiKey($key);
81 | self::assertEquals($key, $this->gcmClient->getApiKey());
82 | }
83 |
84 | public function testGetHttpClientReturnsDefault()
85 | {
86 | self::assertInstanceOf('Zend\Http\Client', (new Client())->getHttpClient());
87 | }
88 |
89 | public function testSetHttpClient()
90 | {
91 | $client = new HttpClient();
92 | $this->gcmClient->setHttpClient($client);
93 | self::assertEquals($client, $this->gcmClient->getHttpClient());
94 | }
95 |
96 | public function testSendThrowsExceptionWhenServiceUnavailable()
97 | {
98 | $this->expectException('RuntimeException');
99 | $this->httpAdapter->setResponse('HTTP/1.1 503 Service Unavailable'."\r\n\r\n");
100 | $this->gcmClient->send($this->message);
101 | }
102 |
103 | public function testSendThrowsExceptionWhenServerUnavailable()
104 | {
105 | $this->expectException('RuntimeException');
106 | $this->httpAdapter->setResponse('HTTP/1.1 500 Internal Server Error'."\r\n\r\n");
107 | $this->gcmClient->send($this->message);
108 | }
109 |
110 | public function testSendThrowsExceptionWhenInvalidAuthToken()
111 | {
112 | $this->expectException('RuntimeException');
113 | $this->httpAdapter->setResponse('HTTP/1.1 401 Unauthorized'."\r\n\r\n");
114 | $this->gcmClient->send($this->message);
115 | }
116 |
117 | public function testSendThrowsExceptionWhenInvalidPayload()
118 | {
119 | $this->expectException('RuntimeException');
120 | $this->httpAdapter->setResponse('HTTP/1.1 400 Bad Request'."\r\n\r\n");
121 | $this->gcmClient->send($this->message);
122 | }
123 |
124 | public function testSendResultInvalidRegistrationId()
125 | {
126 | $body = $this->createJSONResponse(101, 0, 1, 0, [['error' => 'InvalidRegistration']]);
127 | $this->httpAdapter->setResponse(
128 | 'HTTP/1.1 200 OK'."\r\n".
129 | 'Context-Type: text/html'."\r\n\r\n".
130 | $body
131 | );
132 | $response = $this->gcmClient->send($this->message);
133 | $result = $response->getResults();
134 | $result = array_shift($result);
135 | self::assertEquals('InvalidRegistration', $result['error']);
136 | self::assertEquals(0, $response->getSuccessCount());
137 | self::assertEquals(0, $response->getCanonicalCount());
138 | self::assertEquals(1, $response->getFailureCount());
139 | }
140 |
141 | public function testSendResultMismatchSenderId()
142 | {
143 | $body = $this->createJSONResponse(101, 0, 1, 0, [['error' => 'MismatchSenderId']]);
144 | $this->httpAdapter->setResponse(
145 | 'HTTP/1.1 200 OK'."\r\n".
146 | 'Context-Type: text/html'."\r\n\r\n".
147 | $body
148 | );
149 | $response = $this->gcmClient->send($this->message);
150 | $result = $response->getResults();
151 | $result = array_shift($result);
152 | self::assertEquals('MismatchSenderId', $result['error']);
153 | self::assertEquals(0, $response->getSuccessCount());
154 | self::assertEquals(0, $response->getCanonicalCount());
155 | self::assertEquals(1, $response->getFailureCount());
156 | }
157 |
158 | public function testSendResultNotRegistered()
159 | {
160 | $body = $this->createJSONResponse(101, 0, 1, 0, [['error' => 'NotRegistered']]);
161 | $this->httpAdapter->setResponse(
162 | 'HTTP/1.1 200 OK'."\r\n".
163 | 'Context-Type: text/html'."\r\n\r\n".
164 | $body
165 | );
166 | $response = $this->gcmClient->send($this->message);
167 | $result = $response->getResults();
168 | $result = array_shift($result);
169 | self::assertEquals('NotRegistered', $result['error']);
170 | self::assertEquals(0, $response->getSuccessCount());
171 | self::assertEquals(0, $response->getCanonicalCount());
172 | self::assertEquals(1, $response->getFailureCount());
173 | }
174 |
175 | public function testSendResultMessageTooBig()
176 | {
177 | $body = $this->createJSONResponse(101, 0, 1, 0, [['error' => 'MessageTooBig']]);
178 | $this->httpAdapter->setResponse(
179 | 'HTTP/1.1 200 OK'."\r\n".
180 | 'Context-Type: text/html'."\r\n\r\n".
181 | $body
182 | );
183 | $response = $this->gcmClient->send($this->message);
184 | $result = $response->getResults();
185 | $result = array_shift($result);
186 | self::assertEquals('MessageTooBig', $result['error']);
187 | self::assertEquals(0, $response->getSuccessCount());
188 | self::assertEquals(0, $response->getCanonicalCount());
189 | self::assertEquals(1, $response->getFailureCount());
190 | }
191 |
192 | public function testSendResultSuccessful()
193 | {
194 | $body = $this->createJSONResponse(101, 1, 0, 0, [['message_id' => '1:2342']]);
195 | $this->httpAdapter->setResponse(
196 | 'HTTP/1.1 200 OK'."\r\n".
197 | 'Context-Type: text/html'."\r\n\r\n".
198 | $body
199 | );
200 | $response = $this->gcmClient->send($this->message);
201 | $result = $response->getResults();
202 | $result = array_shift($result);
203 | self::assertEquals('1:2342', $result['message_id']);
204 | self::assertEquals(1, $response->getSuccessCount());
205 | self::assertEquals(0, $response->getCanonicalCount());
206 | self::assertEquals(0, $response->getFailureCount());
207 | }
208 |
209 | public function testSendResultSuccessfulWithRegistrationId()
210 | {
211 | $body = $this->createJSONResponse(101, 1, 0, 1, [['message_id' => '1:2342', 'registration_id' => 'testfoo']]);
212 | $this->httpAdapter->setResponse(
213 | 'HTTP/1.1 200 OK'."\r\n".
214 | 'Context-Type: text/html'."\r\n\r\n".
215 | $body
216 | );
217 | $response = $this->gcmClient->send($this->message);
218 | $result = $response->getResults();
219 | $result = array_shift($result);
220 | self::assertEquals('1:2342', $result['message_id']);
221 | self::assertEquals('testfoo', $result['registration_id']);
222 | self::assertEquals(1, $response->getSuccessCount());
223 | self::assertEquals(1, $response->getCanonicalCount());
224 | self::assertEquals(0, $response->getFailureCount());
225 | }
226 | }
227 |
--------------------------------------------------------------------------------
/src/Gcm/Message.php:
--------------------------------------------------------------------------------
1 | clearRegistrationIds();
83 | foreach ($ids as $id) {
84 | $this->addRegistrationId($id);
85 | }
86 |
87 | return $this;
88 | }
89 |
90 | /**
91 | * Get Registration Ids.
92 | *
93 | * @return array
94 | */
95 | public function getRegistrationIds()
96 | {
97 | return $this->registrationIds;
98 | }
99 |
100 | /**
101 | * Add Registration Ids.
102 | *
103 | * @param string $id
104 | *
105 | * @return Message
106 | *
107 | * @throws Exception\InvalidArgumentException
108 | */
109 | public function addRegistrationId($id)
110 | {
111 | if (! is_string($id) || empty($id)) {
112 | throw new Exception\InvalidArgumentException('$id must be a non-empty string');
113 | }
114 | if (! in_array($id, $this->registrationIds)) {
115 | $this->registrationIds[] = $id;
116 | }
117 |
118 | return $this;
119 | }
120 |
121 | /**
122 | * Clear Registration Ids.
123 | *
124 | * @return Message
125 | */
126 | public function clearRegistrationIds()
127 | {
128 | $this->registrationIds = [];
129 |
130 | return $this;
131 | }
132 |
133 | /**
134 | * Get Collapse Key.
135 | *
136 | * @return string
137 | */
138 | public function getCollapseKey()
139 | {
140 | return $this->collapseKey;
141 | }
142 |
143 | /**
144 | * Set Collapse Key.
145 | *
146 | * @param string $key
147 | *
148 | * @return Message
149 | *
150 | * @throws Exception\InvalidArgumentException
151 | */
152 | public function setCollapseKey($key)
153 | {
154 | if (null !== $key && ! (is_string($key) && strlen($key) > 0)) {
155 | throw new Exception\InvalidArgumentException('$key must be null or a non-empty string');
156 | }
157 | $this->collapseKey = $key;
158 |
159 | return $this;
160 | }
161 |
162 | /**
163 | * Get priority
164 | *
165 | * @return string
166 | */
167 | public function getPriority()
168 | {
169 | return $this->priority;
170 | }
171 |
172 | /**
173 | * Set priority
174 | *
175 | * @param string $priority
176 | * @return Message
177 | * @throws Exception\InvalidArgumentException
178 | */
179 | public function setPriority($priority)
180 | {
181 | if (! is_null($priority) && ! (is_string($priority) && strlen($priority) > 0)) {
182 | throw new Exception\InvalidArgumentException('$priority must be null or a non-empty string');
183 | }
184 | $this->priority = $priority;
185 | return $this;
186 | }
187 |
188 | /**
189 | * Set Data
190 | *
191 | * @param array $data
192 | *
193 | * @throws \ZendService\Google\Exception\InvalidArgumentException
194 | *
195 | * @return Message
196 | */
197 | public function setData(array $data)
198 | {
199 | $this->clearData();
200 | foreach ($data as $k => $v) {
201 | $this->addData($k, $v);
202 | }
203 |
204 | return $this;
205 | }
206 |
207 | /**
208 | * Get Data.
209 | *
210 | * @return array
211 | */
212 | public function getData()
213 | {
214 | return $this->data;
215 | }
216 |
217 | /**
218 | * Add Data.
219 | *
220 | * @param string $key
221 | * @param mixed $value
222 | *
223 | * @throws Exception\RuntimeException
224 | * @throws Exception\InvalidArgumentException
225 | *
226 | * @return Message
227 | */
228 | public function addData($key, $value)
229 | {
230 | if (! is_string($key) || empty($key)) {
231 | throw new Exception\InvalidArgumentException('$key must be a non-empty string');
232 | }
233 | if (array_key_exists($key, $this->data)) {
234 | throw new Exception\RuntimeException('$key conflicts with current set data');
235 | }
236 | $this->data[$key] = $value;
237 |
238 | return $this;
239 | }
240 |
241 | /**
242 | * Clear Data.
243 | *
244 | * @return Message
245 | */
246 | public function clearData()
247 | {
248 | $this->data = [];
249 |
250 | return $this;
251 | }
252 |
253 | /**
254 | * Set notification
255 | *
256 | * @param array $data
257 | * @return Message
258 | */
259 | public function setNotification(array $data)
260 | {
261 | $this->clearNotification();
262 | foreach ($data as $k => $v) {
263 | $this->addNotification($k, $v);
264 | }
265 | return $this;
266 | }
267 |
268 | /**
269 | * Get notification
270 | *
271 | * @return array
272 | */
273 | public function getNotification()
274 | {
275 | return $this->notification;
276 | }
277 |
278 | /**
279 | * Add notification data
280 | *
281 | * @param string $key
282 | * @param mixed $value
283 | * @return Message
284 | * @throws Exception\InvalidArgumentException
285 | * @throws Exception\RuntimeException
286 | */
287 | public function addNotification($key, $value)
288 | {
289 | if (! is_string($key) || empty($key)) {
290 | throw new Exception\InvalidArgumentException('$key must be a non-empty string');
291 | }
292 | if (array_key_exists($key, $this->notification)) {
293 | throw new Exception\RuntimeException('$key conflicts with current set data');
294 | }
295 | $this->notification[$key] = $value;
296 | return $this;
297 | }
298 |
299 | /**
300 | * Clear notification
301 | *
302 | * @return Message
303 | */
304 | public function clearNotification()
305 | {
306 | $this->notification = [];
307 |
308 | return $this;
309 | }
310 |
311 | /**
312 | * Set Delay While Idle
313 | *
314 | * @param bool $delay
315 | *
316 | * @return Message
317 | */
318 | public function setDelayWhileIdle($delay)
319 | {
320 | $this->delayWhileIdle = (bool) $delay;
321 |
322 | return $this;
323 | }
324 |
325 | /**
326 | * Get Delay While Idle.
327 | *
328 | * @return bool
329 | */
330 | public function getDelayWhileIdle()
331 | {
332 | return $this->delayWhileIdle;
333 | }
334 |
335 | /**
336 | * Set Time to Live.
337 | *
338 | * @param int $ttl
339 | *
340 | * @return Message
341 | */
342 | public function setTimeToLive($ttl)
343 | {
344 | $this->timeToLive = (int) $ttl;
345 |
346 | return $this;
347 | }
348 |
349 | /**
350 | * Get Time to Live.
351 | *
352 | * @return int
353 | */
354 | public function getTimeToLive()
355 | {
356 | return $this->timeToLive;
357 | }
358 |
359 | /**
360 | * Set Restricted Package Name.
361 | *
362 | * @param string $name
363 | *
364 | * @return Message
365 | *
366 | * @throws Exception\InvalidArgumentException
367 | */
368 | public function setRestrictedPackageName($name)
369 | {
370 | if (null !== $name && ! (is_string($name) && strlen($name) > 0)) {
371 | throw new Exception\InvalidArgumentException('$name must be null OR a non-empty string');
372 | }
373 | $this->restrictedPackageName = $name;
374 |
375 | return $this;
376 | }
377 |
378 | /**
379 | * Get Restricted Package Name.
380 | *
381 | * @return string
382 | */
383 | public function getRestrictedPackageName()
384 | {
385 | return $this->restrictedPackageName;
386 | }
387 |
388 | /**
389 | * Set Dry Run.
390 | *
391 | * @param bool $dryRun
392 | *
393 | * @return Message
394 | */
395 | public function setDryRun($dryRun)
396 | {
397 | $this->dryRun = (bool) $dryRun;
398 |
399 | return $this;
400 | }
401 |
402 | /**
403 | * Get Dry Run.
404 | *
405 | * @return bool
406 | */
407 | public function getDryRun()
408 | {
409 | return $this->dryRun;
410 | }
411 |
412 | /**
413 | * To JSON
414 | * Utility method to put the JSON into the
415 | * GCM proper format for sending the message.
416 | *
417 | * @return string
418 | */
419 | public function toJson()
420 | {
421 | $json = [];
422 | if ($this->registrationIds) {
423 | $json['registration_ids'] = $this->registrationIds;
424 | }
425 | if ($this->collapseKey) {
426 | $json['collapse_key'] = $this->collapseKey;
427 | }
428 | if ($this->priority) {
429 | $json['priority'] = $this->priority;
430 | }
431 | if ($this->data) {
432 | $json['data'] = $this->data;
433 | }
434 | if ($this->notification) {
435 | $json['notification'] = $this->notification;
436 | }
437 | if ($this->delayWhileIdle) {
438 | $json['delay_while_idle'] = $this->delayWhileIdle;
439 | }
440 | if ($this->timeToLive != 2419200) {
441 | $json['time_to_live'] = $this->timeToLive;
442 | }
443 | if ($this->restrictedPackageName) {
444 | $json['restricted_package_name'] = $this->restrictedPackageName;
445 | }
446 | if ($this->dryRun) {
447 | $json['dry_run'] = $this->dryRun;
448 | }
449 |
450 | return Json::encode($json);
451 | }
452 | }
453 |
--------------------------------------------------------------------------------