diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fab9e47 --- /dev/null +++ b/.gitignore @@ -0,0 +1,25 @@ +# IDE +.build_dir +.externalTollBuilders +.settings +.project +.buildpath +deps.lock +.idea + +# OS +.DS_Store +._* + +# Files that might appear on external disk +.Spotlight-V100 +.Trashes +.buildpath +.project +.settings + +# Dependencies +/vendor + +# Composer +composer.phar diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..be11269 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,8 @@ +# Changes between versions + +## Not yet released + +* added Notification, Notifier and Driver +* added NotifySendDriver (can be used on most Linux distributions) +* added TerminalNotifierDriver (can be used on Mac OS X 10.8 and higher) +* added GrowlNotifyDriver (can be used on Mac OS if growlnotify is available) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..e63e838 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,51 @@ +# How to contribute + +Everybody should be able to help. Here's how you can make this project more +awesome: + +1. [Fork it](https://github.com/jolicode/JoliNotif/fork_select) +2. improve it +3. submit a [pull request](https://help.github.com/articles/creating-a-pull-request) + +Your work will then be reviewed as soon as possible (suggestions about some +changes, improvements or alternatives may be given). + +Here's some tips to make you the best contributor ever: + +* [Green tests](#green-tests) +* [Standard code](#standard-code) +* [Keeping your fork up-to-date](#keeping-your-fork-up-to-date) + +## Green tests + +Run the tests using the following script: + + vendor/bin/phpunit + +## Standard code + +Use [PHP CS fixer](http://cs.sensiolabs.org/) to make your code compliant with +JoliNotif's coding standards: + + vendor/bin/php-cs-fixer fix --config=sf23 . + +## Keeping your fork up-to-date + +To keep your fork up-to-date, you should track the upstream (original) one +using the following command: + + git remote add upstream https://github.com/jolicode/JoliNotif.git + +Then get the upstream changes: + + git checkout master + git pull --rebase origin master + git pull --rebase upstream master + git checkout + git rebase master + +Finally, publish your changes: + + git push -f origin + +Your pull request will be automatically updated. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..6051c3a --- /dev/null +++ b/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2015 Loïck Piera + +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..128aaa5 --- /dev/null +++ b/README.md @@ -0,0 +1,65 @@ +# JoliNotif + +JoliNotif allows you to send notification to your system directly from your php +script. The notifier will take care to use the right command available, without +having to worry if you're running on Linux, Windows or MacOS. + +JoliNotif can be used from your long running task, f.e. to inform your user +that a task just finished. Originally inspired by [mikaelbr/node-notifier](https://github.com/mikaelbr/node-notifier). + +## Getting started + +Use [Composer](http://getcomposer.org/) to install JoliNotif in your projects: + + + composer require "jolicode/jolinotif" + + +## Usage + +The main class is `Notifier`. It should be instantiated with an array of +`Driver`. The `Notifier` constructor will choose the best driver to use, +according to which commands are available on your system. If no driver are +supported, a `SystemNotSupportedException` will be thrown. + +Look at below (or [example/index.php](example/index.php)) to see an example on +how to use JoliNotif. + +```php +// Build a Notifier +$notifier = new Notifier([ + new TerminalNotifierDriver(), + new GrowlNotifyDriver(), + new NotifySendDriver(), +]); + +// Create your notification +$notification = new Notification(); +$notification->setTitle('I\'m a notification title'); +$notification->setBody('And this is the body'); +$notification->setIcon(__DIR__.'/notification-icon.png'); + +// Send it +$notifier->send($notification); +``` + +> **Important**: The only required property on Notification is the body. +> The notifier will throw an InvalidNotificationException if it is empty. + +> **Note**: New properties could be added later on Notification. Drivers are +> designed to only handle the supported properties and discard not supported +> ones without throwing any exception. + +## Further documentation + +You can see the current and past versions using one of the following: + +* the `git tag` command +* the [releases page on Github](https://github.com/jolicode/JoliNotif/releases) +* the file listing the [changes between versions](CHANGELOG.md) + +You can find more documentation at the following links: + +* [copyright and MIT license](LICENSE) +* [versioning and branching models](VERSIONING.md) +* [contribution instructions](CONTRIBUTING.md) diff --git a/VERSIONING.md b/VERSIONING.md new file mode 100644 index 0000000..8a5cab0 --- /dev/null +++ b/VERSIONING.md @@ -0,0 +1,22 @@ +# Versioning and branching models + +This file explains the versioning and branching models of this project. + +## Versioning + +The versioning is inspired by [Semantic Versioning](http://semver.org/): + +> Given a version number MAJOR.MINOR.PATCH, increment the: +> +> 1. MAJOR version when you make incompatible API changes +> 2. MINOR version when you add functionality in a backwards-compatible manner +> 3. PATCH version when you make backwards-compatible bug fixes + +## Branching Model + +The branching is inspired by [@jbenet](https://github.com/jbenet) +[simple git branching model](https://gist.github.com/jbenet/ee6c9ac48068889b0912): + +> 1. `master` must always be deployable. +> 2. **all changes** are made through feature branches (pull-request + merge) +> 3. rebase to avoid/resolve conflicts; merge in to `master` diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..75a01a3 --- /dev/null +++ b/composer.json @@ -0,0 +1,28 @@ +{ + "name": "jolicode/jolinotif", + "license": "MIT", + "type": "library", + + "description": "Send desktop notifications on Windows, Linux, MacOS.", + "keywords": ["notification", "windows", "linux", "mac", "growl"], + "minimum-stability": "stable", + + "autoload": { + "psr-4": { + "JoliNotif\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "JoliNotif\\tests\\": "tests/" + } + }, + "require": { + "php": ">=5.4.0", + "symfony/process": "~2.6" + }, + "require-dev": { + "phpunit/phpunit": "~4.5", + "fabpot/php-cs-fixer": "~1.5" + } +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..74fbc09 --- /dev/null +++ b/composer.lock @@ -0,0 +1,1328 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "This file is @generated automatically" + ], + "hash": "550c9d7dab0c3538e7d0f337532705d7", + "packages": [ + { + "name": "symfony/process", + "version": "v2.6.4", + "target-dir": "Symfony/Component/Process", + "source": { + "type": "git", + "url": "https://github.com/symfony/Process.git", + "reference": "ecfc23e89d9967999fa5f60a1e9af7384396e9ae" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/Process/zipball/ecfc23e89d9967999fa5f60a1e9af7384396e9ae", + "reference": "ecfc23e89d9967999fa5f60a1e9af7384396e9ae", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.6-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\Process\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + }, + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "Symfony Process Component", + "homepage": "http://symfony.com", + "time": "2015-01-25 04:39:26" + } + ], + "packages-dev": [ + { + "name": "doctrine/instantiator", + "version": "1.0.4", + "source": { + "type": "git", + "url": "https://github.com/doctrine/instantiator.git", + "reference": "f976e5de371104877ebc89bd8fecb0019ed9c119" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/f976e5de371104877ebc89bd8fecb0019ed9c119", + "reference": "f976e5de371104877ebc89bd8fecb0019ed9c119", + "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.*@ALPHA" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-0": { + "Doctrine\\Instantiator\\": "src" + } + }, + "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": "2014-10-13 12:58:55" + }, + { + "name": "fabpot/php-cs-fixer", + "version": "v1.5", + "source": { + "type": "git", + "url": "https://github.com/FriendsOfPHP/PHP-CS-Fixer.git", + "reference": "2d6851520bf0250f668307ab2fd28cbb0b35d2b9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/2d6851520bf0250f668307ab2fd28cbb0b35d2b9", + "reference": "2d6851520bf0250f668307ab2fd28cbb0b35d2b9", + "shasum": "" + }, + "require": { + "php": ">=5.3.6", + "sebastian/diff": "~1.1", + "symfony/console": "~2.1", + "symfony/event-dispatcher": "~2.1", + "symfony/filesystem": "~2.1", + "symfony/finder": "~2.1", + "symfony/process": "~2.3", + "symfony/stopwatch": "~2.5" + }, + "require-dev": { + "satooshi/php-coveralls": "0.7.*@dev" + }, + "bin": [ + "php-cs-fixer" + ], + "type": "application", + "autoload": { + "psr-4": { + "Symfony\\CS\\": "Symfony/CS/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Dariusz Rumiński", + "email": "dariusz.ruminski@gmail.com" + }, + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "A script to automatically fix Symfony Coding Standard", + "time": "2015-02-18 19:35:59" + }, + { + "name": "phpdocumentor/reflection-docblock", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "d68dbdc53dc358a816f00b300704702b2eaff7b8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/d68dbdc53dc358a816f00b300704702b2eaff7b8", + "reference": "d68dbdc53dc358a816f00b300704702b2eaff7b8", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.0" + }, + "suggest": { + "dflydev/markdown": "~1.0", + "erusev/parsedown": "~1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-0": { + "phpDocumentor": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "mike.vanriel@naenius.com" + } + ], + "time": "2015-02-03 12:10:50" + }, + { + "name": "phpspec/prophecy", + "version": "v1.3.1", + "source": { + "type": "git", + "url": "https://github.com/phpspec/prophecy.git", + "reference": "9ca52329bcdd1500de24427542577ebf3fc2f1c9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/9ca52329bcdd1500de24427542577ebf3fc2f1c9", + "reference": "9ca52329bcdd1500de24427542577ebf3fc2f1c9", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "~1.0,>=1.0.2", + "phpdocumentor/reflection-docblock": "~2.0" + }, + "require-dev": { + "phpspec/phpspec": "~2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2.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": "http://phpspec.org", + "keywords": [ + "Double", + "Dummy", + "fake", + "mock", + "spy", + "stub" + ], + "time": "2014-11-17 16:23:49" + }, + { + "name": "phpunit/php-code-coverage", + "version": "2.0.15", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "34cc484af1ca149188d0d9e91412191e398e0b67" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/34cc484af1ca149188d0d9e91412191e398e0b67", + "reference": "34cc484af1ca149188d0d9e91412191e398e0b67", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "phpunit/php-file-iterator": "~1.3", + "phpunit/php-text-template": "~1.2", + "phpunit/php-token-stream": "~1.3", + "sebastian/environment": "~1.0", + "sebastian/version": "~1.0" + }, + "require-dev": { + "ext-xdebug": ">=2.1.4", + "phpunit/phpunit": "~4" + }, + "suggest": { + "ext-dom": "*", + "ext-xdebug": ">=2.2.1", + "ext-xmlwriter": "*" + }, + "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": "sb@sebastian-bergmann.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": "2015-01-24 10:06:35" + }, + { + "name": "phpunit/php-file-iterator", + "version": "1.3.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "acd690379117b042d1c8af1fafd61bde001bf6bb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/acd690379117b042d1c8af1fafd61bde001bf6bb", + "reference": "acd690379117b042d1c8af1fafd61bde001bf6bb", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "autoload": { + "classmap": [ + "File/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "" + ], + "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": "2013-10-10 15:34:57" + }, + { + "name": "phpunit/php-text-template", + "version": "1.2.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "206dfefc0ffe9cebf65c413e3d0e809c82fbf00a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/206dfefc0ffe9cebf65c413e3d0e809c82fbf00a", + "reference": "206dfefc0ffe9cebf65c413e3d0e809c82fbf00a", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "autoload": { + "classmap": [ + "Text/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "time": "2014-01-30 17:20:04" + }, + { + "name": "phpunit/php-timer", + "version": "1.0.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "19689d4354b295ee3d8c54b4f42c3efb69cbc17c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/19689d4354b295ee3d8c54b4f42c3efb69cbc17c", + "reference": "19689d4354b295ee3d8c54b4f42c3efb69cbc17c", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "autoload": { + "classmap": [ + "PHP/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "" + ], + "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": "2013-08-02 07:42:54" + }, + { + "name": "phpunit/php-token-stream", + "version": "1.4.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-token-stream.git", + "reference": "db32c18eba00b121c145575fcbcd4d4d24e6db74" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/db32c18eba00b121c145575fcbcd4d4d24e6db74", + "reference": "db32c18eba00b121c145575fcbcd4d4d24e6db74", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4-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": "2015-01-17 09:51:32" + }, + { + "name": "phpunit/phpunit", + "version": "4.5.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "5b578d3865a9128b9c209b011fda6539ec06e7a5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/5b578d3865a9128b9c209b011fda6539ec06e7a5", + "reference": "5b578d3865a9128b9c209b011fda6539ec06e7a5", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-json": "*", + "ext-pcre": "*", + "ext-reflection": "*", + "ext-spl": "*", + "php": ">=5.3.3", + "phpspec/prophecy": "~1.3.1", + "phpunit/php-code-coverage": "~2.0", + "phpunit/php-file-iterator": "~1.3.2", + "phpunit/php-text-template": "~1.2", + "phpunit/php-timer": "~1.0.2", + "phpunit/phpunit-mock-objects": "~2.3", + "sebastian/comparator": "~1.1", + "sebastian/diff": "~1.1", + "sebastian/environment": "~1.2", + "sebastian/exporter": "~1.2", + "sebastian/global-state": "~1.0", + "sebastian/version": "~1.0", + "symfony/yaml": "~2.0" + }, + "suggest": { + "phpunit/php-invoker": "~1.1" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.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": "2015-02-05 15:51:19" + }, + { + "name": "phpunit/phpunit-mock-objects", + "version": "2.3.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", + "reference": "c63d2367247365f688544f0d500af90a11a44c65" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/c63d2367247365f688544f0d500af90a11a44c65", + "reference": "c63d2367247365f688544f0d500af90a11a44c65", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "~1.0,>=1.0.1", + "php": ">=5.3.3", + "phpunit/php-text-template": "~1.2" + }, + "require-dev": { + "phpunit/phpunit": "~4.3" + }, + "suggest": { + "ext-soap": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3.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": "Mock Object library for PHPUnit", + "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", + "keywords": [ + "mock", + "xunit" + ], + "time": "2014-10-03 05:12:11" + }, + { + "name": "sebastian/comparator", + "version": "1.1.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "1dd8869519a225f7f2b9eb663e225298fade819e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/1dd8869519a225f7f2b9eb663e225298fade819e", + "reference": "1dd8869519a225f7f2b9eb663e225298fade819e", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "sebastian/diff": "~1.2", + "sebastian/exporter": "~1.2" + }, + "require-dev": { + "phpunit/phpunit": "~4.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.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": "http://www.github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "time": "2015-01-29 16:28:08" + }, + { + "name": "sebastian/diff", + "version": "1.2.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "5843509fed39dee4b356a306401e9dd1a931fec7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/5843509fed39dee4b356a306401e9dd1a931fec7", + "reference": "5843509fed39dee4b356a306401e9dd1a931fec7", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2-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": "http://www.github.com/sebastianbergmann/diff", + "keywords": [ + "diff" + ], + "time": "2014-08-15 10:29:00" + }, + { + "name": "sebastian/environment", + "version": "1.2.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "6e6c71d918088c251b181ba8b3088af4ac336dd7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/6e6c71d918088c251b181ba8b3088af4ac336dd7", + "reference": "6e6c71d918088c251b181ba8b3088af4ac336dd7", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2.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": "2014-10-25 08:00:45" + }, + { + "name": "sebastian/exporter", + "version": "1.2.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "84839970d05254c73cde183a721c7af13aede943" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/84839970d05254c73cde183a721c7af13aede943", + "reference": "84839970d05254c73cde183a721c7af13aede943", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "sebastian/recursion-context": "~1.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2.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": "2015-01-27 07:23:06" + }, + { + "name": "sebastian/global-state", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "c7428acdb62ece0a45e6306f1ae85e1c05b09c01" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/c7428acdb62ece0a45e6306f1ae85e1c05b09c01", + "reference": "c7428acdb62ece0a45e6306f1ae85e1c05b09c01", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.2" + }, + "suggest": { + "ext-uopz": "*" + }, + "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": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "http://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "time": "2014-10-06 09:23:50" + }, + { + "name": "sebastian/recursion-context", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "3989662bbb30a29d20d9faa04a846af79b276252" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/3989662bbb30a29d20d9faa04a846af79b276252", + "reference": "3989662bbb30a29d20d9faa04a846af79b276252", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.4" + }, + "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": "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": "2015-01-24 09:48:32" + }, + { + "name": "sebastian/version", + "version": "1.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "a77d9123f8e809db3fbdea15038c27a95da4058b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/a77d9123f8e809db3fbdea15038c27a95da4058b", + "reference": "a77d9123f8e809db3fbdea15038c27a95da4058b", + "shasum": "" + }, + "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": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "time": "2014-12-15 14:25:24" + }, + { + "name": "symfony/console", + "version": "v2.6.4", + "target-dir": "Symfony/Component/Console", + "source": { + "type": "git", + "url": "https://github.com/symfony/Console.git", + "reference": "e44154bfe3e41e8267d7a3794cd9da9a51cfac34" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/Console/zipball/e44154bfe3e41e8267d7a3794cd9da9a51cfac34", + "reference": "e44154bfe3e41e8267d7a3794cd9da9a51cfac34", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "psr/log": "~1.0", + "symfony/event-dispatcher": "~2.1", + "symfony/process": "~2.1" + }, + "suggest": { + "psr/log": "For using the console logger", + "symfony/event-dispatcher": "", + "symfony/process": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.6-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\Console\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + }, + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "Symfony Console Component", + "homepage": "http://symfony.com", + "time": "2015-01-25 04:39:26" + }, + { + "name": "symfony/event-dispatcher", + "version": "v2.6.4", + "target-dir": "Symfony/Component/EventDispatcher", + "source": { + "type": "git", + "url": "https://github.com/symfony/EventDispatcher.git", + "reference": "f75989f3ab2743a82fe0b03ded2598a2b1546813" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/EventDispatcher/zipball/f75989f3ab2743a82fe0b03ded2598a2b1546813", + "reference": "f75989f3ab2743a82fe0b03ded2598a2b1546813", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "psr/log": "~1.0", + "symfony/config": "~2.0,>=2.0.5", + "symfony/dependency-injection": "~2.6", + "symfony/expression-language": "~2.6", + "symfony/stopwatch": "~2.3" + }, + "suggest": { + "symfony/dependency-injection": "", + "symfony/http-kernel": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.6-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\EventDispatcher\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + }, + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "Symfony EventDispatcher Component", + "homepage": "http://symfony.com", + "time": "2015-02-01 16:10:57" + }, + { + "name": "symfony/filesystem", + "version": "v2.6.4", + "target-dir": "Symfony/Component/Filesystem", + "source": { + "type": "git", + "url": "https://github.com/symfony/Filesystem.git", + "reference": "a1f566d1f92e142fa1593f4555d6d89e3044a9b7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/Filesystem/zipball/a1f566d1f92e142fa1593f4555d6d89e3044a9b7", + "reference": "a1f566d1f92e142fa1593f4555d6d89e3044a9b7", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.6-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\Filesystem\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + }, + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "Symfony Filesystem Component", + "homepage": "http://symfony.com", + "time": "2015-01-03 21:13:09" + }, + { + "name": "symfony/finder", + "version": "v2.6.4", + "target-dir": "Symfony/Component/Finder", + "source": { + "type": "git", + "url": "https://github.com/symfony/Finder.git", + "reference": "16513333bca64186c01609961a2bb1b95b5e1355" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/Finder/zipball/16513333bca64186c01609961a2bb1b95b5e1355", + "reference": "16513333bca64186c01609961a2bb1b95b5e1355", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.6-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\Finder\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + }, + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "Symfony Finder Component", + "homepage": "http://symfony.com", + "time": "2015-01-03 08:01:59" + }, + { + "name": "symfony/stopwatch", + "version": "v2.6.4", + "target-dir": "Symfony/Component/Stopwatch", + "source": { + "type": "git", + "url": "https://github.com/symfony/Stopwatch.git", + "reference": "e8da5286132ba75ce4b4275fbf0f4cd369bfd71c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/Stopwatch/zipball/e8da5286132ba75ce4b4275fbf0f4cd369bfd71c", + "reference": "e8da5286132ba75ce4b4275fbf0f4cd369bfd71c", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.6-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\Stopwatch\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + }, + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "Symfony Stopwatch Component", + "homepage": "http://symfony.com", + "time": "2015-01-03 08:01:59" + }, + { + "name": "symfony/yaml", + "version": "v2.6.4", + "target-dir": "Symfony/Component/Yaml", + "source": { + "type": "git", + "url": "https://github.com/symfony/Yaml.git", + "reference": "60ed7751671113cf1ee7d7778e691642c2e9acd8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/Yaml/zipball/60ed7751671113cf1ee7d7778e691642c2e9acd8", + "reference": "60ed7751671113cf1ee7d7778e691642c2e9acd8", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.6-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\Yaml\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + }, + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "Symfony Yaml Component", + "homepage": "http://symfony.com", + "time": "2015-01-25 04:39:26" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": ">=5.4.0" + }, + "platform-dev": [] +} diff --git a/example/index.php b/example/index.php new file mode 100644 index 0000000..3a44a88 --- /dev/null +++ b/example/index.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use JoliNotif\Driver\GrowlNotifyDriver; +use JoliNotif\Driver\NotifySendDriver; +use JoliNotif\Driver\TerminalNotifierDriver; +use JoliNotif\Notification; +use JoliNotif\Notifier; + +require __DIR__.'/../vendor/autoload.php'; + +$notifier = new Notifier([ + new TerminalNotifierDriver(), + new GrowlNotifyDriver(), + new NotifySendDriver(), +]); + +$notification = new Notification(); +$notification->setTitle('I\'m a notification title'); +$notification->setBody('And this is the body'); +$notification->setIcon(__DIR__.'/notification-icon.png'); + +$notifier->send($notification); diff --git a/example/notification-icon.png b/example/notification-icon.png new file mode 100644 index 0000000..cfead59 Binary files /dev/null and b/example/notification-icon.png differ diff --git a/phpunit.xml.dist b/phpunit.xml.dist new file mode 100644 index 0000000..9781723 --- /dev/null +++ b/phpunit.xml.dist @@ -0,0 +1,27 @@ + + + + + + + + + + ./tests/ + + + + + + ./ + + ./vendor + + + + diff --git a/src/Driver/CliBasedDriver.php b/src/Driver/CliBasedDriver.php new file mode 100644 index 0000000..bb3744a --- /dev/null +++ b/src/Driver/CliBasedDriver.php @@ -0,0 +1,49 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace JoliNotif\Driver; + +use JoliNotif\Notification; +use Symfony\Component\Process\ProcessBuilder; + +abstract class CliBasedDriver implements Driver +{ + /** + * Get the arguments that will be passed to the process in order to send the notification. + * + * @param Notification $notification + * + * @return array + */ + abstract protected function getProcessArguments(Notification $notification); + + /** + * Get the binary to check existence. + * + * @return string + */ + abstract protected function getBinary(); + + /** + * {@inheritdoc} + */ + public function send(Notification $notification) + { + $arguments = $this->getProcessArguments($notification); + + $builder = new ProcessBuilder($arguments); + $process = $builder->getProcess(); + + $process->run(); + + return $process->isSuccessful(); + } +} diff --git a/src/Driver/Driver.php b/src/Driver/Driver.php new file mode 100644 index 0000000..675160e --- /dev/null +++ b/src/Driver/Driver.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace JoliNotif\Driver; + +use JoliNotif\Notification; + +interface Driver +{ + const PRIORITY_LOW = 0; + const PRIORITY_MEDIUM = 50; + const PRIORITY_HIGH = 100; + + /** + * This method is called to check whether the driver can be used on the current system or not. + * + * @return bool + */ + public function isSupported(); + + /** + * The supported driver with the higher priority will be preferred. + * + * @return int + */ + public function getPriority(); + + /** + * Send a the given notification. + * + * @param \JoliNotif\Notification $notification + * + * @return bool + */ + public function send(Notification $notification); +} diff --git a/src/Driver/GrowlNotifyDriver.php b/src/Driver/GrowlNotifyDriver.php new file mode 100644 index 0000000..3e36b46 --- /dev/null +++ b/src/Driver/GrowlNotifyDriver.php @@ -0,0 +1,61 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace JoliNotif\Driver; + +use JoliNotif\Notification; + +/** + * This driver can be used on Mac OS X when growlnotify command is available. + */ +class GrowlNotifyDriver extends UnixBasedDriver +{ + /** + * {@inheritdoc} + */ + public function getBinary() + { + return 'growlnotify'; + } + + /** + * {@inheritdoc} + */ + public function getPriority() + { + return static::PRIORITY_LOW; + } + + /** + * {@inheritdoc} + */ + protected function getProcessArguments(Notification $notification) + { + $arguments = [ + $this->getBinary(), + '--message', + $notification->getBody(), + ]; + + if ($notification->getTitle()) { + $arguments[] = '--title'; + $arguments[] = $notification->getTitle(); + } + + // Require 10.9+ + if (0 < strlen($notification->getIcon())) { + $arguments[] = '--image'; + $arguments[] = $notification->getIcon(); + } + + return $arguments; + } +} diff --git a/src/Driver/NotifySendDriver.php b/src/Driver/NotifySendDriver.php new file mode 100644 index 0000000..734ee1b --- /dev/null +++ b/src/Driver/NotifySendDriver.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace JoliNotif\Driver; + +use JoliNotif\Notification; + +/** + * This driver can be used on most Linux distributions, using the command notify-send. + * This command is packaged in libnotify-bin. + */ +class NotifySendDriver extends UnixBasedDriver +{ + /** + * {@inheritdoc} + */ + public function getBinary() + { + return 'notify-send'; + } + + /** + * {@inheritdoc} + */ + public function getPriority() + { + return static::PRIORITY_MEDIUM; + } + + /** + * {@inheritdoc} + */ + protected function getProcessArguments(Notification $notification) + { + $arguments = [ + $this->getBinary(), + ]; + + if (0 < strlen($notification->getIcon())) { + $arguments[] = '--icon'; + $arguments[] = $notification->getIcon(); + } + + if ($notification->getTitle()) { + $arguments[] = $notification->getTitle(); + } + + $arguments[] = $notification->getBody(); + + return $arguments; + } +} diff --git a/src/Driver/TerminalNotifierDriver.php b/src/Driver/TerminalNotifierDriver.php new file mode 100644 index 0000000..8efcf25 --- /dev/null +++ b/src/Driver/TerminalNotifierDriver.php @@ -0,0 +1,61 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace JoliNotif\Driver; + +use JoliNotif\Notification; + +/** + * This driver can be used on Mac OS X 10.8, or higher, using the terminal-notifier binary. + */ +class TerminalNotifierDriver extends UnixBasedDriver +{ + /** + * {@inheritdoc} + */ + public function getBinary() + { + return 'terminal-notifier'; + } + + /** + * {@inheritdoc} + */ + public function getPriority() + { + return static::PRIORITY_MEDIUM; + } + + /** + * {@inheritdoc} + */ + protected function getProcessArguments(Notification $notification) + { + $arguments = [ + $this->getBinary(), + '-message', + $notification->getBody(), + ]; + + if ($notification->getTitle()) { + $arguments[] = '-title'; + $arguments[] = $notification->getTitle(); + } + + // Require 10.9+ + if (0 < strlen($notification->getIcon())) { + $arguments[] = '-contentImage'; + $arguments[] = $notification->getIcon(); + } + + return $arguments; + } +} diff --git a/src/Driver/UnixBasedDriver.php b/src/Driver/UnixBasedDriver.php new file mode 100644 index 0000000..eb85378 --- /dev/null +++ b/src/Driver/UnixBasedDriver.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace JoliNotif\Driver; + +use Symfony\Component\Process\ProcessBuilder; + +abstract class UnixBasedDriver extends CliBasedDriver +{ + /** + * {@inheritdoc} + */ + public function isSupported() + { + if (DIRECTORY_SEPARATOR !== '/') { + return false; + } + + $bin = $this->getBinary(); + + // Do not use the which programm to check if a binary exists. + // See also http://stackoverflow.com/questions/592620/check-if-a-program-exists-from-a-bash-script + $builder = new ProcessBuilder([ + 'command', + '-v', + $bin, + '>/dev/null', + '2>&1', + ]); + $process = $builder->getProcess(); + + $process->run(); + + return $process->isSuccessful(); + } +} diff --git a/src/Exception/Exception.php b/src/Exception/Exception.php new file mode 100644 index 0000000..736a93f --- /dev/null +++ b/src/Exception/Exception.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace JoliNotif\Exception; + +interface Exception +{ +} diff --git a/src/Exception/InvalidNotificationException.php b/src/Exception/InvalidNotificationException.php new file mode 100644 index 0000000..4c50b49 --- /dev/null +++ b/src/Exception/InvalidNotificationException.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace JoliNotif\Exception; + +use JoliNotif\Notification; + +class InvalidNotificationException extends \LogicException implements Exception +{ + /** + * @var \JoliNotif\Notification + */ + private $notification; + + public function __construct(Notification $notification, $message = "", $code = 0, Exception $previous = null) + { + $this->notification = $notification; + + parent::__construct($message, $code, $previous); + } + + /** + * @return Notification + */ + public function getNotification() + { + return $this->notification; + } +} diff --git a/src/Exception/SystemNotSupportedException.php b/src/Exception/SystemNotSupportedException.php new file mode 100644 index 0000000..16f2dc0 --- /dev/null +++ b/src/Exception/SystemNotSupportedException.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace JoliNotif\Exception; + +class SystemNotSupportedException extends \RuntimeException implements Exception +{ +} diff --git a/src/Notification.php b/src/Notification.php new file mode 100644 index 0000000..f71b9e9 --- /dev/null +++ b/src/Notification.php @@ -0,0 +1,90 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace JoliNotif; + +class Notification +{ + /** + * @var string + */ + private $title; + + /** + * @var string + */ + private $body; + + /** + * @var string + */ + private $icon; + + /** + * @return string + */ + public function getTitle() + { + return $this->title; + } + + /** + * @param string $title + * + * @return $this + */ + public function setTitle($title) + { + $this->title = $title; + + return $this; + } + + /** + * @return string + */ + public function getBody() + { + return $this->body; + } + + /** + * @param string $body + * + * @return $this + */ + public function setBody($body) + { + $this->body = $body; + + return $this; + } + + /** + * @return string + */ + public function getIcon() + { + return $this->icon; + } + + /** + * @param string $icon + * + * @return $this + */ + public function setIcon($icon) + { + $this->icon = $icon; + + return $this; + } +} diff --git a/src/Notifier.php b/src/Notifier.php new file mode 100644 index 0000000..c07a8c3 --- /dev/null +++ b/src/Notifier.php @@ -0,0 +1,91 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace JoliNotif; + +use JoliNotif\Driver\Driver; +use JoliNotif\Exception\InvalidNotificationException; +use JoliNotif\Exception\SystemNotSupportedException; + +class Notifier +{ + /** + * @var Driver[] + */ + private $drivers = []; + + /** + * @var Driver + */ + private $driverInUse; + + /** + * @param Driver[] $drivers + * + * @throw SystemNotSupportedException if no drivers are supported + */ + public function __construct(array $drivers) + { + $this->drivers = $drivers; + + $this->driverInUse = $this->chooseBestDriver(); + } + + /** + * @return Driver + */ + public function getDriverInUse() + { + return $this->driverInUse; + } + + /** + * @param Notification $notification + * + * @return bool + */ + public function send(Notification $notification) + { + if (strlen($notification->getBody()) < 1) { + throw new InvalidNotificationException($notification, 'Notification body can not be empty'); + } + + return $this->driverInUse->send($notification); + } + + /** + * @return Driver + * + * @throw SystemNotSupportedException if no drivers are supported + */ + private function chooseBestDriver() + { + $bestDriver = null; + + foreach ($this->drivers as $driver) { + if (!$driver->isSupported()) { + continue; + } + + if (null !== $bestDriver && $bestDriver->getPriority() >= $driver->getPriority()) { + continue; + } + + $bestDriver = $driver; + } + + if (null === $bestDriver) { + throw new SystemNotSupportedException('No drivers support your system'); + } + + return $bestDriver; + } +} diff --git a/tests/Driver/CliBasedDriverTestTrait.php b/tests/Driver/CliBasedDriverTestTrait.php new file mode 100644 index 0000000..9753f3c --- /dev/null +++ b/tests/Driver/CliBasedDriverTestTrait.php @@ -0,0 +1,57 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace JoliNotif\tests\Driver; + +use JoliNotif\Notification; + +/** + * The class using this trait should extend DriverTestCase. + */ +trait CliBasedDriverTestTrait +{ + /** + * @param \JoliNotif\Notification $notification + * + * @dataProvider provideValidNotifications + */ + public function testSendAcceptAnyValidNotification(Notification $notification) + { + try { + $arguments = $this->invokeMethod($this->getDriver(), 'getProcessArguments', [$notification]); + $this->assertInternalType('array', $arguments); + $this->assertGreaterThan(1, count($arguments)); + } catch(\Exception $e) { + $this->fail($e->getMessage()); + } + } + + /** + * @return array + */ + public static function provideValidNotifications() + { + return [ + [(new Notification()) + ->setBody('The notification body')], + [(new Notification()) + ->setBody('The notification body') + ->setTitle('The notification title')], + [(new Notification()) + ->setBody('The notification body') + ->setIcon('example/notification-icon.png')], + [(new Notification()) + ->setBody('The notification body') + ->setTitle('The notification title') + ->setIcon('example/notification-icon.png')], + ]; + } +} diff --git a/tests/Driver/DriverTestCase.php b/tests/Driver/DriverTestCase.php new file mode 100644 index 0000000..27d5f01 --- /dev/null +++ b/tests/Driver/DriverTestCase.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace JoliNotif\tests\Driver; + +use JoliNotif\Driver\Driver; +use JoliNotif\Notification; + +abstract class DriverTestCase extends \PHPUnit_Framework_TestCase +{ + /** + * @return Driver + */ + abstract protected function getDriver(); + + /** + * Call protected/private method of a class. + * + * @param object $object Instantiated object that we will run method on. + * @param string $methodName Method name to call + * @param array $parameters Array of parameters to pass into method. + * + * @return mixed Method return. + */ + protected function invokeMethod($object, $methodName, array $parameters = array()) + { + $reflection = new \ReflectionClass(get_class($object)); + $method = $reflection->getMethod($methodName); + $method->setAccessible(true); + + return $method->invokeArgs($object, $parameters); + } +} diff --git a/tests/Driver/GrowlNotifyDriverTest.php b/tests/Driver/GrowlNotifyDriverTest.php new file mode 100644 index 0000000..6f27958 --- /dev/null +++ b/tests/Driver/GrowlNotifyDriverTest.php @@ -0,0 +1,109 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace JoliNotif\tests\Driver; + +use JoliNotif\Driver\Driver; +use JoliNotif\Driver\GrowlNotifyDriver; +use JoliNotif\Notification; + +class GrowlNotifyDriverTest extends DriverTestCase +{ + const BINARY = 'growlnotify'; + + use CliBasedDriverTestTrait; + use UnixBasedDriverTestTrait; + + protected function getDriver() + { + return new GrowlNotifyDriver(); + } + + public function testGetBinary() + { + $driver = $this->getDriver(); + + $this->assertSame(self::BINARY, $driver->getBinary()); + } + + public function testGetPriority() + { + $driver = $this->getDriver(); + + $this->assertSame(Driver::PRIORITY_LOW, $driver->getPriority()); + } + + /** + * @param \JoliNotif\Notification $notification + * @param array $expectedArguments + * + * @dataProvider provideNotifications + */ + public function testGetProcessArguments(Notification $notification, array $expectedArguments) + { + try { + $arguments = $this->invokeMethod($this->getDriver(), 'getProcessArguments', [$notification]); + $this->assertInternalType('array', $arguments); + $this->assertEquals($expectedArguments, $arguments); + } catch(\Exception $e) { + $this->fail($e->getMessage()); + } + } + + /** + * @return array + */ + public static function provideNotifications() + { + return [ + [ + (new Notification()) + ->setBody('The notification body'), + [ + self::BINARY, + '--message', 'The notification body' + ], + ], + [ + (new Notification()) + ->setBody('The notification body') + ->setTitle('The notification title'), + [ + self::BINARY, + '--message', 'The notification body', + '--title', 'The notification title' + ], + ], + [ + (new Notification()) + ->setBody('The notification body') + ->setIcon('/home/toto/Images/my-icon.png'), + [ + self::BINARY, + '--message', 'The notification body', + '--image', '/home/toto/Images/my-icon.png' + ], + ], + [ + (new Notification()) + ->setBody('The notification body') + ->setTitle('The notification title') + ->setIcon('/home/toto/Images/my-icon.png'), + [ + self::BINARY, + '--message', 'The notification body', + '--title', 'The notification title', + '--image', '/home/toto/Images/my-icon.png' + ], + ], + ]; + } +} diff --git a/tests/Driver/NotifySendDriverTest.php b/tests/Driver/NotifySendDriverTest.php new file mode 100644 index 0000000..1ab188c --- /dev/null +++ b/tests/Driver/NotifySendDriverTest.php @@ -0,0 +1,109 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace JoliNotif\tests\Driver; + +use JoliNotif\Driver\Driver; +use JoliNotif\Driver\NotifySendDriver; +use JoliNotif\Notification; + +class NotifySendDriverTest extends DriverTestCase +{ + const BINARY = 'notify-send'; + + use CliBasedDriverTestTrait; + use UnixBasedDriverTestTrait; + + protected function getDriver() + { + return new NotifySendDriver(); + } + + public function testGetBinary() + { + $driver = $this->getDriver(); + + $this->assertSame(self::BINARY, $driver->getBinary()); + } + + public function testGetPriority() + { + $driver = $this->getDriver(); + + $this->assertSame(Driver::PRIORITY_MEDIUM, $driver->getPriority()); + } + + /** + * @param Notification $notification + * @param array $expectedArguments + * + * @dataProvider provideNotifications + */ + public function testGetProcessArguments(Notification $notification, array $expectedArguments) + { + try { + $arguments = $this->invokeMethod($this->getDriver(), 'getProcessArguments', [$notification]); + $this->assertInternalType('array', $arguments); + $this->assertEquals($expectedArguments, $arguments); + } catch(\Exception $e) { + $this->fail($e->getMessage()); + } + } + + /** + * @return array + */ + public static function provideNotifications() + { + return [ + [ + (new Notification()) + ->setBody('The notification body'), + [ + self::BINARY, + 'The notification body', + ], + ], + [ + (new Notification()) + ->setBody('The notification body') + ->setTitle('The notification title'), + [ + self::BINARY, + 'The notification title', + 'The notification body', + ], + ], + [ + (new Notification()) + ->setBody('The notification body') + ->setIcon('/home/toto/Images/my-icon.png'), + [ + self::BINARY, + '--icon', '/home/toto/Images/my-icon.png', + 'The notification body', + ], + ], + [ + (new Notification()) + ->setBody('The notification body') + ->setTitle('The notification title') + ->setIcon('/home/toto/Images/my-icon.png'), + [ + self::BINARY, + '--icon', '/home/toto/Images/my-icon.png', + 'The notification title', + 'The notification body' + ], + ], + ]; + } +} diff --git a/tests/Driver/TerminalNotifierDriverTest.php b/tests/Driver/TerminalNotifierDriverTest.php new file mode 100644 index 0000000..4e48f75 --- /dev/null +++ b/tests/Driver/TerminalNotifierDriverTest.php @@ -0,0 +1,109 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace JoliNotif\tests\Driver; + +use JoliNotif\Driver\Driver; +use JoliNotif\Driver\TerminalNotifierDriver; +use JoliNotif\Notification; + +class TerminalNotifierDriverTest extends DriverTestCase +{ + const BINARY = 'terminal-notifier'; + + use CliBasedDriverTestTrait; + use UnixBasedDriverTestTrait; + + protected function getDriver() + { + return new TerminalNotifierDriver(); + } + + public function testGetBinary() + { + $driver = $this->getDriver(); + + $this->assertSame(self::BINARY, $driver->getBinary()); + } + + public function testGetPriority() + { + $driver = $this->getDriver(); + + $this->assertSame(Driver::PRIORITY_MEDIUM, $driver->getPriority()); + } + + /** + * @param \JoliNotif\Notification $notification + * @param array $expectedArguments + * + * @dataProvider provideNotifications + */ + public function testGetProcessArguments(Notification $notification, array $expectedArguments) + { + try { + $arguments = $this->invokeMethod($this->getDriver(), 'getProcessArguments', [$notification]); + $this->assertInternalType('array', $arguments); + $this->assertEquals($expectedArguments, $arguments); + } catch(\Exception $e) { + $this->fail($e->getMessage()); + } + } + + /** + * @return array + */ + public static function provideNotifications() + { + return [ + [ + (new Notification()) + ->setBody('The notification body'), + [ + self::BINARY, + '-message', 'The notification body' + ], + ], + [ + (new Notification()) + ->setBody('The notification body') + ->setTitle('The notification title'), + [ + self::BINARY, + '-message', 'The notification body', + '-title', 'The notification title' + ], + ], + [ + (new Notification()) + ->setBody('The notification body') + ->setIcon('/home/toto/Images/my-icon.png'), + [ + self::BINARY, + '-message', 'The notification body', + '-contentImage', '/home/toto/Images/my-icon.png' + ], + ], + [ + (new Notification()) + ->setBody('The notification body') + ->setTitle('The notification title') + ->setIcon('/home/toto/Images/my-icon.png'), + [ + self::BINARY, + '-message', 'The notification body', + '-title', 'The notification title', + '-contentImage', '/home/toto/Images/my-icon.png' + ], + ], + ]; + } +} diff --git a/tests/Driver/UnixBasedDriverTestTrait.php b/tests/Driver/UnixBasedDriverTestTrait.php new file mode 100644 index 0000000..fa99cc5 --- /dev/null +++ b/tests/Driver/UnixBasedDriverTestTrait.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace JoliNotif\tests\Driver; + +/** + * The class using this trait should define a BINARY constant and extend DriverTestCase. + */ +trait UnixBasedDriverTestTrait +{ + public function testIsSupported() + { + $supported = DIRECTORY_SEPARATOR === '/'; + + if ($supported) { + $commandLine = 'command -v '.static::BINARY.' >/dev/null 2>&1 '; + passthru($commandLine, $return); + + $supported = $return === 0; + } + + $this->assertSame($supported, $this->getDriver()->isSupported()); + } +} diff --git a/tests/NotifierTest.php b/tests/NotifierTest.php new file mode 100644 index 0000000..34ded11 --- /dev/null +++ b/tests/NotifierTest.php @@ -0,0 +1,164 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace JoliNotif\tests; + +use JoliNotif\Notification; +use JoliNotif\Notifier; +use JoliNotif\tests\fixtures\ConfigurableDriver; + +class NotifierTest extends \PHPUnit_Framework_TestCase +{ + public function testNoDriverOrNoSupportedDriverThrowsException() + { + try { + new Notifier([]); + $this->fail('Expected a SystemNotSupportedException'); + } catch (\Exception $e) { + $this->assertInstanceOf('JoliNotif\Exception\SystemNotSupportedException', $e); + } + + try { + new Notifier([ + new ConfigurableDriver(false), + new ConfigurableDriver(false), + ]); + $this->fail('Expected a SystemNotSupportedException'); + } catch (\Exception $e) { + $this->assertInstanceOf('JoliNotif\Exception\SystemNotSupportedException', $e); + } + } + + public function testTheBestSupportedDriverIsUsed() + { + // test case + $driver = new ConfigurableDriver(true); + + $notifier = new Notifier([ + $driver, + ]); + $this->assertSame($driver, $notifier->getDriverInUse()); + + // test case + $driver1 = new ConfigurableDriver(false); + $driver2 = new ConfigurableDriver(true); + $driver3 = new ConfigurableDriver(true); + $driver4 = new ConfigurableDriver(true); + + $notifier = new Notifier([ + $driver1, + $driver2, + $driver3, + $driver4, + ]); + $this->assertSame($driver2, $notifier->getDriverInUse()); + + // test case + $driver1 = new ConfigurableDriver(false); + $driver2 = new ConfigurableDriver(true, 5); + $driver3 = new ConfigurableDriver(false); + $driver4 = new ConfigurableDriver(true, 8); + $driver5 = new ConfigurableDriver(true, 6); + + $notifier = new Notifier([ + $driver1, + $driver2, + $driver3, + $driver4, + $driver5, + ]); + $this->assertSame($driver4, $notifier->getDriverInUse()); + + // test case + $driver1 = new ConfigurableDriver(false); + $driver2 = new ConfigurableDriver(true, 5); + $driver3 = new ConfigurableDriver(true, 8); + $driver4 = new ConfigurableDriver(false); + $driver5 = new ConfigurableDriver(true, 8); + + $notifier = new Notifier([ + $driver1, + $driver2, + $driver3, + $driver4, + $driver5, + ]); + $this->assertSame($driver3, $notifier->getDriverInUse()); + } + + public function testSendThrowsExceptionWhenNotificationHasAnEmptyBody() + { + $notifier = new Notifier([new ConfigurableDriver(true)]); + + // test case + $notification = new Notification(); + + try { + $notifier->send($notification); + $this->fail('Expected a InvalidNotificationException'); + } catch (\Exception $e) { + $this->assertInstanceOf('JoliNotif\Exception\InvalidNotificationException', $e); + } + + // test case + $notification = new Notification(); + $notification->setBody(''); + + try { + $notifier->send($notification); + $this->fail('Expected a InvalidNotificationException'); + } catch (\Exception $e) { + $this->assertInstanceOf('JoliNotif\Exception\InvalidNotificationException', $e); + } + } + + public function testSendUsesTheBestDriverAndReturnsItsReturn() + { + $notification = new Notification(); + $notification->setBody('My notification'); + + // test case + $driver = new ConfigurableDriver(true, 2, false); + + $notifier = new Notifier([ + $driver, + ]); + $this->assertFalse($notifier->send($notification)); + + // test case + $driver1 = new ConfigurableDriver(false, 0, false); + $driver2 = new ConfigurableDriver(true, 0, true); + $driver3 = new ConfigurableDriver(true, 0, false); + $driver4 = new ConfigurableDriver(true, 0, false); + + $notifier = new Notifier([ + $driver1, + $driver2, + $driver3, + $driver4, + ]); + $this->assertTrue($notifier->send($notification)); + + // test case + $driver1 = new ConfigurableDriver(false, 0, false); + $driver2 = new ConfigurableDriver(true, 0, false); + $driver3 = new ConfigurableDriver(true, 5, true); + $driver4 = new ConfigurableDriver(true, 2, false); + + $notifier = new Notifier([ + $driver1, + $driver2, + $driver3, + $driver4, + ]); + $this->assertTrue($notifier->send($notification)); + } +} diff --git a/tests/fixtures/ConfigurableDriver.php b/tests/fixtures/ConfigurableDriver.php new file mode 100644 index 0000000..95b243c --- /dev/null +++ b/tests/fixtures/ConfigurableDriver.php @@ -0,0 +1,69 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace JoliNotif\tests\fixtures; + +use JoliNotif\Driver\Driver; +use JoliNotif\Notification; + +class ConfigurableDriver implements Driver +{ + /** + * @var bool + */ + private $supported; + + /** + * @var int + */ + private $priority; + + /** + * @var bool + */ + private $sendReturn; + + /** + * @param $supported + * @param $priority + * @param $sendReturn + */ + public function __construct($supported, $priority = Driver::PRIORITY_MEDIUM, $sendReturn = true) + { + $this->supported = $supported; + $this->priority = $priority; + $this->sendReturn = $sendReturn; + } + + /** + * {@inheritdoc} + */ + public function isSupported() + { + return $this->supported; + } + + /** + * {@inheritdoc} + */ + public function getPriority() + { + return $this->priority; + } + + /** + * {@inheritdoc} + */ + public function send(Notification $notification) + { + return $this->sendReturn; + } +}