diff --git a/.travis.yml b/.travis.yml index 3b81e04..7f38c26 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,17 +1,22 @@ +sudo: false + language: php -php: - - 5.5 - - 5.6 - - 7.0 - - 7.1 - - hhvm +matrix: + include: + - php: 5.6 + - php: 7.0 + - php: 7.1 + env: + - CS_CHECK=true + - php: 7.2 before_install: - - composer install + - composer install --no-interaction script: - - ./vendor/bin/phpunit + - ./vendor/bin/phpunit + - if [[ $CS_CHECK == 'true' ]]; then ./vendor/bin/phpcs ; fi notifications: - irc: "irc.freenode.org#zftalk.2" + email: false diff --git a/CHANGELOG.md b/CHANGELOG.md index 2789c18..e106bb7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,10 +2,16 @@ All notable changes to this project will be documented in this file, in reverse chronological order by release. -## 2.0.1 - TBD +## 2.1.0 - 2018-05-08 ### Added +- [#35](https://github.com/zendframework/ZendService_Google_Gcm/pull/35) adds support for PHP 7.1 and 7.2. + +- [#13](https://github.com/zendframework/ZendService_Google_Gcm/pull/13) adds constants mapping to common GCM error codes as `ZendService\Gcm\Response::ERROR_*`. + +### Changed + - Nothing. ### Deprecated @@ -14,11 +20,14 @@ All notable changes to this project will be documented in this file, in reverse ### Removed -- Nothing. +- [#35](https://github.com/zendframework/ZendService_Google_Gcm/pull/35) removes support for PHP 5.5. + +- [#35](https://github.com/zendframework/ZendService_Google_Gcm/pull/35) removes support for HHVM. ### Fixed -- Nothing. +- [#18](https://github.com/zendframework/ZendService_Google_Gcm/pull/18) adds a `Content-Length` header with the message length prior to sending + messages to GCM; this fixes 411 errors previously observed. ## 2.0.0 - 2017-01-17 diff --git a/LICENSE.txt b/LICENSE.md similarity index 59% rename from LICENSE.txt rename to LICENSE.md index 5ad81e9..d44ab5d 100644 --- a/LICENSE.txt +++ b/LICENSE.md @@ -1,19 +1,19 @@ -Copyright (c) 2005-2014, Zend Technologies USA, Inc. +Copyright (c) 2005-2018, Zend Technologies USA, Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. +- Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. +- Redistributions in binary form must reproduce the above copyright notice, this + list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. - * Neither the name of Zend Technologies USA, Inc. nor the names of its - contributors may be used to endorse or promote products derived from this - software without specific prior written permission. +- Neither the name of Zend Technologies USA, Inc. nor the names of its + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED diff --git a/README.md b/README.md index 4491bfb..5c41dbb 100644 --- a/README.md +++ b/README.md @@ -3,11 +3,11 @@ ZendService\Google\Gcm [![Build Status](https://api.travis-ci.org/zendframework/ Provides support for Google push notifications. -## Requirements ## +## Requirements -* PHP >= 5.3.3 +* PHP >= 5.6 -## Getting Started ## +## Getting Started Install this library using [Composer](http://getcomposer.org/): @@ -15,6 +15,6 @@ Install this library using [Composer](http://getcomposer.org/): $ composer require zendframework/zendservice-google-gcm ``` -## Documentation ## +## Documentation The documentation can be found at: http://framework.zend.com/manual/current/en/modules/zendservice.google.gcm.html diff --git a/composer.json b/composer.json index ec4282e..0936edb 100644 --- a/composer.json +++ b/composer.json @@ -1,32 +1,48 @@ { "name": "zendframework/zendservice-google-gcm", "description": "OOP wrapper for Google Cloud Messaging", - "type": "library", + "license": "BSD-3-Clause", "keywords": [ - "zf2", + "zf", + "zendframework", "gcm", "push", "notification", "google" ], - "homepage": "https://github.com/zendframework/zendservice-google-gcm", - "license": "BSD-3-Clause", + "support": { + "issues": "https://github.com/zendframework/ZendService_Google_Gcm/issues", + "source": "https://github.com/zendframework/ZendService_Google_Gcm", + "rss": "https://github.com/zendframework/ZendService_Google_Gcm/releases.atom", + "chat": "https://zendframework-slack.herokuapp.com", + "forum": "https://discourse.zendframework.com/c/questions/components" + }, + "require": { + "php": "^5.6 || ^7.0", + "zendframework/zend-http": "^2.0", + "zendframework/zend-json": "^2.0" + }, + "require-dev": { + "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.5", + "zendframework/zend-coding-standard": "~1.0.0" + }, "autoload": { "psr-4": { - "ZendService\\Google\\": "library/" + "ZendService\\Google\\": "src/" } }, "autoload-dev": { "psr-4": { - "ZendServiceTest\\Google\\": "tests/" + "ZendServiceTest\\Google\\": "test/" } }, - "require": { - "php": "^5.5 || ^7.0", - "zendframework/zend-http": "^2.0", - "zendframework/zend-json": "^2.0" + "config": { + "sort-packages": true }, - "require-dev": { - "phpunit/PHPUnit": "^4.8" + "extra": { + "branch-alias": { + "dev-master": "2.1.x-dev", + "dev-develop": "2.2.x-dev" + } } } diff --git a/phpcs.xml b/phpcs.xml new file mode 100644 index 0000000..4bfc5d0 --- /dev/null +++ b/phpcs.xml @@ -0,0 +1,7 @@ + + + + + src + test + diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 0f43c99..87a4ec9 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,13 +1,17 @@ - + + - ./tests + ./test - - - disable - - + + + ./src + + diff --git a/library/Exception/InvalidArgumentException.php b/src/Exception/InvalidArgumentException.php similarity index 100% rename from library/Exception/InvalidArgumentException.php rename to src/Exception/InvalidArgumentException.php diff --git a/library/Exception/RuntimeException.php b/src/Exception/RuntimeException.php similarity index 100% rename from library/Exception/RuntimeException.php rename to src/Exception/RuntimeException.php diff --git a/library/Gcm/Client.php b/src/Gcm/Client.php similarity index 92% rename from library/Gcm/Client.php rename to src/Gcm/Client.php index 519c2f6..45b9ae0 100644 --- a/library/Gcm/Client.php +++ b/src/Gcm/Client.php @@ -60,7 +60,7 @@ public function getApiKey() */ public function setApiKey($apiKey) { - if (!is_string($apiKey) || empty($apiKey)) { + if (! is_string($apiKey) || empty($apiKey)) { throw new Exception\InvalidArgumentException('The api key must be a string and not empty'); } $this->apiKey = $apiKey; @@ -77,7 +77,7 @@ public function setApiKey($apiKey) */ public function getHttpClient() { - if (!$this->httpClient) { + if (! $this->httpClient) { $this->httpClient = new HttpClient(); $this->httpClient->setOptions(['strictredirects' => true]); } @@ -119,7 +119,8 @@ public function send(Message $message) $client = $this->getHttpClient(); $client->setUri(self::SERVER_URI); $headers = $client->getRequest()->getHeaders(); - $headers->addHeaderLine('Authorization', 'key='.$this->getApiKey()); + $headers->addHeaderLine('Authorization', 'key=' . $this->getApiKey()); + $headers->addHeaderLine('Content-length', mb_strlen($message->toJson())); $response = $client->setHeaders($headers) ->setMethod('POST') @@ -146,7 +147,7 @@ public function send(Message $message) break; } - if (!$response = Json::decode($response->getBody(), Json::TYPE_ARRAY)) { + if (! $response = Json::decode($response->getBody(), Json::TYPE_ARRAY)) { throw new Exception\RuntimeException('Response body did not contain a valid JSON response'); } diff --git a/library/Gcm/Message.php b/src/Gcm/Message.php similarity index 95% rename from library/Gcm/Message.php rename to src/Gcm/Message.php index c36d6d5..1e55fbe 100644 --- a/library/Gcm/Message.php +++ b/src/Gcm/Message.php @@ -108,10 +108,10 @@ public function getRegistrationIds() */ public function addRegistrationId($id) { - if (!is_string($id) || empty($id)) { + if (! is_string($id) || empty($id)) { throw new Exception\InvalidArgumentException('$id must be a non-empty string'); } - if (!in_array($id, $this->registrationIds)) { + if (! in_array($id, $this->registrationIds)) { $this->registrationIds[] = $id; } @@ -151,7 +151,7 @@ public function getCollapseKey() */ public function setCollapseKey($key) { - if (null !== $key && !(is_string($key) && strlen($key) > 0)) { + if (null !== $key && ! (is_string($key) && strlen($key) > 0)) { throw new Exception\InvalidArgumentException('$key must be null or a non-empty string'); } $this->collapseKey = $key; @@ -178,7 +178,7 @@ public function getPriority() */ public function setPriority($priority) { - if (!is_null($priority) && !(is_string($priority) && strlen($priority) > 0)) { + if (! is_null($priority) && ! (is_string($priority) && strlen($priority) > 0)) { throw new Exception\InvalidArgumentException('$priority must be null or a non-empty string'); } $this->priority = $priority; @@ -227,7 +227,7 @@ public function getData() */ public function addData($key, $value) { - if (!is_string($key) || empty($key)) { + if (! is_string($key) || empty($key)) { throw new Exception\InvalidArgumentException('$key must be a non-empty string'); } if (array_key_exists($key, $this->data)) { @@ -286,7 +286,7 @@ public function getNotification() */ public function addNotification($key, $value) { - if (!is_string($key) || empty($key)) { + if (! is_string($key) || empty($key)) { throw new Exception\InvalidArgumentException('$key must be a non-empty string'); } if (array_key_exists($key, $this->notification)) { @@ -367,7 +367,7 @@ public function getTimeToLive() */ public function setRestrictedPackageName($name) { - if (null !== $name && !(is_string($name) && strlen($name) > 0)) { + if (null !== $name && ! (is_string($name) && strlen($name) > 0)) { throw new Exception\InvalidArgumentException('$name must be null OR a non-empty string'); } $this->restrictedPackageName = $name; diff --git a/library/Gcm/Response.php b/src/Gcm/Response.php similarity index 79% rename from library/Gcm/Response.php rename to src/Gcm/Response.php index d0c16fe..15f86ee 100644 --- a/library/Gcm/Response.php +++ b/src/Gcm/Response.php @@ -37,6 +37,24 @@ class Response */ const RESULT_CANONICAL = 'registration_id'; + /** + * Error field responses + * @link https://developers.google.com/cloud-messaging/http-server-ref#error-codes + * @var string + */ + const ERROR_MISSING_REGISTRATION = 'MissingRegistration'; + const ERROR_INVALID_REGISTRATION = 'InvalidRegistration'; + const ERROR_NOT_REGISTERED = 'NotRegistered'; + const ERROR_INVALID_PACKAGE_NAME = 'InvalidPackageName'; + const ERROR_MISMATCH_SENDER_ID = 'MismatchSenderId'; + const ERROR_MESSAGE_TOO_BIG = 'MessageTooBig'; + const ERROR_INVALID_DATA_KEY = 'InvalidDataKey'; + const ERROR_INVALID_TTL = 'InvalidTtl'; + const ERROR_UNAVAILABLE = 'Unavailable'; + const ERROR_INTERNAL_SERVER_ERROR = 'InternalServerError'; + const ERROR_DEVICE_MESSAGE_RATE_EXCEEDED = 'DeviceMessageRateExceeded'; + const ERROR_TOPICS_MESSAGE_RATE_EXCEEDED = 'TopicsMessageRateExceeded'; + /** * @var int */ @@ -138,7 +156,13 @@ public function getResponse() */ public function setResponse(array $response) { - if (!isset($response['results'], $response['success'], $response['failure'], $response['canonical_ids'], $response['multicast_id'])) { + if (! isset( + $response['results'], + $response['success'], + $response['failure'], + $response['canonical_ids'], + $response['multicast_id'] + )) { throw new Exception\InvalidArgumentException('Response did not contain the proper fields'); } diff --git a/tests/Gcm/ClientTest.php b/test/Gcm/ClientTest.php similarity index 87% rename from tests/Gcm/ClientTest.php rename to test/Gcm/ClientTest.php index a34d3de..f6ab6cd 100644 --- a/tests/Gcm/ClientTest.php +++ b/test/Gcm/ClientTest.php @@ -11,6 +11,7 @@ */ namespace ZendServiceTest\Google\Gcm; +use PHPUnit\Framework\TestCase; use Zend\Http\Client\Adapter\Test; use Zend\Http\Client as HttpClient; use ZendService\Google\Gcm\Client; @@ -22,7 +23,7 @@ * @group ZendService_Google * @group ZendService_Google_Gcm */ -class ClientTest extends \PHPUnit_Framework_TestCase +class ClientTest extends TestCase { /** * @var Test @@ -43,7 +44,7 @@ class ClientTest extends \PHPUnit_Framework_TestCase */ protected $message; - protected function _createJSONResponse($id, $success, $failure, $ids, $results) + protected function createJSONResponse($id, $success, $failure, $ids, $results) { return json_encode([ 'multicast_id' => $id, @@ -69,7 +70,7 @@ public function setUp() public function testSetApiKeyThrowsExceptionOnNonString() { - $this->setExpectedException('InvalidArgumentException'); + $this->expectException('InvalidArgumentException'); $this->gcmClient->setApiKey([]); } @@ -94,35 +95,35 @@ public function testSetHttpClient() public function testSendThrowsExceptionWhenServiceUnavailable() { - $this->setExpectedException('RuntimeException'); + $this->expectException('RuntimeException'); $this->httpAdapter->setResponse('HTTP/1.1 503 Service Unavailable'."\r\n\r\n"); $this->gcmClient->send($this->message); } public function testSendThrowsExceptionWhenServerUnavailable() { - $this->setExpectedException('RuntimeException'); + $this->expectException('RuntimeException'); $this->httpAdapter->setResponse('HTTP/1.1 500 Internal Server Error'."\r\n\r\n"); $this->gcmClient->send($this->message); } public function testSendThrowsExceptionWhenInvalidAuthToken() { - $this->setExpectedException('RuntimeException'); + $this->expectException('RuntimeException'); $this->httpAdapter->setResponse('HTTP/1.1 401 Unauthorized'."\r\n\r\n"); $this->gcmClient->send($this->message); } public function testSendThrowsExceptionWhenInvalidPayload() { - $this->setExpectedException('RuntimeException'); + $this->expectException('RuntimeException'); $this->httpAdapter->setResponse('HTTP/1.1 400 Bad Request'."\r\n\r\n"); $this->gcmClient->send($this->message); } public function testSendResultInvalidRegistrationId() { - $body = $this->_createJSONResponse(101, 0, 1, 0, [['error' => 'InvalidRegistration']]); + $body = $this->createJSONResponse(101, 0, 1, 0, [['error' => 'InvalidRegistration']]); $this->httpAdapter->setResponse( 'HTTP/1.1 200 OK'."\r\n". 'Context-Type: text/html'."\r\n\r\n". @@ -139,7 +140,7 @@ public function testSendResultInvalidRegistrationId() public function testSendResultMismatchSenderId() { - $body = $this->_createJSONResponse(101, 0, 1, 0, [['error' => 'MismatchSenderId']]); + $body = $this->createJSONResponse(101, 0, 1, 0, [['error' => 'MismatchSenderId']]); $this->httpAdapter->setResponse( 'HTTP/1.1 200 OK'."\r\n". 'Context-Type: text/html'."\r\n\r\n". @@ -156,7 +157,7 @@ public function testSendResultMismatchSenderId() public function testSendResultNotRegistered() { - $body = $this->_createJSONResponse(101, 0, 1, 0, [['error' => 'NotRegistered']]); + $body = $this->createJSONResponse(101, 0, 1, 0, [['error' => 'NotRegistered']]); $this->httpAdapter->setResponse( 'HTTP/1.1 200 OK'."\r\n". 'Context-Type: text/html'."\r\n\r\n". @@ -173,7 +174,7 @@ public function testSendResultNotRegistered() public function testSendResultMessageTooBig() { - $body = $this->_createJSONResponse(101, 0, 1, 0, [['error' => 'MessageTooBig']]); + $body = $this->createJSONResponse(101, 0, 1, 0, [['error' => 'MessageTooBig']]); $this->httpAdapter->setResponse( 'HTTP/1.1 200 OK'."\r\n". 'Context-Type: text/html'."\r\n\r\n". @@ -190,7 +191,7 @@ public function testSendResultMessageTooBig() public function testSendResultSuccessful() { - $body = $this->_createJSONResponse(101, 1, 0, 0, [['message_id' => '1:2342']]); + $body = $this->createJSONResponse(101, 1, 0, 0, [['message_id' => '1:2342']]); $this->httpAdapter->setResponse( 'HTTP/1.1 200 OK'."\r\n". 'Context-Type: text/html'."\r\n\r\n". @@ -207,7 +208,7 @@ public function testSendResultSuccessful() public function testSendResultSuccessfulWithRegistrationId() { - $body = $this->_createJSONResponse(101, 1, 0, 1, [['message_id' => '1:2342', 'registration_id' => 'testfoo']]); + $body = $this->createJSONResponse(101, 1, 0, 1, [['message_id' => '1:2342', 'registration_id' => 'testfoo']]); $this->httpAdapter->setResponse( 'HTTP/1.1 200 OK'."\r\n". 'Context-Type: text/html'."\r\n\r\n". diff --git a/tests/Gcm/MessageTest.php b/test/Gcm/MessageTest.php similarity index 91% rename from tests/Gcm/MessageTest.php rename to test/Gcm/MessageTest.php index f9729a4..496c5b3 100644 --- a/tests/Gcm/MessageTest.php +++ b/test/Gcm/MessageTest.php @@ -11,6 +11,7 @@ */ namespace ZendServiceTest\Google\Gcm; +use PHPUnit\Framework\TestCase; use ZendService\Google\Gcm\Message; /** @@ -19,7 +20,7 @@ * @group ZendService_Google * @group ZendService_Google_Gcm */ -class MessageTest extends \PHPUnit_Framework_TestCase +class MessageTest extends TestCase { protected $validRegistrationIds = [ '1234567890', @@ -64,7 +65,7 @@ public function testExpectedRegistrationIdBehavior() public function testInvalidRegistrationIdThrowsException() { - $this->setExpectedException(\InvalidArgumentException::class); + $this->expectException(\InvalidArgumentException::class); $this->m->addRegistrationId(['1234']); } @@ -82,7 +83,7 @@ public function testExpectedCollapseKeyBehavior() public function testInvalidCollapseKeyThrowsException() { - $this->setExpectedException(\InvalidArgumentException::class); + $this->expectException(\InvalidArgumentException::class); $this->m->setCollapseKey(['1234']); } @@ -103,28 +104,28 @@ public function testExpectedDataBehavior() public function testExpectedNotificationBehavior() { - $this->assertEquals($this->m->getNotification(), array()); + $this->assertEquals($this->m->getNotification(), []); $this->assertNotContains('notification', $this->m->toJson()); $this->m->setNotification($this->validData); $this->assertEquals($this->m->getNotification(), $this->validData); $this->assertContains('notification', $this->m->toJson()); $this->m->clearNotification(); - $this->assertEquals($this->m->getNotification(), array()); + $this->assertEquals($this->m->getNotification(), []); $this->assertNotContains('notification', $this->m->toJson()); $this->m->addNotification('mykey', 'myvalue'); - $this->assertEquals($this->m->getNotification(), array('mykey' => 'myvalue')); + $this->assertEquals($this->m->getNotification(), ['mykey' => 'myvalue']); $this->assertContains('notification', $this->m->toJson()); } public function testInvalidDataThrowsException() { - $this->setExpectedException(\InvalidArgumentException::class); + $this->expectException(\InvalidArgumentException::class); $this->m->addData(['1234'], 'value'); } public function testDuplicateDataKeyThrowsException() { - $this->setExpectedException(\RuntimeException::class); + $this->expectException(\RuntimeException::class); $this->m->setData($this->validData); $this->m->addData('key', 'value'); } @@ -167,7 +168,7 @@ public function testExpectedRestrictedPackageBehavior() public function testInvalidRestrictedPackageThrowsException() { - $this->setExpectedException(\InvalidArgumentException::class); + $this->expectException(\InvalidArgumentException::class); $this->m->setRestrictedPackageName(['1234']); } diff --git a/tests/Gcm/ResponseTest.php b/test/Gcm/ResponseTest.php similarity index 94% rename from tests/Gcm/ResponseTest.php rename to test/Gcm/ResponseTest.php index 47ddc6c..14e1a43 100644 --- a/tests/Gcm/ResponseTest.php +++ b/test/Gcm/ResponseTest.php @@ -11,6 +11,7 @@ */ namespace ZendServiceTest\Google\Gcm; +use PHPUnit\Framework\TestCase; use ZendService\Google\Gcm\Message; use ZendService\Google\Gcm\Response; @@ -20,7 +21,7 @@ * @group ZendService_Google * @group ZendService_Google_Gcm */ -class ResponseTest extends \PHPUnit_Framework_TestCase +class ResponseTest extends TestCase { /** * @var Message @@ -64,7 +65,7 @@ public function testInvalidConstructorThrowsException() self::markTestSkipped('PHP 7 required.'); } - $this->setExpectedException(\TypeError::class); + $this->expectException(\TypeError::class); new Response('{bad'); } @@ -74,7 +75,7 @@ public function testInvalidConstructorThrowsExceptionOnPhp7() self::markTestSkipped('PHP >=5.5 required.'); } - $this->setExpectedException(\PHPUnit_Framework_Error::class); + $this->expectException(\PHPUnit_Framework_Error::class); new Response('{bad'); }