From c065be97ba235b9a5a0bf4ec7a9fc966360a9d66 Mon Sep 17 00:00:00 2001 From: Rich Sage Date: Thu, 2 Apr 2015 12:10:56 +0100 Subject: [PATCH 01/33] Fix use of PHP5.4 array syntax. The library is still valid for use with PHP 5.3 --- Service/EventListener.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Service/EventListener.php b/Service/EventListener.php index f745bab..bfe8ba2 100644 --- a/Service/EventListener.php +++ b/Service/EventListener.php @@ -8,7 +8,7 @@ class EventListener { /** * @var EventListenerInterface[] */ - protected $listeners = []; + protected $listeners = array(); /** * @param EventListenerInterface $listener @@ -25,4 +25,4 @@ public function onKernelTerminate () { $listener->onKernelTerminate(); } } -} \ No newline at end of file +} From 69accec94cc72c1328958fea6182f0af4556e648 Mon Sep 17 00:00:00 2001 From: Antoine Mineau Date: Wed, 8 Apr 2015 11:05:22 +0200 Subject: [PATCH 02/33] added logger for Android GCM --- Resources/config/android.xml | 1 + Service/OS/AndroidGCMNotification.php | 16 +++++++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/Resources/config/android.xml b/Resources/config/android.xml index 03d5849..5cdecd7 100644 --- a/Resources/config/android.xml +++ b/Resources/config/android.xml @@ -24,6 +24,7 @@ %rms_push_notifications.android.gcm.api_key% %rms_push_notifications.android.gcm.use_multi_curl% %rms_push_notifications.android.timeout% + diff --git a/Service/OS/AndroidGCMNotification.php b/Service/OS/AndroidGCMNotification.php index 9fade96..ea318d5 100644 --- a/Service/OS/AndroidGCMNotification.php +++ b/Service/OS/AndroidGCMNotification.php @@ -47,15 +47,23 @@ class AndroidGCMNotification implements OSNotificationServiceInterface */ protected $responses; + /** + * Monolog logger + * + * @var Symfony\Bridge\Monolog\Logger + */ + protected $logger; + /** * Constructor * * @param $apiKey * @param bool $useMultiCurl * @param int $timeout + * @param Logger $logger * @param AbstractCurl $client (optional) */ - public function __construct($apiKey, $useMultiCurl, $timeout, AbstractCurl $client = null) + public function __construct($apiKey, $useMultiCurl, $timeout, $logger, AbstractCurl $client = null) { $this->apiKey = $apiKey; if (!$client) { @@ -65,6 +73,7 @@ public function __construct($apiKey, $useMultiCurl, $timeout, AbstractCurl $clie $this->browser = new Browser($client); $this->browser->getClient()->setVerifyPeer(false); + $this->logger = $logger; } /** @@ -112,6 +121,11 @@ public function send(MessageInterface $message) foreach ($this->responses as $response) { $message = json_decode($response->getContent()); if ($message === null || $message->success == 0 || $message->failure > 0) { + if ($message == null) { + $this->logger->err($response->getContent()); + } else { + $this->logger->err($message->failure); + } return false; } } From 2794d2c4e8047b420be3c7dd45a4f80f23ca3755 Mon Sep 17 00:00:00 2001 From: Dave Marshall Date: Mon, 11 Feb 2013 15:54:54 +0000 Subject: [PATCH 03/33] Make symfony optional --- composer.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index d061b4d..dd360ba 100644 --- a/composer.json +++ b/composer.json @@ -14,9 +14,14 @@ ], "require": { "php": ">=5.3.0", - "symfony/symfony": "2.*", "kriswallsmith/buzz": "*" }, + "require-dev": { + "symfony/symfony": "2.*" + }, + "suggest": { + "symfony/symfony": "To use as a bundle" + }, "autoload": { "psr-0": { "RMS\\PushNotificationsBundle": "" } }, From 31ccbf1a3b54a7b22d96dd3e45bbf6cbe61e8a3a Mon Sep 17 00:00:00 2001 From: Antoine Mineau Date: Fri, 15 May 2015 11:25:15 +0200 Subject: [PATCH 04/33] added logger for push errors --- Resources/config/blackberry.xml | 1 + Resources/config/ios.xml | 1 + Resources/config/mac.xml | 1 + Resources/config/windowsphone.xml | 1 + Service/OS/AppleNotification.php | 24 +++++++++++++++++------- Service/OS/BlackberryNotification.php | 15 ++++++++++++++- Service/OS/MicrosoftNotification.php | 14 +++++++++++++- 7 files changed, 48 insertions(+), 9 deletions(-) diff --git a/Resources/config/blackberry.xml b/Resources/config/blackberry.xml index 374a04e..eb9b5a0 100644 --- a/Resources/config/blackberry.xml +++ b/Resources/config/blackberry.xml @@ -15,6 +15,7 @@ %rms_push_notifications.blackberry.app_id% %rms_push_notifications.blackberry.password% %rms_push_notifications.blackberry.timeout% + diff --git a/Resources/config/ios.xml b/Resources/config/ios.xml index 8ca6789..3ca7bec 100644 --- a/Resources/config/ios.xml +++ b/Resources/config/ios.xml @@ -18,6 +18,7 @@ %rms_push_notifications.ios.timeout% %kernel.cache_dir% + diff --git a/Resources/config/mac.xml b/Resources/config/mac.xml index 20322c5..14bd70d 100644 --- a/Resources/config/mac.xml +++ b/Resources/config/mac.xml @@ -16,6 +16,7 @@ %rms_push_notifications.mac.passphrase% %rms_push_notifications.mac.json_unescaped_unicode% %rms_push_notifications.mac.timeout% + diff --git a/Resources/config/windowsphone.xml b/Resources/config/windowsphone.xml index 8659821..6703a17 100644 --- a/Resources/config/windowsphone.xml +++ b/Resources/config/windowsphone.xml @@ -12,6 +12,7 @@ %rms_push_notifications.windowsphone.timeout% + diff --git a/Service/OS/AppleNotification.php b/Service/OS/AppleNotification.php index e50b883..8fd8ce9 100644 --- a/Service/OS/AppleNotification.php +++ b/Service/OS/AppleNotification.php @@ -96,6 +96,13 @@ class AppleNotification implements OSNotificationServiceInterface, EventListener */ protected $cachedir; + /** + * Monolog logger + * + * @var Symfony\Bridge\Monolog\Logger + */ + protected $logger; + /** * Cache pem filename */ @@ -104,15 +111,16 @@ class AppleNotification implements OSNotificationServiceInterface, EventListener /** * Constructor * - * @param $sandbox - * @param $pem - * @param string $passphrase - * @param bool $jsonUnescapedUnicode - * @param int $timeout - * @param string $cachedir + * @param bool $sandbox + * @param string $pem + * @param string $passphrase + * @param bool $jsonUnescapedUnicode + * @param int $timeout + * @param string $cachedir * @param EventListener $eventListener + * @param Logger $logger */ - public function __construct($sandbox, $pem, $passphrase = "", $jsonUnescapedUnicode = FALSE, $timeout = 60, $cachedir = "", EventListener $eventListener = null) + public function __construct($sandbox, $pem, $passphrase = "", $jsonUnescapedUnicode = FALSE, $timeout = 60, $cachedir = "", EventListener $eventListener = null, $logger) { $this->useSandbox = $sandbox; $this->pemPath = $pem; @@ -123,6 +131,7 @@ public function __construct($sandbox, $pem, $passphrase = "", $jsonUnescapedUnic $this->jsonUnescapedUnicode = $jsonUnescapedUnicode; $this->timeout = $timeout; $this->cachedir = $cachedir; + $this->logger = $logger; if ($eventListener != null) $eventListener->addListener($this); @@ -205,6 +214,7 @@ protected function sendMessages($firstMessageId, $apnURL) // Resend all messages that were sent after the failed message $this->sendMessages($result['identifier']+1, $apnURL); $errors[] = $result; + $this->logger->err(json_encode($result)); } else { $this->responses[] = true; } diff --git a/Service/OS/BlackberryNotification.php b/Service/OS/BlackberryNotification.php index a80aa63..4b81d0a 100644 --- a/Service/OS/BlackberryNotification.php +++ b/Service/OS/BlackberryNotification.php @@ -39,6 +39,13 @@ class BlackberryNotification implements OSNotificationServiceInterface */ protected $timeout; + /** + * Monolog logger + * + * @var Symfony\Bridge\Monolog\Logger + */ + protected $logger; + /** * Constructor * @@ -46,13 +53,15 @@ class BlackberryNotification implements OSNotificationServiceInterface * @param $appID * @param $password * @param $timeout + * @param $logger */ - public function __construct($evaluation, $appID, $password, $timeout) + public function __construct($evaluation, $appID, $password, $timeout, $logger) { $this->evaluation = $evaluation; $this->appID = $appID; $this->password = $password; $this->timeout = $timeout; + $this->logger = $logger; } /** @@ -145,9 +154,13 @@ protected function parseResponse(\Buzz\Message\Response $response) $doc->loadXML($response->getContent()); $elems = $doc->getElementsByTagName("response-result"); if (!$elems->length) { + $this->logger->err('Response is empty'); return false; } $responseElement = $elems->item(0); + if ($responseElement->getAttribute("code") != "1001") { + $this->logger->err($responseElement->getAttribute("code"). ' : '. $responseElement->getAttribute("desc")); + } return ($responseElement->getAttribute("code") == "1001"); } diff --git a/Service/OS/MicrosoftNotification.php b/Service/OS/MicrosoftNotification.php index 35db37c..cc20f3d 100644 --- a/Service/OS/MicrosoftNotification.php +++ b/Service/OS/MicrosoftNotification.php @@ -17,14 +17,22 @@ class MicrosoftNotification implements OSNotificationServiceInterface */ protected $browser; + /** + * Monolog logger + * + * @var Symfony\Bridge\Monolog\Logger + */ + protected $logger; + /** * @param $timeout */ - public function __construct($timeout) + public function __construct($timeout, $logger) { $this->browser = new Browser(new Curl()); $this->browser->getClient()->setVerifyPeer(false); $this->browser->getClient()->setTimeout($timeout); + $this->logger = $logger; } public function send(MessageInterface $message) @@ -51,6 +59,10 @@ public function send(MessageInterface $message) $response = $this->browser->post($message->getDeviceIdentifier(), $headers, $xml->asXML()); + if (!$response->isSuccessful()) { + $this->logger->err($response->getStatusCode(). ' : '. $response->getReasonPhrase()); + } + return $response->isSuccessful(); } } From b789834640409f4f320db6cd336cd05b8cec55e0 Mon Sep 17 00:00:00 2001 From: tgallice Date: Fri, 29 May 2015 11:49:58 +0200 Subject: [PATCH 05/33] Close apn stream in case of shutdown error --- Service/OS/AppleNotification.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Service/OS/AppleNotification.php b/Service/OS/AppleNotification.php index e50b883..013ecbe 100644 --- a/Service/OS/AppleNotification.php +++ b/Service/OS/AppleNotification.php @@ -201,6 +201,12 @@ protected function sendMessages($firstMessageId, $apnURL) // Check if there is an error result if (is_array($result)) { + + // Close the apn stream in case of Shutdown status code. + if ($result['status'] === 10) { + $this->closeApnStream($apnURL); + } + $this->responses[] = $result; // Resend all messages that were sent after the failed message $this->sendMessages($result['identifier']+1, $apnURL); From 5383f5042be07968b1a828b415e6a4596d600779 Mon Sep 17 00:00:00 2001 From: tgallice Date: Mon, 8 Jun 2015 10:48:02 +0200 Subject: [PATCH 06/33] Define constant for shutdown status code --- Service/OS/AppleNotification.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Service/OS/AppleNotification.php b/Service/OS/AppleNotification.php index 013ecbe..a9f3d62 100644 --- a/Service/OS/AppleNotification.php +++ b/Service/OS/AppleNotification.php @@ -101,6 +101,11 @@ class AppleNotification implements OSNotificationServiceInterface, EventListener */ const APNS_CERTIFICATE_FILE = '/rms_push_notifications/apns.pem'; + /** + * Status code retrieve when APNS server closed the connection + */ + const APNS_SHUTDOWN_CODE = 10; + /** * Constructor * @@ -203,7 +208,7 @@ protected function sendMessages($firstMessageId, $apnURL) if (is_array($result)) { // Close the apn stream in case of Shutdown status code. - if ($result['status'] === 10) { + if ($result['status'] === self::APNS_SHUTDOWN_CODE) { $this->closeApnStream($apnURL); } From 5db201ab89f46162b057109ad422d47981635f1e Mon Sep 17 00:00:00 2001 From: Rich Sage Date: Sun, 28 Jun 2015 21:41:00 +0100 Subject: [PATCH 07/33] Add missing cache directory to AppleNotification. --- Resources/config/mac.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/Resources/config/mac.xml b/Resources/config/mac.xml index 14bd70d..0c72abb 100644 --- a/Resources/config/mac.xml +++ b/Resources/config/mac.xml @@ -16,6 +16,7 @@ %rms_push_notifications.mac.passphrase% %rms_push_notifications.mac.json_unescaped_unicode% %rms_push_notifications.mac.timeout% + %kernel.cache_dir% From 6bb892e9eddc05999d68ebd4540dfb8735649276 Mon Sep 17 00:00:00 2001 From: Rich Sage Date: Sun, 28 Jun 2015 21:44:38 +0100 Subject: [PATCH 08/33] Add note about Windows Phone (fixes #103). --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index ceca8ad..cd8255c 100644 --- a/README.md +++ b/README.md @@ -111,6 +111,10 @@ Here, `$uuids` contains an array of [Feedback](https://github.com/richsage/RMSPu Apple recommend you poll this service daily. +## Windows Phone - Toast support + +The bundle has beta support for Windows Phone, and supports the Toast notification. Use the `WindowsphoneMessage` message class to send accordingly. + # Thanks Firstly, thanks to all contributors to this bundle! From 9e3e764ed230446637afade019aa73293d015cc0 Mon Sep 17 00:00:00 2001 From: Rich Sage Date: Mon, 20 Jul 2015 22:34:48 +0100 Subject: [PATCH 09/33] Provide correct method parameters (fixes #108). --- Resources/config/mac.xml | 1 + Service/OS/AppleNotification.php | 9 ++++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Resources/config/mac.xml b/Resources/config/mac.xml index 0c72abb..e4e4c0e 100644 --- a/Resources/config/mac.xml +++ b/Resources/config/mac.xml @@ -17,6 +17,7 @@ %rms_push_notifications.mac.json_unescaped_unicode% %rms_push_notifications.mac.timeout% %kernel.cache_dir% + diff --git a/Service/OS/AppleNotification.php b/Service/OS/AppleNotification.php index 06b3561..632b03d 100644 --- a/Service/OS/AppleNotification.php +++ b/Service/OS/AppleNotification.php @@ -125,7 +125,7 @@ class AppleNotification implements OSNotificationServiceInterface, EventListener * @param EventListener $eventListener * @param Logger $logger */ - public function __construct($sandbox, $pem, $passphrase = "", $jsonUnescapedUnicode = FALSE, $timeout = 60, $cachedir = "", EventListener $eventListener = null, $logger) + public function __construct($sandbox, $pem, $passphrase = "", $jsonUnescapedUnicode = FALSE, $timeout = 60, $cachedir = "", EventListener $eventListener = null, $logger = null) { $this->useSandbox = $sandbox; $this->pemPath = $pem; @@ -138,8 +138,9 @@ public function __construct($sandbox, $pem, $passphrase = "", $jsonUnescapedUnic $this->cachedir = $cachedir; $this->logger = $logger; - if ($eventListener != null) + if ($eventListener != null) { $eventListener->addListener($this); + } } /** @@ -225,7 +226,9 @@ protected function sendMessages($firstMessageId, $apnURL) // Resend all messages that were sent after the failed message $this->sendMessages($result['identifier']+1, $apnURL); $errors[] = $result; - $this->logger->err(json_encode($result)); + if ($this->logger) { + $this->logger->err(json_encode($result)); + } } else { $this->responses[] = true; } From b033491aaf50b87b785e61f107cddbac4c6b359a Mon Sep 17 00:00:00 2001 From: Rick van Laarhoven Date: Fri, 9 Oct 2015 11:52:09 +0200 Subject: [PATCH 10/33] Log the actual GCM error messages --- Service/OS/AndroidGCMNotification.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Service/OS/AndroidGCMNotification.php b/Service/OS/AndroidGCMNotification.php index ea318d5..96dd70d 100644 --- a/Service/OS/AndroidGCMNotification.php +++ b/Service/OS/AndroidGCMNotification.php @@ -124,7 +124,11 @@ public function send(MessageInterface $message) if ($message == null) { $this->logger->err($response->getContent()); } else { - $this->logger->err($message->failure); + foreach ($message->results as $result) { + if (isset($result->error)) { + $this->logger->error($result->error); + } + } } return false; } From b263392f92400e4d67005063b0c6e2cbb8878880 Mon Sep 17 00:00:00 2001 From: Matthieu Lemoine Date: Mon, 26 Oct 2015 20:10:50 +0100 Subject: [PATCH 11/33] Add IOS notification category --- Message/AppleMessage.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Message/AppleMessage.php b/Message/AppleMessage.php index 00e329b..9025aa2 100644 --- a/Message/AppleMessage.php +++ b/Message/AppleMessage.php @@ -210,6 +210,17 @@ public function setAPSContentAvailable($contentAvailable) $this->apsBody["aps"]["content-available"] = $contentAvailable; } + /** + * iOS-specific + * Sets the APS category + * + * @param string $category The notification category + */ + public function setCategory($category) + { + $this->apsBody["aps"]["category"] = $category; + } + /** * Set expiry of message * From 67298cbb0d08b5c81ce625b962890381c8e421cb Mon Sep 17 00:00:00 2001 From: Rich Sage Date: Wed, 9 Mar 2016 14:48:06 +0000 Subject: [PATCH 12/33] Remove erroneous clearing of errors array in loop --- Service/OS/AppleNotification.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/Service/OS/AppleNotification.php b/Service/OS/AppleNotification.php index 632b03d..4f1a85f 100644 --- a/Service/OS/AppleNotification.php +++ b/Service/OS/AppleNotification.php @@ -212,8 +212,6 @@ protected function sendMessages($firstMessageId, $apnURL) // Send the message $result = $this->writeApnStream($apnURL, $this->messages[$currentMessageId]); - $errors = array(); - // Check if there is an error result if (is_array($result)) { From b1d87c10265009c6eaacead24aeededd0479fe33 Mon Sep 17 00:00:00 2001 From: Christian Mayer Date: Wed, 9 Mar 2016 19:04:02 +0100 Subject: [PATCH 13/33] Assign the count of messages to a variable. count() in a for() loop condition is always a bad idea because the count() will always be executed, even if the $messages array does not change. --- Service/OS/AppleNotification.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Service/OS/AppleNotification.php b/Service/OS/AppleNotification.php index 4f1a85f..666ac97 100644 --- a/Service/OS/AppleNotification.php +++ b/Service/OS/AppleNotification.php @@ -208,7 +208,8 @@ protected function sendMessages($firstMessageId, $apnURL) { $errors = array(); // Loop through all messages starting from the given ID - for ($currentMessageId = $firstMessageId; $currentMessageId < count($this->messages); $currentMessageId++) { + $messagesCount = count($this->messages); + for ($currentMessageId = $firstMessageId; $currentMessageId < $messagesCount; $currentMessageId++) { // Send the message $result = $this->writeApnStream($apnURL, $this->messages[$currentMessageId]); From 4a32867e6d8370c88756e9e4cb5b3ad4e7a8176b Mon Sep 17 00:00:00 2001 From: TiMESPLiNTER Date: Thu, 24 Mar 2016 09:30:33 +0100 Subject: [PATCH 14/33] Fixed #123 --- DependencyInjection/Configuration.php | 1 + .../RMSPushNotificationsExtension.php | 2 ++ README.md | 1 + Resources/config/android.xml | 1 + Service/OS/AndroidGCMNotification.php | 20 ++++++++++++++++--- .../DependencyInjection/ConfigurationTest.php | 6 ++++++ 6 files changed, 28 insertions(+), 3 deletions(-) diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index ce1022e..9d8f30f 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -65,6 +65,7 @@ protected function addAndroid() children()-> scalarNode("api_key")->isRequired()->cannotBeEmpty()->end()-> booleanNode("use_multi_curl")->defaultValue(true)->end()-> + booleanNode("dry_run")->defaultFalse()->end()-> end()-> end()-> end()-> diff --git a/DependencyInjection/RMSPushNotificationsExtension.php b/DependencyInjection/RMSPushNotificationsExtension.php index c00f476..4975b39 100644 --- a/DependencyInjection/RMSPushNotificationsExtension.php +++ b/DependencyInjection/RMSPushNotificationsExtension.php @@ -100,6 +100,8 @@ protected function setAndroidConfig(array $config) if (isset($config["android"]["gcm"])) { $this->container->setParameter("rms_push_notifications.android.gcm.api_key", $config["android"]["gcm"]["api_key"]); $this->container->setParameter("rms_push_notifications.android.gcm.use_multi_curl", $config["android"]["gcm"]["use_multi_curl"]); + $this->container->setParameter('rms_push_notifications.android.gcm.dry_run', $config["android"]["dry_run"]); + } } diff --git a/README.md b/README.md index cd8255c..e3cc8af 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,7 @@ only be available if you provide configuration respectively for them. gcm: api_key: # This is titled "Server Key" when creating it use_multi_curl: # default is true + dry_run: ios: timeout: 60 # Seconds to wait for connection timeout, default is 60 sandbox: diff --git a/Resources/config/android.xml b/Resources/config/android.xml index 5cdecd7..f1fa7df 100644 --- a/Resources/config/android.xml +++ b/Resources/config/android.xml @@ -24,6 +24,7 @@ %rms_push_notifications.android.gcm.api_key% %rms_push_notifications.android.gcm.use_multi_curl% %rms_push_notifications.android.timeout% + %rms_push_notifications.android.dry_run% diff --git a/Service/OS/AndroidGCMNotification.php b/Service/OS/AndroidGCMNotification.php index ea318d5..a613e96 100644 --- a/Service/OS/AndroidGCMNotification.php +++ b/Service/OS/AndroidGCMNotification.php @@ -12,6 +12,14 @@ class AndroidGCMNotification implements OSNotificationServiceInterface { + + /** + * Whether or not to use the dry run GCM + * + * @var bool + */ + protected $useDryRun = false; + /** * GCM endpoint * @@ -57,14 +65,16 @@ class AndroidGCMNotification implements OSNotificationServiceInterface /** * Constructor * - * @param $apiKey + * @param bool $dryRun + * @param string $apiKey * @param bool $useMultiCurl * @param int $timeout * @param Logger $logger - * @param AbstractCurl $client (optional) + * @param AbstractCurl $client (optional) */ - public function __construct($apiKey, $useMultiCurl, $timeout, $logger, AbstractCurl $client = null) + public function __construct($dryRun, $apiKey, $useMultiCurl, $timeout, $logger, AbstractCurl $client = null) { + $this->useDryRun = $dryRun; $this->apiKey = $apiKey; if (!$client) { $client = ($useMultiCurl ? new MultiCurl() : new Curl()); @@ -101,6 +111,10 @@ public function send(MessageInterface $message) array("data" => $message->getData()) ); + if ($this->useDryRun) { + $data['dry_run'] = true; + } + // Chunk number of registration IDs according to the maximum allowed by GCM $chunks = array_chunk($message->getGCMIdentifiers(), $this->registrationIdMaxCount); diff --git a/Tests/DependencyInjection/ConfigurationTest.php b/Tests/DependencyInjection/ConfigurationTest.php index 10ef8bd..b6e64aa 100644 --- a/Tests/DependencyInjection/ConfigurationTest.php +++ b/Tests/DependencyInjection/ConfigurationTest.php @@ -111,12 +111,18 @@ public function testGCMIsOK() "gcm" => array( "api_key" => "foo", "use_multi_curl" => true, + "dry_run" => false, ) ) ), ); $config = $this->process($arr); $this->assertEquals("foo", $config["android"]["gcm"]["api_key"]); + $this->assertFalse($config["android"]["gcm"]["dry_run"]); + + $arr[0]["android"]["gcm"]["dry_run"] = true; + $config = $this->process($arr); + $this->assertTrue($config["android"]["gcm"]["dry_run"]); } /** From df174f8e401d6fb91200d388c4e68110bc346cc3 Mon Sep 17 00:00:00 2001 From: TiMESPLiNTER Date: Thu, 24 Mar 2016 09:35:43 +0100 Subject: [PATCH 15/33] Remove unnecessary new line --- DependencyInjection/RMSPushNotificationsExtension.php | 1 - 1 file changed, 1 deletion(-) diff --git a/DependencyInjection/RMSPushNotificationsExtension.php b/DependencyInjection/RMSPushNotificationsExtension.php index 4975b39..8705c89 100644 --- a/DependencyInjection/RMSPushNotificationsExtension.php +++ b/DependencyInjection/RMSPushNotificationsExtension.php @@ -101,7 +101,6 @@ protected function setAndroidConfig(array $config) $this->container->setParameter("rms_push_notifications.android.gcm.api_key", $config["android"]["gcm"]["api_key"]); $this->container->setParameter("rms_push_notifications.android.gcm.use_multi_curl", $config["android"]["gcm"]["use_multi_curl"]); $this->container->setParameter('rms_push_notifications.android.gcm.dry_run', $config["android"]["dry_run"]); - } } From 2d5a87ddf7150cf942026dd5aea9c28d6dbf69b5 Mon Sep 17 00:00:00 2001 From: TiMESPLiNTER Date: Thu, 24 Mar 2016 11:45:40 +0100 Subject: [PATCH 16/33] Moves dryRun param to the end of the signature with default value of "false" --- Resources/config/android.xml | 2 +- Service/OS/AndroidGCMNotification.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Resources/config/android.xml b/Resources/config/android.xml index f1fa7df..73efc8c 100644 --- a/Resources/config/android.xml +++ b/Resources/config/android.xml @@ -24,8 +24,8 @@ %rms_push_notifications.android.gcm.api_key% %rms_push_notifications.android.gcm.use_multi_curl% %rms_push_notifications.android.timeout% - %rms_push_notifications.android.dry_run% + %rms_push_notifications.android.dry_run% diff --git a/Service/OS/AndroidGCMNotification.php b/Service/OS/AndroidGCMNotification.php index a613e96..ce5dcbd 100644 --- a/Service/OS/AndroidGCMNotification.php +++ b/Service/OS/AndroidGCMNotification.php @@ -65,14 +65,14 @@ class AndroidGCMNotification implements OSNotificationServiceInterface /** * Constructor * - * @param bool $dryRun * @param string $apiKey * @param bool $useMultiCurl * @param int $timeout * @param Logger $logger * @param AbstractCurl $client (optional) + * @param bool $dryRun */ - public function __construct($dryRun, $apiKey, $useMultiCurl, $timeout, $logger, AbstractCurl $client = null) + public function __construct($apiKey, $useMultiCurl, $timeout, $logger, AbstractCurl $client = null, $dryRun = false) { $this->useDryRun = $dryRun; $this->apiKey = $apiKey; From b436f71860adf1e828311b1ee1e8fa48c198c4fb Mon Sep 17 00:00:00 2001 From: Rich Sage Date: Thu, 24 Mar 2016 10:58:51 +0000 Subject: [PATCH 17/33] Fix error logging and use Psr interface. --- Service/OS/AndroidGCMNotification.php | 7 ++++--- composer.json | 3 ++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Service/OS/AndroidGCMNotification.php b/Service/OS/AndroidGCMNotification.php index e697cf1..9a945ff 100644 --- a/Service/OS/AndroidGCMNotification.php +++ b/Service/OS/AndroidGCMNotification.php @@ -2,6 +2,7 @@ namespace RMS\PushNotificationsBundle\Service\OS; +use Psr\Log\LoggerInterface; use RMS\PushNotificationsBundle\Exception\InvalidMessageTypeException, RMS\PushNotificationsBundle\Message\AndroidMessage, RMS\PushNotificationsBundle\Message\MessageInterface; @@ -58,7 +59,7 @@ class AndroidGCMNotification implements OSNotificationServiceInterface /** * Monolog logger * - * @var Symfony\Bridge\Monolog\Logger + * @var LoggerInterface */ protected $logger; @@ -68,7 +69,7 @@ class AndroidGCMNotification implements OSNotificationServiceInterface * @param string $apiKey * @param bool $useMultiCurl * @param int $timeout - * @param Logger $logger + * @param LoggerInterface $logger * @param AbstractCurl $client (optional) * @param bool $dryRun */ @@ -136,7 +137,7 @@ public function send(MessageInterface $message) $message = json_decode($response->getContent()); if ($message === null || $message->success == 0 || $message->failure > 0) { if ($message == null) { - $this->logger->err($response->getContent()); + $this->logger->error($response->getContent()); } else { foreach ($message->results as $result) { if (isset($result->error)) { diff --git a/composer.json b/composer.json index dd360ba..4dcaf34 100644 --- a/composer.json +++ b/composer.json @@ -14,7 +14,8 @@ ], "require": { "php": ">=5.3.0", - "kriswallsmith/buzz": "*" + "kriswallsmith/buzz": "*", + "psr/log": "^1.0" }, "require-dev": { "symfony/symfony": "2.*" From 25e9d13bee1e9dae00b1dd58aec6a8d13aeb44d1 Mon Sep 17 00:00:00 2001 From: Rich Sage Date: Thu, 24 Mar 2016 10:59:57 +0000 Subject: [PATCH 18/33] Update Apple service to use Psr logger. --- Service/OS/AppleNotification.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Service/OS/AppleNotification.php b/Service/OS/AppleNotification.php index 666ac97..3b19ace 100644 --- a/Service/OS/AppleNotification.php +++ b/Service/OS/AppleNotification.php @@ -2,6 +2,7 @@ namespace RMS\PushNotificationsBundle\Service\OS; +use Psr\Log\LoggerInterface; use RMS\PushNotificationsBundle\Exception\InvalidMessageTypeException, RMS\PushNotificationsBundle\Message\AppleMessage, RMS\PushNotificationsBundle\Message\MessageInterface, @@ -99,7 +100,7 @@ class AppleNotification implements OSNotificationServiceInterface, EventListener /** * Monolog logger * - * @var Symfony\Bridge\Monolog\Logger + * @var LoggerInterface */ protected $logger; @@ -123,7 +124,7 @@ class AppleNotification implements OSNotificationServiceInterface, EventListener * @param int $timeout * @param string $cachedir * @param EventListener $eventListener - * @param Logger $logger + * @param LoggerInterface $logger */ public function __construct($sandbox, $pem, $passphrase = "", $jsonUnescapedUnicode = FALSE, $timeout = 60, $cachedir = "", EventListener $eventListener = null, $logger = null) { @@ -226,7 +227,7 @@ protected function sendMessages($firstMessageId, $apnURL) $this->sendMessages($result['identifier']+1, $apnURL); $errors[] = $result; if ($this->logger) { - $this->logger->err(json_encode($result)); + $this->logger->error(json_encode($result)); } } else { $this->responses[] = true; From 2c80c6537de067f92ae4cbd8483d3d014cdf37b9 Mon Sep 17 00:00:00 2001 From: Rich Sage Date: Thu, 24 Mar 2016 11:01:21 +0000 Subject: [PATCH 19/33] Update Blackberry service to use Psr logger. --- Service/OS/BlackberryNotification.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Service/OS/BlackberryNotification.php b/Service/OS/BlackberryNotification.php index 4b81d0a..acc4b4c 100644 --- a/Service/OS/BlackberryNotification.php +++ b/Service/OS/BlackberryNotification.php @@ -2,6 +2,7 @@ namespace RMS\PushNotificationsBundle\Service\OS; +use Psr\Log\LoggerInterface; use RMS\PushNotificationsBundle\Exception\InvalidMessageTypeException, RMS\PushNotificationsBundle\Message\BlackberryMessage, RMS\PushNotificationsBundle\Message\MessageInterface; @@ -42,7 +43,7 @@ class BlackberryNotification implements OSNotificationServiceInterface /** * Monolog logger * - * @var Symfony\Bridge\Monolog\Logger + * @var LoggerInterface */ protected $logger; @@ -154,12 +155,12 @@ protected function parseResponse(\Buzz\Message\Response $response) $doc->loadXML($response->getContent()); $elems = $doc->getElementsByTagName("response-result"); if (!$elems->length) { - $this->logger->err('Response is empty'); + $this->logger->error('Response is empty'); return false; } $responseElement = $elems->item(0); if ($responseElement->getAttribute("code") != "1001") { - $this->logger->err($responseElement->getAttribute("code"). ' : '. $responseElement->getAttribute("desc")); + $this->logger->error($responseElement->getAttribute("code"). ' : '. $responseElement->getAttribute("desc")); } return ($responseElement->getAttribute("code") == "1001"); From 4aa2097997526d624763c6335ea175c5fe8f8cf6 Mon Sep 17 00:00:00 2001 From: Rich Sage Date: Thu, 24 Mar 2016 11:02:06 +0000 Subject: [PATCH 20/33] Update Microsoft service to use Psr logger. --- Service/OS/MicrosoftNotification.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Service/OS/MicrosoftNotification.php b/Service/OS/MicrosoftNotification.php index cc20f3d..204b1f0 100644 --- a/Service/OS/MicrosoftNotification.php +++ b/Service/OS/MicrosoftNotification.php @@ -2,6 +2,7 @@ namespace RMS\PushNotificationsBundle\Service\OS; +use Psr\Log\LoggerInterface; use RMS\PushNotificationsBundle\Exception\InvalidMessageTypeException; use RMS\PushNotificationsBundle\Message\WindowsphoneMessage; use RMS\PushNotificationsBundle\Message\MessageInterface; @@ -20,12 +21,13 @@ class MicrosoftNotification implements OSNotificationServiceInterface /** * Monolog logger * - * @var Symfony\Bridge\Monolog\Logger + * @var LoggerInterface */ protected $logger; /** * @param $timeout + * @param $logger */ public function __construct($timeout, $logger) { @@ -60,7 +62,7 @@ public function send(MessageInterface $message) $response = $this->browser->post($message->getDeviceIdentifier(), $headers, $xml->asXML()); if (!$response->isSuccessful()) { - $this->logger->err($response->getStatusCode(). ' : '. $response->getReasonPhrase()); + $this->logger->error($response->getStatusCode(). ' : '. $response->getReasonPhrase()); } return $response->isSuccessful(); From d9f93122b59a44493a4406ea3d27f1c64bd507fc Mon Sep 17 00:00:00 2001 From: Rich Sage Date: Thu, 24 Mar 2016 11:11:02 +0000 Subject: [PATCH 21/33] Fix exception class being thrown. --- DependencyInjection/Compiler/AddHandlerPass.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DependencyInjection/Compiler/AddHandlerPass.php b/DependencyInjection/Compiler/AddHandlerPass.php index 57294fd..3151328 100644 --- a/DependencyInjection/Compiler/AddHandlerPass.php +++ b/DependencyInjection/Compiler/AddHandlerPass.php @@ -38,7 +38,7 @@ public function process(ContainerBuilder $container) } $refClass = new \ReflectionClass($class); - } catch (\ReflectionClass $ref) { + } catch (\ReflectionException $ref) { // Class not found or other reflection error throw new \RuntimeException(sprintf( 'Can\'t compile notification handler by service id "%s".', From c19b7c455f4ff42a259612be746d7c4cdd27fb4e Mon Sep 17 00:00:00 2001 From: Rich Sage Date: Thu, 24 Mar 2016 11:13:46 +0000 Subject: [PATCH 22/33] Fix dry-run parameter passed to service. --- Resources/config/android.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/config/android.xml b/Resources/config/android.xml index 73efc8c..63ae9f8 100644 --- a/Resources/config/android.xml +++ b/Resources/config/android.xml @@ -25,7 +25,7 @@ %rms_push_notifications.android.gcm.use_multi_curl% %rms_push_notifications.android.timeout% - %rms_push_notifications.android.dry_run% + %rms_push_notifications.android.gcm.dry_run% From c40db9d932a520ea47014725d6cc7d477c40b69a Mon Sep 17 00:00:00 2001 From: Rich Sage Date: Thu, 24 Mar 2016 11:42:32 +0000 Subject: [PATCH 23/33] Fix GCM configuration bug. --- DependencyInjection/RMSPushNotificationsExtension.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DependencyInjection/RMSPushNotificationsExtension.php b/DependencyInjection/RMSPushNotificationsExtension.php index 8705c89..a0c4745 100644 --- a/DependencyInjection/RMSPushNotificationsExtension.php +++ b/DependencyInjection/RMSPushNotificationsExtension.php @@ -100,7 +100,7 @@ protected function setAndroidConfig(array $config) if (isset($config["android"]["gcm"])) { $this->container->setParameter("rms_push_notifications.android.gcm.api_key", $config["android"]["gcm"]["api_key"]); $this->container->setParameter("rms_push_notifications.android.gcm.use_multi_curl", $config["android"]["gcm"]["use_multi_curl"]); - $this->container->setParameter('rms_push_notifications.android.gcm.dry_run', $config["android"]["dry_run"]); + $this->container->setParameter('rms_push_notifications.android.gcm.dry_run', $config["android"]["gcm"]["dry_run"]); } } From 45b90b3c2a6ae8068cfeacfd73615b8107835a8c Mon Sep 17 00:00:00 2001 From: Rich Sage Date: Thu, 24 Mar 2016 15:42:58 +0000 Subject: [PATCH 24/33] Update branch-alias. --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 4dcaf34..7947f38 100644 --- a/composer.json +++ b/composer.json @@ -29,7 +29,7 @@ "target-dir": "RMS/PushNotificationsBundle", "extra": { "branch-alias": { - "dev-master": "0.1.x-dev" + "dev-master": "0.2.x-dev" } } } From 052b4f9379fabc03aed7ca8991ebaa2447546da1 Mon Sep 17 00:00:00 2001 From: Rich Sage Date: Sat, 26 Mar 2016 11:53:26 +0000 Subject: [PATCH 25/33] Add missing null argument to GCM service. --- Resources/config/android.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/Resources/config/android.xml b/Resources/config/android.xml index 63ae9f8..5802e00 100644 --- a/Resources/config/android.xml +++ b/Resources/config/android.xml @@ -25,6 +25,7 @@ %rms_push_notifications.android.gcm.use_multi_curl% %rms_push_notifications.android.timeout% + null %rms_push_notifications.android.gcm.dry_run% From 26840ddb386aa58e499261477996ac055e05acd7 Mon Sep 17 00:00:00 2001 From: Anton Minin Date: Fri, 1 Apr 2016 12:54:32 +0300 Subject: [PATCH 26/33] [GCM] Don't use registration_ids for single recipient --- Service/OS/AndroidGCMNotification.php | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/Service/OS/AndroidGCMNotification.php b/Service/OS/AndroidGCMNotification.php index 9a945ff..69a1c1a 100644 --- a/Service/OS/AndroidGCMNotification.php +++ b/Service/OS/AndroidGCMNotification.php @@ -116,14 +116,21 @@ public function send(MessageInterface $message) $data['dry_run'] = true; } - // Chunk number of registration IDs according to the maximum allowed by GCM - $chunks = array_chunk($message->getGCMIdentifiers(), $this->registrationIdMaxCount); - // Perform the calls (in parallel) $this->responses = array(); - foreach ($chunks as $registrationIDs) { - $data["registration_ids"] = $registrationIDs; + $gcmIdentifiers = $message->getGCMIdentifiers(); + + if (count($message->getGCMIdentifiers()) == 1) { + $data['to'] = $gcmIdentifiers[0]; $this->responses[] = $this->browser->post($this->apiURL, $headers, json_encode($data)); + } else { + // Chunk number of registration IDs according to the maximum allowed by GCM + $chunks = array_chunk($message->getGCMIdentifiers(), $this->registrationIdMaxCount); + + foreach ($chunks as $registrationIDs) { + $data['registration_ids'] = $registrationIDs; + $this->responses[] = $this->browser->post($this->apiURL, $headers, json_encode($data)); + } } // If we're using multiple concurrent connections via MultiCurl From a81560b1092994465bfa932baace3998f95f85ce Mon Sep 17 00:00:00 2001 From: Oleksandr Shpagin Date: Tue, 10 May 2016 17:55:39 +0200 Subject: [PATCH 27/33] Close all APNS streams just after setting PEM as string. --- Service/OS/AppleNotification.php | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/Service/OS/AppleNotification.php b/Service/OS/AppleNotification.php index 3b19ace..508ed4c 100644 --- a/Service/OS/AppleNotification.php +++ b/Service/OS/AppleNotification.php @@ -422,24 +422,41 @@ public function getResponses() public function setPemAsString($pemContent, $passphrase) { $this->pemContent = $pemContent; $this->pemContentPassphrase = $passphrase; + + // for new pem will take affect we need to close existing streams which use cached pem + $this->closeStreams(); } /** * Called on kernel terminate */ - public function onKernelTerminate() { + public function onKernelTerminate() + { + $this->removeCachedPemFile(); + $this->closeStreams(); + } - // Remove cache pem file + /** + * Remove cache pem file + */ + private function removeCachedPemFile() + { $fs = new Filesystem(); $filename = $this->cachedir . self::APNS_CERTIFICATE_FILE; if ($fs->exists(dirname($filename))) { $fs->remove(dirname($filename)); } + } - // Close streams + /** + * Close existing streams + */ + private function closeStreams() + { foreach ($this->apnStreams as $stream) { fclose($stream); } + $this->apnStreams = []; } } From d42cc257197668b5de0a420994431594ec65eba5 Mon Sep 17 00:00:00 2001 From: Oleksandr Shpagin Date: Thu, 12 May 2016 11:18:36 +0200 Subject: [PATCH 28/33] Do nothing if the same PEM and passphrase came. --- Service/OS/AppleNotification.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Service/OS/AppleNotification.php b/Service/OS/AppleNotification.php index 508ed4c..c529f3a 100644 --- a/Service/OS/AppleNotification.php +++ b/Service/OS/AppleNotification.php @@ -420,6 +420,10 @@ public function getResponses() * @param $passphrase */ public function setPemAsString($pemContent, $passphrase) { + if ($this->pemContent === $pemContent && $this->pemContentPassphrase === $passphrase) { + return; + } + $this->pemContent = $pemContent; $this->pemContentPassphrase = $passphrase; From ef92c6b054a975bf536ec16a5f39a76eb7efc20f Mon Sep 17 00:00:00 2001 From: Oleksandr Shpagin Date: Mon, 1 Aug 2016 16:31:49 +0200 Subject: [PATCH 29/33] adjust [] to array() in AppleNotification::closeStreams() --- Service/OS/AppleNotification.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Service/OS/AppleNotification.php b/Service/OS/AppleNotification.php index c529f3a..d19aa14 100644 --- a/Service/OS/AppleNotification.php +++ b/Service/OS/AppleNotification.php @@ -461,6 +461,6 @@ private function closeStreams() fclose($stream); } - $this->apnStreams = []; + $this->apnStreams = array(); } } From 176f196afbe04ba99abd92fd4d1a6a804de1fad8 Mon Sep 17 00:00:00 2001 From: Emmanuel Vella Date: Fri, 6 Jan 2017 15:13:36 +0100 Subject: [PATCH 30/33] Update composer.json Hi, the travis file seems to test this bundle against symfony master, is it possible to allow symfony 3 in composer.json ? --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 7947f38..2dfd3fa 100644 --- a/composer.json +++ b/composer.json @@ -18,7 +18,7 @@ "psr/log": "^1.0" }, "require-dev": { - "symfony/symfony": "2.*" + "symfony/symfony": "^2.0 || ^3.0" }, "suggest": { "symfony/symfony": "To use as a bundle" From 6c3e28f217de349857171407e1e1d6b86f4edd4d Mon Sep 17 00:00:00 2001 From: Dave Marshall Date: Mon, 13 Feb 2017 12:30:59 +0000 Subject: [PATCH 31/33] Adds support for mutable-content flag --- Message/AppleMessage.php | 11 +++++++++++ Tests/Message/iOSMessageTest.php | 13 +++++++++++++ 2 files changed, 24 insertions(+) diff --git a/Message/AppleMessage.php b/Message/AppleMessage.php index 9025aa2..62e2866 100644 --- a/Message/AppleMessage.php +++ b/Message/AppleMessage.php @@ -221,6 +221,17 @@ public function setCategory($category) $this->apsBody["aps"]["category"] = $category; } + /** + * iOS-specific + * Sets the APS mutable-content attribute + * + * @param bool $mutableContent + */ + public function setMutableContent($mutableContent) + { + $this->apsBody["aps"]["mutable-content"] = $mutableContent ? 1 : 0; + } + /** * Set expiry of message * diff --git a/Tests/Message/iOSMessageTest.php b/Tests/Message/iOSMessageTest.php index 7c3b5e6..49226d3 100644 --- a/Tests/Message/iOSMessageTest.php +++ b/Tests/Message/iOSMessageTest.php @@ -70,4 +70,17 @@ public function testCustomDataAddedOK() $msg->setData(array("custom" => array("foo" => "bar"))); $this->assertEquals($expected, $msg->getMessageBody()); } + + public function testMutableContentAddOk() + { + $expected = array( + "aps" => array( + "mutable-content" => 1, + ), + ); + $msg = new iOSMessage(); + $msg->setMutableContent(true); + $this->assertEquals($expected, $msg->getMessageBody()); + } + } From ed389997049501a3d28ad292eb12ccba7c5641e1 Mon Sep 17 00:00:00 2001 From: Phobetor Date: Tue, 4 Apr 2017 12:01:31 +0200 Subject: [PATCH 32/33] Add 7.0 and 7.1 to travis PHP versions --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index cca78b8..5387047 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,8 @@ php: - 5.4 - 5.5 - 5.6 + - 7.0 + - 7.1 env: - SYMFONY_VERSION=origin/master From 38d24f38abb8337e94046faaad7d69dbb426c843 Mon Sep 17 00:00:00 2001 From: Phobetor Date: Tue, 4 Apr 2017 12:18:37 +0200 Subject: [PATCH 33/33] Force PHPunit 5.7 for travis builds with PHP 7.* --- .travis.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.travis.yml b/.travis.yml index 5387047..f248c95 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,9 @@ language: php + +before_install: + # If PHP >= 5.6, download & install PHPunit 5.7 to avoid builds failures + - if php -r "exit( (int)! version_compare( '$TRAVIS_PHP_VERSION', '5.6', '>=' ) );"; then wget -O phpunit https://phar.phpunit.de/phpunit-5.7.phar && chmod +x phpunit && mkdir ~/bin && mv -v phpunit ~/bin; fi + php: - 5.3 - 5.4