From 7be637139fe54ec23f37c8dba519bafa7543e336 Mon Sep 17 00:00:00 2001 From: Edwin Hoksberg Date: Mon, 9 Apr 2018 21:32:41 +0200 Subject: [PATCH] Initial commit --- LICENSE.md | 21 + README.md | 20 + composer.json | 27 + composer.lock | 1822 +++++++++++++++++++++++ docs/Makefile | 20 + docs/_build/.gitkeep | 0 docs/conf.py | 158 ++ docs/device.rst | 58 + docs/devicegroup.rst | 86 ++ docs/index.rst | 43 + docs/integration.rst | 18 + docs/message.rst | 94 ++ docs/overview.rst | 45 + docs/quickstart.rst | 36 + docs/topic.rst | 75 + phpunit.dist.xml | 23 + src/Device/Info.php | 50 + src/DeviceGroup/Create.php | 40 + src/DeviceGroup/DeviceGroup.php | 37 + src/DeviceGroup/Remove.php | 48 + src/DeviceGroup/Update.php | 48 + src/Exception/FcmClientException.php | 10 + src/Exception/NotificationException.php | 10 + src/Exception/TopicException.php | 10 + src/FcmClient.php | 125 ++ src/Push/Data.php | 72 + src/Push/Notification.php | 105 ++ src/Push/Push.php | 78 + src/Request.php | 16 + src/Topic/Subscribe.php | 47 + src/Topic/Topic.php | 35 + src/Topic/Unsubscribe.php | 47 + tests/ClientTests/FcmClientTest.php | 83 ++ tests/DeviceGroupTests/CreateTest.php | 63 + tests/DeviceGroupTests/RemoveTest.php | 69 + tests/DeviceGroupTests/UpdateTest.php | 69 + tests/DeviceTests/InfoTest.php | 28 + tests/PushTests/DataTest.php | 151 ++ tests/PushTests/NotificationTest.php | 151 ++ tests/TopicTests/SubscribeTest.php | 70 + tests/TopicTests/UnsubscribeTest.php | 70 + 41 files changed, 4078 insertions(+) create mode 100644 LICENSE.md create mode 100644 README.md create mode 100644 composer.json create mode 100644 composer.lock create mode 100644 docs/Makefile create mode 100644 docs/_build/.gitkeep create mode 100644 docs/conf.py create mode 100644 docs/device.rst create mode 100644 docs/devicegroup.rst create mode 100644 docs/index.rst create mode 100644 docs/integration.rst create mode 100644 docs/message.rst create mode 100644 docs/overview.rst create mode 100644 docs/quickstart.rst create mode 100644 docs/topic.rst create mode 100644 phpunit.dist.xml create mode 100644 src/Device/Info.php create mode 100644 src/DeviceGroup/Create.php create mode 100644 src/DeviceGroup/DeviceGroup.php create mode 100644 src/DeviceGroup/Remove.php create mode 100644 src/DeviceGroup/Update.php create mode 100644 src/Exception/FcmClientException.php create mode 100644 src/Exception/NotificationException.php create mode 100644 src/Exception/TopicException.php create mode 100644 src/FcmClient.php create mode 100644 src/Push/Data.php create mode 100644 src/Push/Notification.php create mode 100644 src/Push/Push.php create mode 100644 src/Request.php create mode 100644 src/Topic/Subscribe.php create mode 100644 src/Topic/Topic.php create mode 100644 src/Topic/Unsubscribe.php create mode 100644 tests/ClientTests/FcmClientTest.php create mode 100644 tests/DeviceGroupTests/CreateTest.php create mode 100644 tests/DeviceGroupTests/RemoveTest.php create mode 100644 tests/DeviceGroupTests/UpdateTest.php create mode 100644 tests/DeviceTests/InfoTest.php create mode 100644 tests/PushTests/DataTest.php create mode 100644 tests/PushTests/NotificationTest.php create mode 100644 tests/TopicTests/SubscribeTest.php create mode 100644 tests/TopicTests/UnsubscribeTest.php diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..686e9d9 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 Edwin Hoksberg + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..6e5e06b --- /dev/null +++ b/README.md @@ -0,0 +1,20 @@ +# PHP-FCM [![Build Status](https://travis-ci.org/EdwinHoksberg/php-fcm.svg?branch=master)](https://travis-ci.org/EdwinHoksberg/php-fcm) [![Documentation](https://readthedocs.org/projects/php-fcm/badge/?version=latest)](https://php-fcm.readthedocs.io/en/latest/) +A PHP library for sending Firebase Cloud Messages and managing user topic subscriptions, device groups and devices. + +## Installation +Installation with composer: +```bash +composer require edwinhoksberg/php-fcm +``` + +## Documentation +Read the documentation [here](https://php-fcm.readthedocs.io/en/latest/) or look in the [docs](docs/) directory. + +## Tests +Run the unit tests with PHPUnit: +```bash +vendor/bin/phpunit -c phpunit.dist.xml +``` + +## License +[MIT](LICENSE.md) diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..ddcbec0 --- /dev/null +++ b/composer.json @@ -0,0 +1,27 @@ +{ + "name": "edwinhoksberg/php-fcm", + "description": "A library for sending Firebase cloud messages and managing user topic subscriptions, device groups and devices.", + "keywords": ["firebase cloud messaging", "fcm", "notifications", "firebase", "google"], + "type": "library", + "license": "MIT", + "homepage": "https://github.com/EdwinHoksberg/php-fcm", + "authors": [ + { + "name": "Edwin Hoksberg", + "email": "mail@edwinhoksberg.nl" + } + ], + "autoload": { + "psr-4": { + "Fcm\\": "src/" + } + }, + "require": { + "php": ">= 7.0", + "guzzlehttp/guzzle": "^6.3" + }, + "require-dev": { + "phpunit/phpunit": "^6.5", + "mockery/mockery": "^1.0" + } +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..6129c25 --- /dev/null +++ b/composer.lock @@ -0,0 +1,1822 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "This file is @generated automatically" + ], + "content-hash": "57e7c7e5cc409d0d7760a4f7ceaff8a6", + "packages": [ + { + "name": "guzzlehttp/guzzle", + "version": "6.3.2", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle.git", + "reference": "68d0ea14d5a3f42a20e87632a5f84931e2709c90" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/68d0ea14d5a3f42a20e87632a5f84931e2709c90", + "reference": "68d0ea14d5a3f42a20e87632a5f84931e2709c90", + "shasum": "" + }, + "require": { + "guzzlehttp/promises": "^1.0", + "guzzlehttp/psr7": "^1.4", + "php": ">=5.5" + }, + "require-dev": { + "ext-curl": "*", + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4", + "psr/log": "^1.0" + }, + "suggest": { + "psr/log": "Required for using the Log middleware" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "6.3-dev" + } + }, + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "GuzzleHttp\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "Guzzle is a PHP HTTP client library", + "homepage": "http://guzzlephp.org/", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "rest", + "web service" + ], + "time": "2018-03-26T16:33:04+00:00" + }, + { + "name": "guzzlehttp/promises", + "version": "v1.3.1", + "source": { + "type": "git", + "url": "https://github.com/guzzle/promises.git", + "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/promises/zipball/a59da6cf61d80060647ff4d3eb2c03a2bc694646", + "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646", + "shasum": "" + }, + "require": { + "php": ">=5.5.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4-dev" + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Promise\\": "src/" + }, + "files": [ + "src/functions_include.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "Guzzle promises library", + "keywords": [ + "promise" + ], + "time": "2016-12-20T10:07:11+00:00" + }, + { + "name": "guzzlehttp/psr7", + "version": "1.4.2", + "source": { + "type": "git", + "url": "https://github.com/guzzle/psr7.git", + "reference": "f5b8a8512e2b58b0071a7280e39f14f72e05d87c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/f5b8a8512e2b58b0071a7280e39f14f72e05d87c", + "reference": "f5b8a8512e2b58b0071a7280e39f14f72e05d87c", + "shasum": "" + }, + "require": { + "php": ">=5.4.0", + "psr/http-message": "~1.0" + }, + "provide": { + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4-dev" + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Psr7\\": "src/" + }, + "files": [ + "src/functions_include.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Tobias Schultze", + "homepage": "https://github.com/Tobion" + } + ], + "description": "PSR-7 message implementation that also provides common utility methods", + "keywords": [ + "http", + "message", + "request", + "response", + "stream", + "uri", + "url" + ], + "time": "2017-03-20T17:10:46+00:00" + }, + { + "name": "psr/http-message", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "time": "2016-08-06T14:39:51+00:00" + } + ], + "packages-dev": [ + { + "name": "doctrine/instantiator", + "version": "1.0.5", + "source": { + "type": "git", + "url": "https://github.com/doctrine/instantiator.git", + "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d", + "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d", + "shasum": "" + }, + "require": { + "php": ">=5.3,<8.0-DEV" + }, + "require-dev": { + "athletic/athletic": "~0.1.8", + "ext-pdo": "*", + "ext-phar": "*", + "phpunit/phpunit": "~4.0", + "squizlabs/php_codesniffer": "~2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "http://ocramius.github.com/" + } + ], + "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", + "homepage": "https://github.com/doctrine/instantiator", + "keywords": [ + "constructor", + "instantiate" + ], + "time": "2015-06-14T21:17:01+00:00" + }, + { + "name": "hamcrest/hamcrest-php", + "version": "v2.0.0", + "source": { + "type": "git", + "url": "https://github.com/hamcrest/hamcrest-php.git", + "reference": "776503d3a8e85d4f9a1148614f95b7a608b046ad" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/776503d3a8e85d4f9a1148614f95b7a608b046ad", + "reference": "776503d3a8e85d4f9a1148614f95b7a608b046ad", + "shasum": "" + }, + "require": { + "php": "^5.3|^7.0" + }, + "replace": { + "cordoval/hamcrest-php": "*", + "davedevelopment/hamcrest-php": "*", + "kodova/hamcrest-php": "*" + }, + "require-dev": { + "phpunit/php-file-iterator": "1.3.3", + "phpunit/phpunit": "~4.0", + "satooshi/php-coveralls": "^1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "hamcrest" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD" + ], + "description": "This is the PHP port of Hamcrest Matchers", + "keywords": [ + "test" + ], + "time": "2016-01-20T08:20:44+00:00" + }, + { + "name": "mockery/mockery", + "version": "1.0", + "source": { + "type": "git", + "url": "https://github.com/mockery/mockery.git", + "reference": "1bac8c362b12f522fdd1f1fa3556284c91affa38" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mockery/mockery/zipball/1bac8c362b12f522fdd1f1fa3556284c91affa38", + "reference": "1bac8c362b12f522fdd1f1fa3556284c91affa38", + "shasum": "" + }, + "require": { + "hamcrest/hamcrest-php": "~2.0", + "lib-pcre": ">=7.0", + "php": ">=5.6.0" + }, + "require-dev": { + "phpunit/phpunit": "~5.7|~6.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-0": { + "Mockery": "library/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Padraic Brady", + "email": "padraic.brady@gmail.com", + "homepage": "http://blog.astrumfutura.com" + }, + { + "name": "Dave Marshall", + "email": "dave.marshall@atstsolutions.co.uk", + "homepage": "http://davedevelopment.co.uk" + } + ], + "description": "Mockery is a simple yet flexible PHP mock object framework for use in unit testing with PHPUnit, PHPSpec or any other testing framework. Its core goal is to offer a test double framework with a succinct API capable of clearly defining all possible object operations and interactions using a human readable Domain Specific Language (DSL). Designed as a drop in alternative to PHPUnit's phpunit-mock-objects library, Mockery is easy to integrate with PHPUnit and can operate alongside phpunit-mock-objects without the World ending.", + "homepage": "http://github.com/mockery/mockery", + "keywords": [ + "BDD", + "TDD", + "library", + "mock", + "mock objects", + "mockery", + "stub", + "test", + "test double", + "testing" + ], + "time": "2017-10-06T16:20:43+00:00" + }, + { + "name": "myclabs/deep-copy", + "version": "1.7.0", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e", + "reference": "3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e", + "shasum": "" + }, + "require": { + "php": "^5.6 || ^7.0" + }, + "require-dev": { + "doctrine/collections": "^1.0", + "doctrine/common": "^2.6", + "phpunit/phpunit": "^4.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + }, + "files": [ + "src/DeepCopy/deep_copy.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "time": "2017-10-19T19:58:43+00:00" + }, + { + "name": "phar-io/manifest", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "2df402786ab5368a0169091f61a7c1e0eb6852d0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/2df402786ab5368a0169091f61a7c1e0eb6852d0", + "reference": "2df402786ab5368a0169091f61a7c1e0eb6852d0", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-phar": "*", + "phar-io/version": "^1.0.1", + "php": "^5.6 || ^7.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "time": "2017-03-05T18:14:27+00:00" + }, + { + "name": "phar-io/version", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "a70c0ced4be299a63d32fa96d9281d03e94041df" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/a70c0ced4be299a63d32fa96d9281d03e94041df", + "reference": "a70c0ced4be299a63d32fa96d9281d03e94041df", + "shasum": "" + }, + "require": { + "php": "^5.6 || ^7.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "time": "2017-03-05T17:38:23+00:00" + }, + { + "name": "phpdocumentor/reflection-common", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionCommon.git", + "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", + "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", + "shasum": "" + }, + "require": { + "php": ">=5.5" + }, + "require-dev": { + "phpunit/phpunit": "^4.6" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": [ + "src" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "Common reflection classes used by phpdocumentor to reflect the code structure", + "homepage": "http://www.phpdoc.org", + "keywords": [ + "FQSEN", + "phpDocumentor", + "phpdoc", + "reflection", + "static analysis" + ], + "time": "2017-09-11T18:02:19+00:00" + }, + { + "name": "phpdocumentor/reflection-docblock", + "version": "4.3.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "94fd0001232e47129dd3504189fa1c7225010d08" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/94fd0001232e47129dd3504189fa1c7225010d08", + "reference": "94fd0001232e47129dd3504189fa1c7225010d08", + "shasum": "" + }, + "require": { + "php": "^7.0", + "phpdocumentor/reflection-common": "^1.0.0", + "phpdocumentor/type-resolver": "^0.4.0", + "webmozart/assert": "^1.0" + }, + "require-dev": { + "doctrine/instantiator": "~1.0.5", + "mockery/mockery": "^1.0", + "phpunit/phpunit": "^6.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "time": "2017-11-30T07:14:17+00:00" + }, + { + "name": "phpdocumentor/type-resolver", + "version": "0.4.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/TypeResolver.git", + "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/9c977708995954784726e25d0cd1dddf4e65b0f7", + "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7", + "shasum": "" + }, + "require": { + "php": "^5.5 || ^7.0", + "phpdocumentor/reflection-common": "^1.0" + }, + "require-dev": { + "mockery/mockery": "^0.9.4", + "phpunit/phpunit": "^5.2||^4.8.24" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "time": "2017-07-14T14:27:02+00:00" + }, + { + "name": "phpspec/prophecy", + "version": "1.7.5", + "source": { + "type": "git", + "url": "https://github.com/phpspec/prophecy.git", + "reference": "dfd6be44111a7c41c2e884a336cc4f461b3b2401" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/dfd6be44111a7c41c2e884a336cc4f461b3b2401", + "reference": "dfd6be44111a7c41c2e884a336cc4f461b3b2401", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.0.2", + "php": "^5.3|^7.0", + "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0", + "sebastian/comparator": "^1.1|^2.0", + "sebastian/recursion-context": "^1.0|^2.0|^3.0" + }, + "require-dev": { + "phpspec/phpspec": "^2.5|^3.2", + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.7.x-dev" + } + }, + "autoload": { + "psr-0": { + "Prophecy\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Konstantin Kudryashov", + "email": "ever.zet@gmail.com", + "homepage": "http://everzet.com" + }, + { + "name": "Marcello Duarte", + "email": "marcello.duarte@gmail.com" + } + ], + "description": "Highly opinionated mocking framework for PHP 5.3+", + "homepage": "https://github.com/phpspec/prophecy", + "keywords": [ + "Double", + "Dummy", + "fake", + "mock", + "spy", + "stub" + ], + "time": "2018-02-19T10:16:54+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "5.3.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "661f34d0bd3f1a7225ef491a70a020ad23a057a1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/661f34d0bd3f1a7225ef491a70a020ad23a057a1", + "reference": "661f34d0bd3f1a7225ef491a70a020ad23a057a1", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-xmlwriter": "*", + "php": "^7.0", + "phpunit/php-file-iterator": "^1.4.2", + "phpunit/php-text-template": "^1.2.1", + "phpunit/php-token-stream": "^2.0.1", + "sebastian/code-unit-reverse-lookup": "^1.0.1", + "sebastian/environment": "^3.0", + "sebastian/version": "^2.0.1", + "theseer/tokenizer": "^1.1" + }, + "require-dev": { + "phpunit/phpunit": "^6.0" + }, + "suggest": { + "ext-xdebug": "^2.5.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.3.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "time": "2017-12-06T09:29:45+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "1.4.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/730b01bc3e867237eaac355e06a36b85dd93a8b4", + "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "time": "2017-11-27T13:52:08+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "1.2.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", + "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "time": "2015-06-21T13:50:34+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "1.0.9", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", + "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", + "shasum": "" + }, + "require": { + "php": "^5.3.3 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "time": "2017-02-26T11:10:40+00:00" + }, + { + "name": "phpunit/php-token-stream", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-token-stream.git", + "reference": "791198a2c6254db10131eecfe8c06670700904db" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/791198a2c6254db10131eecfe8c06670700904db", + "reference": "791198a2c6254db10131eecfe8c06670700904db", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": "^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.2.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Wrapper around PHP's tokenizer extension.", + "homepage": "https://github.com/sebastianbergmann/php-token-stream/", + "keywords": [ + "tokenizer" + ], + "time": "2017-11-27T05:48:46+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "6.5.7", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "6bd77b57707c236833d2b57b968e403df060c9d9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/6bd77b57707c236833d2b57b968e403df060c9d9", + "reference": "6bd77b57707c236833d2b57b968e403df060c9d9", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "myclabs/deep-copy": "^1.6.1", + "phar-io/manifest": "^1.0.1", + "phar-io/version": "^1.0", + "php": "^7.0", + "phpspec/prophecy": "^1.7", + "phpunit/php-code-coverage": "^5.3", + "phpunit/php-file-iterator": "^1.4.3", + "phpunit/php-text-template": "^1.2.1", + "phpunit/php-timer": "^1.0.9", + "phpunit/phpunit-mock-objects": "^5.0.5", + "sebastian/comparator": "^2.1", + "sebastian/diff": "^2.0", + "sebastian/environment": "^3.1", + "sebastian/exporter": "^3.1", + "sebastian/global-state": "^2.0", + "sebastian/object-enumerator": "^3.0.3", + "sebastian/resource-operations": "^1.0", + "sebastian/version": "^2.0.1" + }, + "conflict": { + "phpdocumentor/reflection-docblock": "3.0.2", + "phpunit/dbunit": "<3.0" + }, + "require-dev": { + "ext-pdo": "*" + }, + "suggest": { + "ext-xdebug": "*", + "phpunit/php-invoker": "^1.1" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "6.5.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "time": "2018-02-26T07:01:09+00:00" + }, + { + "name": "phpunit/phpunit-mock-objects", + "version": "5.0.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", + "reference": "33fd41a76e746b8fa96d00b49a23dadfa8334cdf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/33fd41a76e746b8fa96d00b49a23dadfa8334cdf", + "reference": "33fd41a76e746b8fa96d00b49a23dadfa8334cdf", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.0.5", + "php": "^7.0", + "phpunit/php-text-template": "^1.2.1", + "sebastian/exporter": "^3.1" + }, + "conflict": { + "phpunit/phpunit": "<6.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.5" + }, + "suggest": { + "ext-soap": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Mock Object library for PHPUnit", + "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", + "keywords": [ + "mock", + "xunit" + ], + "time": "2018-01-06T05:45:45+00:00" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", + "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", + "shasum": "" + }, + "require": { + "php": "^5.6 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^5.7 || ^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "time": "2017-03-04T06:30:41+00:00" + }, + { + "name": "sebastian/comparator", + "version": "2.1.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "34369daee48eafb2651bea869b4b15d75ccc35f9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/34369daee48eafb2651bea869b4b15d75ccc35f9", + "reference": "34369daee48eafb2651bea869b4b15d75ccc35f9", + "shasum": "" + }, + "require": { + "php": "^7.0", + "sebastian/diff": "^2.0 || ^3.0", + "sebastian/exporter": "^3.1" + }, + "require-dev": { + "phpunit/phpunit": "^6.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.1.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "time": "2018-02-01T13:46:46+00:00" + }, + { + "name": "sebastian/diff", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "347c1d8b49c5c3ee30c7040ea6fc446790e6bddd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/347c1d8b49c5c3ee30c7040ea6fc446790e6bddd", + "reference": "347c1d8b49c5c3ee30c7040ea6fc446790e6bddd", + "shasum": "" + }, + "require": { + "php": "^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff" + ], + "time": "2017-08-03T08:09:46+00:00" + }, + { + "name": "sebastian/environment", + "version": "3.1.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "cd0871b3975fb7fc44d11314fd1ee20925fce4f5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/cd0871b3975fb7fc44d11314fd1ee20925fce4f5", + "reference": "cd0871b3975fb7fc44d11314fd1ee20925fce4f5", + "shasum": "" + }, + "require": { + "php": "^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "http://www.github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "time": "2017-07-01T08:51:00+00:00" + }, + { + "name": "sebastian/exporter", + "version": "3.1.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "234199f4528de6d12aaa58b612e98f7d36adb937" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/234199f4528de6d12aaa58b612e98f7d36adb937", + "reference": "234199f4528de6d12aaa58b612e98f7d36adb937", + "shasum": "" + }, + "require": { + "php": "^7.0", + "sebastian/recursion-context": "^3.0" + }, + "require-dev": { + "ext-mbstring": "*", + "phpunit/phpunit": "^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "http://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "time": "2017-04-03T13:19:02+00:00" + }, + { + "name": "sebastian/global-state", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", + "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", + "shasum": "" + }, + "require": { + "php": "^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.0" + }, + "suggest": { + "ext-uopz": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "http://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "time": "2017-04-27T15:39:26+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/7cfd9e65d11ffb5af41198476395774d4c8a84c5", + "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5", + "shasum": "" + }, + "require": { + "php": "^7.0", + "sebastian/object-reflector": "^1.1.1", + "sebastian/recursion-context": "^3.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "time": "2017-08-03T12:35:26+00:00" + }, + { + "name": "sebastian/object-reflector", + "version": "1.1.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "773f97c67f28de00d397be301821b06708fca0be" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/773f97c67f28de00d397be301821b06708fca0be", + "reference": "773f97c67f28de00d397be301821b06708fca0be", + "shasum": "" + }, + "require": { + "php": "^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "time": "2017-03-29T09:07:27+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", + "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", + "shasum": "" + }, + "require": { + "php": "^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "http://www.github.com/sebastianbergmann/recursion-context", + "time": "2017-03-03T06:23:57+00:00" + }, + { + "name": "sebastian/resource-operations", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/resource-operations.git", + "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/ce990bb21759f94aeafd30209e8cfcdfa8bc3f52", + "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52", + "shasum": "" + }, + "require": { + "php": ">=5.6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides a list of PHP built-in functions that operate on resources", + "homepage": "https://www.github.com/sebastianbergmann/resource-operations", + "time": "2015-07-28T20:34:47+00:00" + }, + { + "name": "sebastian/version", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/99732be0ddb3361e16ad77b68ba41efc8e979019", + "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019", + "shasum": "" + }, + "require": { + "php": ">=5.6" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "time": "2016-10-03T07:35:21+00:00" + }, + { + "name": "theseer/tokenizer", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/theseer/tokenizer.git", + "reference": "cb2f008f3f05af2893a87208fe6a6c4985483f8b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/cb2f008f3f05af2893a87208fe6a6c4985483f8b", + "reference": "cb2f008f3f05af2893a87208fe6a6c4985483f8b", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^7.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + } + ], + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "time": "2017-04-07T12:08:54+00:00" + }, + { + "name": "webmozart/assert", + "version": "1.3.0", + "source": { + "type": "git", + "url": "https://github.com/webmozart/assert.git", + "reference": "0df1908962e7a3071564e857d86874dad1ef204a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozart/assert/zipball/0df1908962e7a3071564e857d86874dad1ef204a", + "reference": "0df1908962e7a3071564e857d86874dad1ef204a", + "shasum": "" + }, + "require": { + "php": "^5.3.3 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.6", + "sebastian/version": "^1.0.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3-dev" + } + }, + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], + "time": "2018-01-29T19:49:41+00:00" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": ">= 7.0" + }, + "platform-dev": [] +} diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..2c3b8f5 --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +SPHINXPROJ = PHP-FCM +SOURCEDIR = . +BUILDDIR = _build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) \ No newline at end of file diff --git a/docs/_build/.gitkeep b/docs/_build/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000..44a331b --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,158 @@ +# -*- coding: utf-8 -*- +# +# Configuration file for the Sphinx documentation builder. +# +# This file does only contain a selection of the most common options. For a +# full list see the documentation: +# http://www.sphinx-doc.org/en/stable/config + +# -- Path setup -------------------------------------------------------------- + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +# import os +# import sys +# sys.path.insert(0, os.path.abspath('.')) + +# -- Project information ----------------------------------------------------- + +project = 'PHP-FCM' +copyright = '2018, Edwin Hoksberg' +author = 'Edwin Hoksberg' + +# The short X.Y version +version = '' +# The full version, including alpha/beta/rc tags +release = '0.0.1' + + +# -- General configuration --------------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. +# +# needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + 'sphinx.ext.githubpages', +] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +# +# source_suffix = ['.rst', '.md'] +source_suffix = '.rst' + +# The master toctree document. +master_doc = 'index' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +language = None + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path . +exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + + +# -- Options for HTML output ------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = 'sphinx_rtd_theme' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +# +# html_theme_options = {} + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# Custom sidebar templates, must be a dictionary that maps document names +# to template names. +# +# The default sidebars (for documents that don't match any pattern) are +# defined by theme itself. Builtin themes are using these templates by +# default: ``['localtoc.html', 'relations.html', 'sourcelink.html', +# 'searchbox.html']``. +# +# html_sidebars = {} + + +# -- Options for HTMLHelp output --------------------------------------------- + +# Output file base name for HTML help builder. +htmlhelp_basename = 'PHP-FCMdoc' + + +# -- Options for LaTeX output ------------------------------------------------ + +latex_elements = { + # The paper size ('letterpaper' or 'a4paper'). + # + # 'papersize': 'letterpaper', + + # The font size ('10pt', '11pt' or '12pt'). + # + # 'pointsize': '10pt', + + # Additional stuff for the LaTeX preamble. + # + # 'preamble': '', + + # Latex figure (float) alignment + # + # 'figure_align': 'htbp', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + (master_doc, 'PHP-FCM.tex', 'PHP-FCM Documentation', + 'Edwin Hoksberg', 'manual'), +] + + +# -- Options for manual page output ------------------------------------------ + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + (master_doc, 'php-fcm', 'PHP-FCM Documentation', + [author], 1) +] + + +# -- Options for Texinfo output ---------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + (master_doc, 'PHP-FCM', 'PHP-FCM Documentation', + author, 'PHP-FCM', 'One line description of project.', + 'Miscellaneous'), +] + + +# -- Extension configuration ------------------------------------------------- diff --git a/docs/device.rst b/docs/device.rst new file mode 100644 index 0000000..c949cf1 --- /dev/null +++ b/docs/device.rst @@ -0,0 +1,58 @@ +================== +Device Information +================== + +There is only 1 device call Firebase exposes, and it can be used to retrieve information about a single device, such as registration date and subscribed topics. + +Device Info +=========== + +.. code-block:: php + + deviceInfo($deviceId, true); + + $client->send($info); + +Example response: + +.. code-block:: text + + array(10) { + 'applicationVersion' => + string(1) "1" + 'connectDate' => + string(10) "2018-08-07" + 'attestStatus' => + string(6) "ROOTED" + 'application' => + string(27) "com.google.application" + 'scope' => + string(1) "*" + 'authorizedEntity' => + string(12) "347372151029" + 'rel' => + array(1) { + 'topics' => + array(1) { + 'news' => + array(1) { + 'addDate' => + string(10) "2018-08-07" + } + } + } + 'connectionType' => + string(4) "WIFI" + 'appSigner' => + string(40) "c5abd4420a7b4844c034fe9c47fcb42234bbf5fe" + 'platform' => + string(7) "ANDROID" + } diff --git a/docs/devicegroup.rst b/docs/devicegroup.rst new file mode 100644 index 0000000..3b9278f --- /dev/null +++ b/docs/devicegroup.rst @@ -0,0 +1,86 @@ +====================== +Managing Device Groups +====================== + +`With device group messaging, you can send a single message to multiple instances of an app running on devices belonging to a group. Typically, "group" refers a set of different devices that belong to a single user. All devices in a group share a common notification key, which is the token that FCM uses to fan out messages to all devices in the group.` +`Read more on at official documentation. `_ + +Creating a device group +======================= + +When creating a new group, the response will contain a `notification key`, which will be used when adding or removing devices from the group, and sending messages to it. + +.. code-block:: php + + addDevice($deviceId); + + // Shortcut function: + // $client->deviceGroupCreate('phones', $deviceId); + + $client->send($newGroup); + +Example response: + +.. code-block:: text + + array(1) { + 'notification_key' => + string(119) "APA91bE8asD44A2gjSUJqRp8Ym4pe7TlrlrSLVkKRBdvkoWOFmusdc87934ASDURl8xaUbXXdKC5DRkUssYtkOl_lnWXT7gF0vO9E666XeL1qJs02FsunJ4" + } + +Adding devices to a group +========================= + +.. code-block:: php + + addDevice($deviceId); + + // Shortcut function: + // $client->deviceGroupUpdate('phones', $notification_key, $deviceId); + + $client->send($group); + +Example response: + +.. code-block:: text + + array(1) { + 'notification_key' => + string(119) "APA91bE8asD44A2gjSUJqRp8Ym4pe7TlrlrSLVkKRBdvkoWOFmusdc87934ASDURl8xaUbXXdKC5DRkUssYtkOl_lnWXT7gF0vO9E666XeL1qJs02FsunJ4" + } + +Removing devies from a group +============================ + +.. code-block:: php + + addDevice($deviceId); + + // Shortcut function: + // $client->deviceGroupRemove('phones', $notification_key, $deviceId); + + $client->send($group); + +Example response: + +.. code-block:: text + + array(1) { + 'notification_key' => + string(119) "APA91bE8asD44A2gjSUJqRp8Ym4pe7TlrlrSLVkKRBdvkoWOFmusdc87934ASDURl8xaUbXXdKC5DRkUssYtkOl_lnWXT7gF0vO9E666XeL1qJs02FsunJ4" + } diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000..6928d2f --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,43 @@ +.. title:: PHP-FCM, Firebase Cloud Messaging client + +===================== +PHP-FCM Documentation +===================== + +PHP-FCM is a PHP HTTP client that makes it easy to send push notifications, +manage groups of devices and message topics using Google Firebase Cloud Messaging. + +.. code-block:: php + + addRecipient($deviceId) + ->setTitle('Hello from php-fcm!') + ->setBody('Notification body') + ->addData('key', 'value'); + + // Send the notification to the Firebase servers for further handling. + $client->send($notification); + + +Before installing, read the :doc:`overview` for more information about this project. +Read the :doc:`quickstart` for installation and using the project. + +.. toctree:: + :maxdepth: 2 + + overview + quickstart + integration + device + message + devicegroup + topic diff --git a/docs/integration.rst b/docs/integration.rst new file mode 100644 index 0000000..5142a43 --- /dev/null +++ b/docs/integration.rst @@ -0,0 +1,18 @@ +=============== +App integration +=============== + +Android Integration +=================== + +The easiest way to start is using the offcial example messaging app which can be found `here `_. +It is also documented on how to register a device and how to capture the device id, which will be used for sending notifications or managing device groups/topics. + +Official documentation: ``_. + +iOS Integration +=============== + +An example application was also made available for iOS, which can be found on the official `github repository `_. + +Official documentation: ``_. diff --git a/docs/message.rst b/docs/message.rst new file mode 100644 index 0000000..485146a --- /dev/null +++ b/docs/message.rst @@ -0,0 +1,94 @@ +================ +Sending Messages +================ + +There are two different types of messages Firebase can support, `Notification messages` and `Data messages`. +Read `here `_ for more information. + +Notification message +==================== + +`FCM automatically displays the message to end-user devices on behalf of the client app. Notification messages have a predefined set of user-visible keys and an optional data payload of custom key-value pairs.` + +.. code-block:: php + + setTitle('Hello from php-fcm!') + ->addRecipient($deviceId); + + // Shortcut function: + // $notification = $client->pushNotification('The title', 'The body', $deviceId); + + $response = $client->send($notification); + +Example response: + +.. code-block:: text + + array(5) { + 'multicast_id' => + int(9014353506250345342) + 'success' => + int(1) + 'failure' => + int(0) + 'canonical_ids' => + int(0) + 'results' => + array(1) { + [0] => + array(1) { + 'message_id' => + string(35) "0:154231004164960%c5f39c08c5f39c543" + } + } + } + +Data message +============ + +`Client app is responsible for processing data messages. Data messages have only custom key-value pairs.` + +.. code-block:: php + + addData('test', '123'); + ->addRecipient($deviceId) + + // Shortcut function: + // $notification = $client->pushData(['key' => 'value'], $deviceId); + + $response = $client->send($notification); + +Example response: + +.. code-block:: text + + array(5) { + 'multicast_id' => + int(76762359248473280622) + 'success' => + int(1) + 'failure' => + int(0) + 'canonical_ids' => + int(0) + 'results' => + array(1) { + [0] => + array(1) { + 'message_id' => + string(35) "0:1524927061384248%c5f39c08f9fd7ecd" + } + } + } diff --git a/docs/overview.rst b/docs/overview.rst new file mode 100644 index 0000000..0daee2a --- /dev/null +++ b/docs/overview.rst @@ -0,0 +1,45 @@ +======== +Overview +======== + +Requirements +============ + +#. PHP 7.0 +#. A free Firebase account, read the :doc:`quickstart` for more information. + +Running the tests +================= + +This project uses PHPUnit for running its unit tests. Run the tests using the vendor provided phpunit binary: + +.. code-block:: bash + + vendor/bin/phpunit -c phpunit.dist.xml + +License +======= + +Licensed using the `MIT license `_. + +.. code-block:: text + + Copyright (c) 2018 Edwin Hoksberg + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. diff --git a/docs/quickstart.rst b/docs/quickstart.rst new file mode 100644 index 0000000..933cd62 --- /dev/null +++ b/docs/quickstart.rst @@ -0,0 +1,36 @@ +========== +Quickstart +========== + +Installing this package +======================= + +This package is available on packagist, and can be installed with composer: + +.. code-block:: bash + + composer require edwinhoksberg/php-fcm + +Configuring a Firebase account +============================== + +This project uses Google Firebase for sending notifications, so we need to signup for an free account. +A free account can store up to ~20 million messages, device groups and topics. + +#. Click `here `_ to go the firebase console page. +#. Click on the Create Project button. Enter a project name and select your country/region. +#. Once your project is created, click on continue to go to the project dashboard. +#. Click on the gear icon in the top left, next to the Project Overview link, and click on Project Settings. +#. Go to the Cloud Messaging tab. +#. On this page, you should see several authentication tokens. Copy and save the `Server key` and `Sender ID` tokens somewhere. These will be used in the project configuration. + +Configuring the project +======================= + +When you instantiate a new Firebase Cloud Messaging object, pass the `Server Key` and `Sender ID` you retrieved from the previous steps: + +.. code-block:: php + + `_ for more information. + +Subscribing to a topic +====================== + +When a topic does not exist, you can still subscribe to it, and the topic will be automaticly created. + +.. code-block:: php + + addDevice($deviceId); + + // Shortcut function: + // $client->topicSubscribe('my_topic_name', $deviceId); + + $client->send($subscribe); + +Example response: + +.. code-block:: text + + // When an error occurs, this will be filled with the message. + array(1) { + 'results' => + array(1) { + [0] => + array(0) { + } + } + } + +Unsubscribing from a topic +========================== + +Just like creating a topic, a topic will be automaticly deleted once all devices are unsubscribed from it. + +.. code-block:: php + + addDevice($deviceId); + + // Shortcut function: + // $client->topicUnsubscribe('my_topic_name', $deviceId); + + $client->send($unsubscribe); + +Example response: + +.. code-block:: text + + // When an error occurs, this will be filled with the message. + array(1) { + 'results' => + array(1) { + [0] => + array(0) { + } + } + } diff --git a/phpunit.dist.xml b/phpunit.dist.xml new file mode 100644 index 0000000..344421b --- /dev/null +++ b/phpunit.dist.xml @@ -0,0 +1,23 @@ + + + + + ./tests + + + + + + ./src + + + diff --git a/src/Device/Info.php b/src/Device/Info.php new file mode 100644 index 0000000..f3f87f8 --- /dev/null +++ b/src/Device/Info.php @@ -0,0 +1,50 @@ +deviceId = $deviceId; + $this->details = $details; + } + + /** + * @inheritdoc + */ + public function getUrl(): string + { + $url = "https://iid.googleapis.com/iid/info/{$this->deviceId}"; + + if ($this->details) { + $url .= '?details=true'; + } + + return $url; + } + + /** + * @inheritdoc + */ + public function getBody(): array + { + return []; + } +} diff --git a/src/DeviceGroup/Create.php b/src/DeviceGroup/Create.php new file mode 100644 index 0000000..418d4c8 --- /dev/null +++ b/src/DeviceGroup/Create.php @@ -0,0 +1,40 @@ +groupName = $groupName; + + if (!empty($deviceId)) { + $this->addDevice($deviceId); + } + } + + /** + * @inheritdoc + */ + public function getBody(): array + { + return [ + 'operation' => 'create', + 'notification_key_name' => $this->groupName, + 'registration_ids' => $this->deviceIds + ]; + } +} diff --git a/src/DeviceGroup/DeviceGroup.php b/src/DeviceGroup/DeviceGroup.php new file mode 100644 index 0000000..f8eeedd --- /dev/null +++ b/src/DeviceGroup/DeviceGroup.php @@ -0,0 +1,37 @@ +deviceIds[] = $deviceId; + } + + if (\is_array($deviceId)) { + $this->deviceIds = array_merge($this->deviceIds, $deviceId); + } + + return $this; + } + + /** + * @inheritdoc + */ + public function getUrl(): string + { + return 'https://android.googleapis.com/gcm/notification'; + } +} diff --git a/src/DeviceGroup/Remove.php b/src/DeviceGroup/Remove.php new file mode 100644 index 0000000..8474bd4 --- /dev/null +++ b/src/DeviceGroup/Remove.php @@ -0,0 +1,48 @@ +notification_key_name = $notification_key_name; + $this->notification_key = $notification_key; + + if (!empty($deviceId)) { + $this->addDevice($deviceId); + } + } + + /** + * @inheritdoc + */ + public function getBody(): array + { + return [ + 'operation' => 'remove', + 'notification_key_name' => $this->notification_key_name, + 'notification_key' => $this->notification_key, + 'registration_ids' => $this->deviceIds + ]; + } +} diff --git a/src/DeviceGroup/Update.php b/src/DeviceGroup/Update.php new file mode 100644 index 0000000..647e30b --- /dev/null +++ b/src/DeviceGroup/Update.php @@ -0,0 +1,48 @@ +notification_key_name = $notification_key_name; + $this->notification_key = $notification_key; + + if (!empty($deviceId)) { + $this->addDevice($deviceId); + } + } + + /** + * @inheritdoc + */ + public function getBody(): array + { + return [ + 'operation' => 'add', + 'notification_key_name' => $this->notification_key_name, + 'notification_key' => $this->notification_key, + 'registration_ids' => $this->deviceIds + ]; + } +} diff --git a/src/Exception/FcmClientException.php b/src/Exception/FcmClientException.php new file mode 100644 index 0000000..08b8b9c --- /dev/null +++ b/src/Exception/FcmClientException.php @@ -0,0 +1,10 @@ +apiToken = $apiToken; + $this->senderId = $senderId; + } + + /** + * @param string $apiToken + * @param string $projectId + * + * @return FcmClient + */ + public static function create(string $apiToken, string $projectId): FcmClient + { + return new self($apiToken, $projectId); + } + + /** + * @param Request $request + * + * @return array + */ + public function send(Request $request): array + { + // Build guzzle api client. + $client = $this->getGuzzleClient(); + + // Generate request url. + $url = $request->getUrl(); + + // Create and send the request. + $response = $client->post($url, [ + 'json' => $request->getBody() + ]); + + // Decode the response body from json to a plain php array. + $body = json_decode($response->getBody()->getContents(), true); + if ($body === null || json_last_error() !== JSON_ERROR_NONE) { + throw new FcmClientException('Failed to json decode response body: '.json_last_error_msg()); + } + + return $body; + } + + /** + * @param string $name + * @param array $arguments + * + * @return object + */ + public function __call(string $name, array $arguments) + { + $camelCaseSplit = preg_split('/(?=[A-Z])/', $name); + + // Get all but last item from the split class name array, + // and concatinate it back together. + $group = array_slice($camelCaseSplit, 0, -1); + $group = ucwords(implode('', $group)); + + // The classname is always the last item in the group name. + $class = ucwords(end($camelCaseSplit)); + + // Check if the class exists before instantiating it. + $class = "\Fcm\\{$group}\\{$class}"; + if (!class_exists($class)) { + throw new FcmClientException('Invalid magic method called: '.$class); + } + + // Instantiate the class with the correct constructor arguments and return it. + $class = new ReflectionClass($class); + return $class->newInstanceArgs($arguments); + } + + /** + * @return Client + */ + public function getGuzzleClient(): Client + { + // Create configuration array + return new Client([ + 'headers' => [ + 'Authorization' => 'key='.$this->apiToken, + 'project_id' => $this->senderId, + ] + ]); + } +} diff --git a/src/Push/Data.php b/src/Push/Data.php new file mode 100644 index 0000000..c376029 --- /dev/null +++ b/src/Push/Data.php @@ -0,0 +1,72 @@ +data = $data; + } + + if (!empty($recipient)) { + $this->addRecipient($recipient); + } + } + + /** + * @inheritdoc + */ + public function getBody(): array + { + if (empty($this->data)) { + throw new NotificationException('Data should not be empty for a Data Notification.'); + } + + if (empty($this->recipients) && empty($this->topics)) { + throw new NotificationException('Must minimaly specify a single recipient or topic.'); + } + + if (!empty($this->recipients) && !empty($this->topics)) { + throw new NotificationException('Must either specify a recipient or topic, not more then one.'); + } + + $request = []; + + if (!empty($this->recipients)) { + if (\count($this->recipients) === 1) { + $request['to'] = current($this->recipients); + } else { + $request['registration_ids'] = $this->recipients; + } + } + + if (!empty($this->topics)) { + $request['condition'] = array_reduce($this->topics, function ($carry, string $topic) { + $topicSyntax = "'%s' in topics"; + + if (end($this->topics) === $topic) { + return $carry .= sprintf($topicSyntax, $topic); + } + + return $carry .= sprintf($topicSyntax, $topic) . '||'; + }); + } + + if (!empty($this->data)) { + $request['data'] = $this->data; + } + + return $request; + } +} diff --git a/src/Push/Notification.php b/src/Push/Notification.php new file mode 100644 index 0000000..33f4863 --- /dev/null +++ b/src/Push/Notification.php @@ -0,0 +1,105 @@ +title = $title; + $this->body = $body; + + if (!empty($recipient)) { + $this->addRecipient($recipient); + } + } + + /** + * @param string $title + * + * @return $this + */ + public function setTitle(string $title): self + { + $this->title = $title; + + return $this; + } + + /** + * @param string $body + * + * @return $this + */ + public function setBody(string $body): self + { + $this->body = $body; + + return $this; + } + + /** + * @inheritdoc + */ + public function getBody(): array + { + if (empty($this->recipients) && empty($this->topics)) { + throw new NotificationException('Must minimaly specify a single recipient or topic.'); + } + + if (!empty($this->recipients) && !empty($this->topics)) { + throw new NotificationException('Must either specify a recipient or topic, not more then one.'); + } + + $request = []; + + if (!empty($this->recipients)) { + if (\count($this->recipients) === 1) { + $request['to'] = current($this->recipients); + } else { + $request['registration_ids'] = $this->recipients; + } + } + + if (!empty($this->topics)) { + $request['condition'] = array_reduce($this->topics, function ($carry, string $topic) { + $topicSyntax = "'%s' in topics"; + + if (end($this->topics) === $topic) { + return $carry .= sprintf($topicSyntax, $topic); + } + + return $carry .= sprintf($topicSyntax, $topic) . '||'; + }); + } + + if (!empty($this->data)) { + $request['data'] = $this->data; + } + + $request['notification']['title'] = $this->title; + $request['notification']['body'] = $this->body; + + return $request; + } +} diff --git a/src/Push/Push.php b/src/Push/Push.php new file mode 100644 index 0000000..aca9042 --- /dev/null +++ b/src/Push/Push.php @@ -0,0 +1,78 @@ +recipients[] = $iidToken; + } + + if (\is_array($iidToken)) { + $this->recipients = array_merge($this->recipients, $iidToken); + } + + return $this; + } + + /** + * @param $topic + * + * @return self + */ + public function addTopic($topic): self + { + if (\is_string($topic)) { + $this->topics[] = $topic; + } + + if (\is_array($topic)) { + $this->topics = array_merge($this->topics, $topic); + } + + return $this; + } + + /** + * @param string $name + * @param mixed $value + * + * @return Push + */ + public function addData($name, $value): self + { + $this->data[$name] = $value; + + return $this; + } + + /** + * @inheritdoc + */ + public function getUrl(): string + { + return 'https://fcm.googleapis.com/fcm/send'; + } +} diff --git a/src/Request.php b/src/Request.php new file mode 100644 index 0000000..4c984c5 --- /dev/null +++ b/src/Request.php @@ -0,0 +1,16 @@ +topicName = $topicName; + + if (!empty($deviceId)) { + $this->addDevice($deviceId); + } + } + + /** + * @inheritdoc + */ + public function getUrl(): string + { + return 'https://iid.googleapis.com/iid/v1:batchAdd'; + } + + /** + * @inheritdoc + */ + public function getBody(): array + { + return [ + 'to' => "/topics/{$this->topicName}", + 'registration_tokens' => $this->devices, + ]; + } +} diff --git a/src/Topic/Topic.php b/src/Topic/Topic.php new file mode 100644 index 0000000..b5c2908 --- /dev/null +++ b/src/Topic/Topic.php @@ -0,0 +1,35 @@ +devices[] = $deviceId; + } + + if (\is_array($deviceId)) { + $this->devices = array_merge($this->devices, $deviceId); + } + + return $this; + } +} diff --git a/src/Topic/Unsubscribe.php b/src/Topic/Unsubscribe.php new file mode 100644 index 0000000..3556126 --- /dev/null +++ b/src/Topic/Unsubscribe.php @@ -0,0 +1,47 @@ +topicName = $topicName; + + if (!empty($deviceId)) { + $this->addDevice($deviceId); + } + } + + /** + * @inheritdoc + */ + public function getUrl(): string + { + return 'https://iid.googleapis.com/iid/v1:batchRemove'; + } + + /** + * @inheritdoc + */ + public function getBody(): array + { + return [ + 'to' => "/topics/{$this->topicName}", + 'registration_tokens' => $this->devices, + ]; + } +} diff --git a/tests/ClientTests/FcmClientTest.php b/tests/ClientTests/FcmClientTest.php new file mode 100644 index 0000000..4fe6a7b --- /dev/null +++ b/tests/ClientTests/FcmClientTest.php @@ -0,0 +1,83 @@ +getGuzzleClient() + ->getConfig('headers'); + + $this->assertArrayHasKey('Authorization', $headers); + $this->assertSame('key=api_token_test', $headers['Authorization']); + + $this->assertArrayHasKey('project_id', $headers); + $this->assertSame('the_sender_id', $headers['project_id']); + } + + /** @test */ + public function it_correctly_sends_a_request_and_parses_the_response() + { + $client = Mockery::mock(\Fcm\FcmClient::class)->makePartial(); + + $client + ->shouldReceive('getGuzzleClient') + ->andReturn(new GuzzleClientMock()); + + $info = new \Fcm\Device\Info('device_id'); + + $response = $client->send($info); + $this->assertSame(['json_syntax' => 'correct'], $response); + } + + /** + * @test + * + * @expectedException \Fcm\Exception\FcmClientException + * @expectedExceptionMessage Failed to json decode response body: Syntax error + */ + public function it_throws_an_exception_if_invalid_json_was_returned() + { + $client = Mockery::mock(\Fcm\FcmClient::class)->makePartial(); + + $client + ->shouldReceive('getGuzzleClient') + ->andReturn(new InvalidJsonGuzzleClientMock()); + + $info = new \Fcm\Device\Info('device_id'); + + $client->send($info); + } + + /** + * @test + * + * @expectedException \Fcm\Exception\FcmClientException + * @expectedExceptionMessage Invalid magic method called: \Fcm\Fail\Test + */ + public function it_fails_when_calling_a_nonexisting_magic_method() + { + $client = new \Fcm\FcmClient('', ''); + + $client->failTest(); + } +} + +class GuzzleClientMock extends \GuzzleHttp\Client +{ + public function post($uri, array $options = []): \Psr\Http\Message\ResponseInterface + { + return new \GuzzleHttp\Psr7\Response(200, [], '{"json_syntax": "correct"}'); + } +} + +class InvalidJsonGuzzleClientMock extends \GuzzleHttp\Client +{ + public function post($uri, array $options = []): \Psr\Http\Message\ResponseInterface + { + return new \GuzzleHttp\Psr7\Response(200, [], '%Invalid"json^'); + } +} diff --git a/tests/DeviceGroupTests/CreateTest.php b/tests/DeviceGroupTests/CreateTest.php new file mode 100644 index 0000000..d99d679 --- /dev/null +++ b/tests/DeviceGroupTests/CreateTest.php @@ -0,0 +1,63 @@ +addDevice('my-phone'); + + $expected = [ + 'operation' => 'create', + 'notification_key_name' => 'group_name', + 'registration_ids' => [ + 'my-phone', + ] + ]; + + $this->assertSame($expected, $create->getBody()); + $this->assertSame('https://android.googleapis.com/gcm/notification', $create->getUrl()); + } + + /** @test */ + public function it_can_create_a_group_from_multiple_devices() + { + $create = new \Fcm\DeviceGroup\Create('group_name'); + $create + ->addDevice('my-phone') + ->addDevice(['device_1', 'device_2']) + ->addDevice('my-tablet'); + + $expected = [ + 'operation' => 'create', + 'notification_key_name' => 'group_name', + 'registration_ids' => [ + 'my-phone', + 'device_1', + 'device_2', + 'my-tablet', + ] + ]; + + $this->assertSame($expected, $create->getBody()); + } + + /** @test */ + public function it_can_generate_a_quick_object_from_magic_method() + { + $client = \Fcm\FcmClient::create('', ''); + + $create = $client->deviceGroupCreate('devicegroup_new', 'device_id'); + + $expected = [ + 'operation' => 'create', + 'notification_key_name' => 'devicegroup_new', + 'registration_ids' => [ + 'device_id', + ] + ]; + + $this->assertSame($expected, $create->getBody()); + } +} diff --git a/tests/DeviceGroupTests/RemoveTest.php b/tests/DeviceGroupTests/RemoveTest.php new file mode 100644 index 0000000..e91a1b0 --- /dev/null +++ b/tests/DeviceGroupTests/RemoveTest.php @@ -0,0 +1,69 @@ +addDevice('device_test'); + + $expected = [ + 'operation' => 'remove', + 'notification_key_name' => 'devices_to_remove', + 'notification_key' => 'generated_key', + 'registration_ids' => [ + 'device_test', + ] + ]; + + $this->assertSame($expected, $remove->getBody()); + } + + /** @test */ + public function it_can_update_a_group_with_multiple_devices() + { + $remove = new \Fcm\DeviceGroup\Remove('group_name', 'generated_key'); + $remove + ->addDevice('my-phone') + ->addDevice(['device_1', 'device_2']) + ->addDevice('my-tablet'); + + $expected = [ + 'operation' => 'remove', + 'notification_key_name' => 'group_name', + 'notification_key' => 'generated_key', + 'registration_ids' => [ + 'my-phone', + 'device_1', + 'device_2', + 'my-tablet', + ] + ]; + + $this->assertSame($expected, $remove->getBody()); + } + + /** @test */ + public function it_can_generate_a_quick_object_from_magic_method() + { + $client = \Fcm\FcmClient::create('', ''); + + $remove = $client->deviceGroupRemove( + 'devicegroup_new', + 'generated_token', + 'the_device' + ); + + $expected = [ + 'operation' => 'remove', + 'notification_key_name' => 'devicegroup_new', + 'notification_key' => 'generated_token', + 'registration_ids' => [ + 'the_device', + ] + ]; + + $this->assertSame($expected, $remove->getBody()); + } +} diff --git a/tests/DeviceGroupTests/UpdateTest.php b/tests/DeviceGroupTests/UpdateTest.php new file mode 100644 index 0000000..71ab569 --- /dev/null +++ b/tests/DeviceGroupTests/UpdateTest.php @@ -0,0 +1,69 @@ +addDevice('device_test'); + + $expected = [ + 'operation' => 'add', + 'notification_key_name' => 'group_name', + 'notification_key' => 'generated_key', + 'registration_ids' => [ + 'device_test', + ] + ]; + + $this->assertSame($expected, $update->getBody()); + } + + /** @test */ + public function it_can_update_a_group_with_multiple_devices() + { + $update = new \Fcm\DeviceGroup\Update('group_name', 'generated_key'); + $update + ->addDevice('my-phone') + ->addDevice(['device_1', 'device_2']) + ->addDevice('my-tablet'); + + $expected = [ + 'operation' => 'add', + 'notification_key_name' => 'group_name', + 'notification_key' => 'generated_key', + 'registration_ids' => [ + 'my-phone', + 'device_1', + 'device_2', + 'my-tablet', + ] + ]; + + $this->assertSame($expected, $update->getBody()); + } + + /** @test */ + public function it_can_generate_a_quick_object_from_magic_method() + { + $client = \Fcm\FcmClient::create('', ''); + + $update = $client->deviceGroupUpdate( + 'devicegroup_new', + 'generated_token', + 'the_device' + ); + + $expected = [ + 'operation' => 'add', + 'notification_key_name' => 'devicegroup_new', + 'notification_key' => 'generated_token', + 'registration_ids' => [ + 'the_device', + ] + ]; + + $this->assertSame($expected, $update->getBody()); + } +} diff --git a/tests/DeviceTests/InfoTest.php b/tests/DeviceTests/InfoTest.php new file mode 100644 index 0000000..8967b83 --- /dev/null +++ b/tests/DeviceTests/InfoTest.php @@ -0,0 +1,28 @@ +assertSame([], $info->getBody()); + $this->assertSame('https://iid.googleapis.com/iid/info/device_id', $info->getUrl()); + + $info = new \Fcm\Device\Info('device_id_new', true); + + $this->assertSame([], $info->getBody()); + $this->assertSame('https://iid.googleapis.com/iid/info/device_id_new?details=true', $info->getUrl()); + } + + /** @test */ + public function it_can_generate_a_quick_object_from_magic_method() + { + $client = new \Fcm\FcmClient('', ''); + + $info = $client->deviceInfo('my-magic_Device'); + + $this->assertSame('https://iid.googleapis.com/iid/info/my-magic_Device', $info->getUrl()); + } +} diff --git a/tests/PushTests/DataTest.php b/tests/PushTests/DataTest.php new file mode 100644 index 0000000..33fd22f --- /dev/null +++ b/tests/PushTests/DataTest.php @@ -0,0 +1,151 @@ +addRecipient('device_1') + ->addData('key', 'value'); + + $expected = [ + 'to' => 'device_1', + 'data' => [ + 'key' => 'value', + ], + ]; + + $this->assertSame($expected, $data->getBody()); + } + + /** + * @test + * + * @expectedException \Fcm\Exception\NotificationException + * @expectedExceptionMessage Must minimaly specify a single recipient or topic. + */ + public function it_can_not_have_no_recipients_or_topics() + { + $data = new \Fcm\Push\Data(); + $data->addData('key', 'value'); + + $data->getBody(); + } + + /** + * @test + * + * @expectedException \Fcm\Exception\NotificationException + * @expectedExceptionMessage Must either specify a recipient or topic, not more then one. + */ + public function it_can_not_have_a_recipient_and_topic() + { + $data = new \Fcm\Push\Data(); + $data + ->addRecipient('device') + ->addTopic('topic') + ->addData('key', 'value'); + + $data->getBody(); + } + + /** + * @test + * + * @expectedException \Fcm\Exception\NotificationException + * @expectedExceptionMessage Data should not be empty for a Data Notification. + */ + public function it_should_always_have_data_parameters() + { + $data = new \Fcm\Push\Data(); + + $data->getBody(); + } + + /** @test */ + public function it_can_generate_a_data_push_for_multiple_recipients() + { + $data = new \Fcm\Push\Data(); + $data + ->addRecipient('device_1') + ->addRecipient(['device_2', 'device_3']) + ->addData('key', 'value'); + + $expected = [ + 'registration_ids' => [ + 'device_1', + 'device_2', + 'device_3', + ], + 'data' => [ + 'key' => 'value', + ], + ]; + + $this->assertSame($expected, $data->getBody()); + } + + /** @test */ + public function it_can_generate_a_data_push_for_multiple_topics() + { + $data = new \Fcm\Push\Data(); + $data + ->addData('test', 'value') + ->addTopic(['news', 'weather']) + ->addTopic('personal'); + + $expected = [ + 'condition' => "'news' in topics||'weather' in topics||'personal' in topics", + 'data' => [ + 'test' => 'value', + ], + ]; + + $this->assertSame($expected, $data->getBody()); + } + + /** @test */ + public function it_can_generate_a_data_push_with_data() + { + $data = new \Fcm\Push\Data(); + $data + ->addRecipient('device') + ->addData('key', 'value') + ->addData('name', 'notification') + ->addData('test', 'data'); + + $expected = [ + 'to' => 'device', + 'data' => [ + 'key' => 'value', + 'name' => 'notification', + 'test' => 'data', + ], + ]; + + $this->assertSame($expected, $data->getBody()); + } + + /** @test */ + public function it_can_generate_a_quick_object_from_magic_method() + { + $client = \Fcm\FcmClient::create('', ''); + + $data = $client->pushData([ + 'sample' => 'example', + 'my_name' => 'john doe', + ], 'device_id'); + + $expected = [ + 'to' => 'device_id', + 'data' => [ + 'sample' => 'example', + 'my_name' => 'john doe', + ], + ]; + + $this->assertSame($expected, $data->getBody()); + } +} diff --git a/tests/PushTests/NotificationTest.php b/tests/PushTests/NotificationTest.php new file mode 100644 index 0000000..511d2b8 --- /dev/null +++ b/tests/PushTests/NotificationTest.php @@ -0,0 +1,151 @@ +setTitle('Test title') + ->setBody('A small body as an example') + ->addRecipient('device_1') + ->addData('key', 'value'); + + $expected = [ + 'to' => 'device_1', + 'data' => [ + 'key' => 'value', + ], + 'notification' => [ + 'title' => 'Test title', + 'body' => 'A small body as an example', + ], + ]; + + $this->assertSame($expected, $notification->getBody()); + $this->assertSame('https://fcm.googleapis.com/fcm/send', $notification->getUrl()); + } + + /** + * @test + * + * @expectedException \Fcm\Exception\NotificationException + * @expectedExceptionMessage Must minimaly specify a single recipient or topic. + */ + public function it_can_not_have_no_recipients_or_topics() + { + $notification = new \Fcm\Push\Notification(); + $notification + ->setTitle('Test title') + ->setBody('A small body as an example'); + + $notification->getBody(); + } + + /** + * @test + * + * @expectedException \Fcm\Exception\NotificationException + * @expectedExceptionMessage Must either specify a recipient or topic, not more then one. + */ + public function it_can_not_have_a_recipient_and_topic() + { + $notification = new \Fcm\Push\Notification(); + $notification + ->setTitle('Test title') + ->addRecipient('device') + ->addTopic('topic'); + + $notification->getBody(); + } + + /** @test */ + public function it_can_generate_a_notification_for_multiple_recipients() + { + $notification = new \Fcm\Push\Notification(); + $notification + ->setTitle('Test title') + ->addRecipient('device_1') + ->addRecipient(['device_2', 'device_3']); + + $expected = [ + 'registration_ids' => [ + 'device_1', + 'device_2', + 'device_3', + ], + 'notification' => [ + 'title' => 'Test title', + 'body' => '', + ], + ]; + + $this->assertSame($expected, $notification->getBody()); + } + + /** @test */ + public function it_can_generate_a_notification_for_multiple_topics() + { + $notification = new \Fcm\Push\Notification(); + $notification + ->setTitle('Test title') + ->addTopic(['news', 'weather']) + ->addTopic('personal'); + + $expected = [ + 'condition' => "'news' in topics||'weather' in topics||'personal' in topics", + 'notification' => [ + 'title' => 'Test title', + 'body' => '', + ], + ]; + + $this->assertSame($expected, $notification->getBody()); + } + + /** @test */ + public function it_can_generate_a_notification_with_data() + { + $notification = new \Fcm\Push\Notification(); + $notification + ->setTitle('Test title') + ->addRecipient('device') + ->addData('key', 'value') + ->addData('name', 'notification') + ->addData('test', 'data'); + + $expected = [ + 'to' => 'device', + 'data' => [ + 'key' => 'value', + 'name' => 'notification', + 'test' => 'data', + ], + 'notification' => [ + 'title' => 'Test title', + 'body' => '', + ], + ]; + + $this->assertSame($expected, $notification->getBody()); + } + + /** @test */ + public function it_can_generate_a_quick_object_from_magic_method() + { + $client = \Fcm\FcmClient::create('', ''); + + $notification = $client->pushNotification('Sample title', 'Sample body', 'device_id'); + + $expected = [ + 'to' => 'device_id', + 'notification' => [ + 'title' => 'Sample title', + 'body' => 'Sample body', + ], + ]; + + $this->assertSame($expected, $notification->getBody()); + } +} diff --git a/tests/TopicTests/SubscribeTest.php b/tests/TopicTests/SubscribeTest.php new file mode 100644 index 0000000..1b3fc77 --- /dev/null +++ b/tests/TopicTests/SubscribeTest.php @@ -0,0 +1,70 @@ +addDevice('device_1'); + + $expected = [ + 'to' => '/topics/the_topic', + 'registration_tokens' => [ + 'device_1', + ], + ]; + + $this->assertSame($expected, $subscribe->getBody()); + $this->assertSame('https://iid.googleapis.com/iid/v1:batchAdd', $subscribe->getUrl()); + } + + /** @test */ + public function it_can_subscribe_multiple_devices_to_topic() + { + $subscribe = new \Fcm\Topic\Subscribe('news'); + $subscribe + ->addDevice('device_1') + ->addDevice(['device_new', 'test_device']); + + $expected = [ + 'to' => '/topics/news', + 'registration_tokens' => [ + 'device_1', + 'device_new', + 'test_device', + ], + ]; + + $this->assertSame($expected, $subscribe->getBody()); + } + + /** + * @test + * + * @expectedException \Fcm\Exception\TopicException + * @expectedExceptionMessage Device id is empty + */ + public function it_can_not_use_an_empty_deviceId() + { + $subscribe = new \Fcm\Topic\Subscribe('news'); + $subscribe->addDevice(''); + } + + /** @test */ + public function it_can_generate_a_quick_object_from_magic_method() + { + $client = new \Fcm\FcmClient('', ''); + + $subscribe = $client->topicSubscribe('topic_name', 'device_identifier'); + + $expected = [ + 'to' => '/topics/topic_name', + 'registration_tokens' => [ + 'device_identifier', + ], + ]; + + $this->assertSame($expected, $subscribe->getBody()); + } +} diff --git a/tests/TopicTests/UnsubscribeTest.php b/tests/TopicTests/UnsubscribeTest.php new file mode 100644 index 0000000..ee51668 --- /dev/null +++ b/tests/TopicTests/UnsubscribeTest.php @@ -0,0 +1,70 @@ +addDevice('device_1'); + + $expected = [ + 'to' => '/topics/the_topic', + 'registration_tokens' => [ + 'device_1', + ], + ]; + + $this->assertSame($expected, $unsubscribe->getBody()); + $this->assertSame('https://iid.googleapis.com/iid/v1:batchRemove', $unsubscribe->getUrl()); + } + + /** @test */ + public function it_can_unsubscribe_multiple_devices_to_topic() + { + $unsubscribe = new \Fcm\Topic\Unsubscribe('news'); + $unsubscribe + ->addDevice('device_1') + ->addDevice(['device_new', 'test_device']); + + $expected = [ + 'to' => '/topics/news', + 'registration_tokens' => [ + 'device_1', + 'device_new', + 'test_device', + ], + ]; + + $this->assertSame($expected, $unsubscribe->getBody()); + } + + /** + * @test + * + * @expectedException \Fcm\Exception\TopicException + * @expectedExceptionMessage Device id is empty + */ + public function it_can_not_use_an_empty_deviceId() + { + $unsubscribe = new \Fcm\Topic\Unsubscribe('news'); + $unsubscribe->addDevice(''); + } + + /** @test */ + public function it_can_generate_a_quick_object_from_magic_method() + { + $client = new \Fcm\FcmClient('', ''); + + $unsubscribe = $client->topicUnsubscribe('topic_name', 'device_identifier'); + + $expected = [ + 'to' => '/topics/topic_name', + 'registration_tokens' => [ + 'device_identifier', + ], + ]; + + $this->assertSame($expected, $unsubscribe->getBody()); + } +}