[COMPOSER] Add new php-ffmpeg package

This commit is contained in:
t3nma 2020-08-07 23:42:38 +01:00
parent 0a6bb5190f
commit c527ad0803
8874 changed files with 1090008 additions and 154 deletions

View File

@ -29,6 +29,7 @@
"openid/php-openid": "^2.3",
"paragonie/constant_time_encoding": "^1.0.4",
"pear/console_getopt": "^1.4",
"php-ffmpeg/php-ffmpeg": "^0.16",
"phpseclib/phpseclib": "dev-master#f815e43077da67d3dd5b4d18a45753f5b79c1ab9",
"predis/predis": "^1.1",
"stomp-php/stomp-php": "^4.5.1"

617
composer.lock generated
View File

@ -4,8 +4,70 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "9ada0acf1c46320bb59933b35031a551",
"content-hash": "4e2a2a7254378e2dc8be73be627b766b",
"packages": [
{
"name": "alchemy/binary-driver",
"version": "v5.2.0",
"source": {
"type": "git",
"url": "https://github.com/alchemy-fr/BinaryDriver.git",
"reference": "e0615cdff315e6b4b05ada67906df6262a020d22"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/alchemy-fr/BinaryDriver/zipball/e0615cdff315e6b4b05ada67906df6262a020d22",
"reference": "e0615cdff315e6b4b05ada67906df6262a020d22",
"shasum": ""
},
"require": {
"evenement/evenement": "^3.0|^2.0|^1.0",
"php": ">=5.5",
"psr/log": "^1.0",
"symfony/process": "^2.3|^3.0|^4.0|^5.0"
},
"require-dev": {
"phpunit/phpunit": "^4.0|^5.0"
},
"type": "library",
"autoload": {
"psr-0": {
"Alchemy": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Le Goff",
"email": "legoff.n@gmail.com"
},
{
"name": "Romain Neutron",
"email": "imprec@gmail.com",
"homepage": "http://www.lickmychip.com/"
},
{
"name": "Phraseanet Team",
"email": "info@alchemy.fr",
"homepage": "http://www.phraseanet.com/"
},
{
"name": "Jens Hausdorf",
"email": "mail@jens-hausdorf.de",
"homepage": "https://jens-hausdorf.de",
"role": "Maintainer"
}
],
"description": "A set of tools to build binary drivers",
"keywords": [
"binary",
"driver"
],
"time": "2020-02-12T19:35:11+00:00"
},
{
"name": "apereo/phpcas",
"version": "1.3.8",
@ -202,6 +264,102 @@
],
"time": "2019-11-03T00:18:51+00:00"
},
{
"name": "doctrine/cache",
"version": "1.10.2",
"source": {
"type": "git",
"url": "https://github.com/doctrine/cache.git",
"reference": "13e3381b25847283a91948d04640543941309727"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/doctrine/cache/zipball/13e3381b25847283a91948d04640543941309727",
"reference": "13e3381b25847283a91948d04640543941309727",
"shasum": ""
},
"require": {
"php": "~7.1 || ^8.0"
},
"conflict": {
"doctrine/common": ">2.2,<2.4"
},
"require-dev": {
"alcaeus/mongo-php-adapter": "^1.1",
"doctrine/coding-standard": "^6.0",
"mongodb/mongodb": "^1.1",
"phpunit/phpunit": "^7.0",
"predis/predis": "~1.0"
},
"suggest": {
"alcaeus/mongo-php-adapter": "Required to use legacy MongoDB driver"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.9.x-dev"
}
},
"autoload": {
"psr-4": {
"Doctrine\\Common\\Cache\\": "lib/Doctrine/Common/Cache"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Guilherme Blanco",
"email": "guilhermeblanco@gmail.com"
},
{
"name": "Roman Borschel",
"email": "roman@code-factory.org"
},
{
"name": "Benjamin Eberlei",
"email": "kontakt@beberlei.de"
},
{
"name": "Jonathan Wage",
"email": "jonwage@gmail.com"
},
{
"name": "Johannes Schmitt",
"email": "schmittjoh@gmail.com"
}
],
"description": "PHP Doctrine Cache library is a popular cache implementation that supports many different drivers such as redis, memcache, apc, mongodb and others.",
"homepage": "https://www.doctrine-project.org/projects/cache.html",
"keywords": [
"abstraction",
"apcu",
"cache",
"caching",
"couchdb",
"memcached",
"php",
"redis",
"xcache"
],
"funding": [
{
"url": "https://www.doctrine-project.org/sponsorship.html",
"type": "custom"
},
{
"url": "https://www.patreon.com/phpdoctrine",
"type": "patreon"
},
{
"url": "https://tidelift.com/funding/github/packagist/doctrine%2Fcache",
"type": "tidelift"
}
],
"time": "2020-07-07T18:54:01+00:00"
},
{
"name": "embed/embed",
"version": "v3.4.8",
@ -256,6 +414,49 @@
],
"time": "2020-07-03T15:04:01+00:00"
},
{
"name": "evenement/evenement",
"version": "v3.0.1",
"source": {
"type": "git",
"url": "https://github.com/igorw/evenement.git",
"reference": "531bfb9d15f8aa57454f5f0285b18bec903b8fb7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/igorw/evenement/zipball/531bfb9d15f8aa57454f5f0285b18bec903b8fb7",
"reference": "531bfb9d15f8aa57454f5f0285b18bec903b8fb7",
"shasum": ""
},
"require": {
"php": ">=7.0"
},
"require-dev": {
"phpunit/phpunit": "^6.0"
},
"type": "library",
"autoload": {
"psr-0": {
"Evenement": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Igor Wiedler",
"email": "igor@wiedler.ch"
}
],
"description": "Événement is a very simple event dispatching library for PHP",
"keywords": [
"event-dispatcher",
"event-emitter"
],
"time": "2017-07-23T21:35:13+00:00"
},
{
"name": "ezyang/htmlpurifier",
"version": "v4.13.0",
@ -792,6 +993,46 @@
],
"time": "2019-12-02T02:32:27+00:00"
},
{
"name": "neutron/temporary-filesystem",
"version": "2.4",
"source": {
"type": "git",
"url": "https://github.com/romainneutron/Temporary-Filesystem.git",
"reference": "3c55497da8d7762fb4dcabc91d54a5de510e3c99"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/romainneutron/Temporary-Filesystem/zipball/3c55497da8d7762fb4dcabc91d54a5de510e3c99",
"reference": "3c55497da8d7762fb4dcabc91d54a5de510e3c99",
"shasum": ""
},
"require": {
"php": "^5.6 || ^7.0",
"symfony/filesystem": "^2.3 || ^3.0 || ^4.0 || ^5.0"
},
"require-dev": {
"symfony/phpunit-bridge": "^5.0.4"
},
"type": "library",
"autoload": {
"psr-0": {
"Neutron": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Romain Neutron",
"email": "imprec@gmail.com"
}
],
"description": "Symfony filesystem extension to handle temporary files",
"time": "2020-02-17T15:27:36+00:00"
},
{
"name": "openid/php-openid",
"version": "2.3.0",
@ -1000,6 +1241,90 @@
"description": "More info available on: http://pear.php.net/package/Console_Getopt",
"time": "2019-11-20T18:27:48+00:00"
},
{
"name": "php-ffmpeg/php-ffmpeg",
"version": "v0.16",
"source": {
"type": "git",
"url": "https://github.com/PHP-FFMpeg/PHP-FFMpeg.git",
"reference": "4175c02b7d9f7e1a02cec2ba73474266ba2c5fa1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHP-FFMpeg/PHP-FFMpeg/zipball/4175c02b7d9f7e1a02cec2ba73474266ba2c5fa1",
"reference": "4175c02b7d9f7e1a02cec2ba73474266ba2c5fa1",
"shasum": ""
},
"require": {
"alchemy/binary-driver": "^1.5 || ~2.0.0 || ^5.0",
"doctrine/cache": "^1.0",
"evenement/evenement": "^3.0 || ^2.0 || ^1.0",
"neutron/temporary-filesystem": "^2.1.1",
"php": "^5.3.9 || ^7.0"
},
"require-dev": {
"sami/sami": "~1.0",
"silex/silex": "~1.0",
"symfony/phpunit-bridge": "^5.0.4"
},
"suggest": {
"php-ffmpeg/extras": "A compilation of common audio & video drivers for PHP-FFMpeg"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "0.7-dev"
}
},
"autoload": {
"psr-0": {
"FFMpeg": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Romain Neutron",
"email": "imprec@gmail.com",
"homepage": "http://www.lickmychip.com/"
},
{
"name": "Phraseanet Team",
"email": "info@alchemy.fr",
"homepage": "http://www.phraseanet.com/"
},
{
"name": "Patrik Karisch",
"email": "patrik@karisch.guru",
"homepage": "http://www.karisch.guru"
},
{
"name": "Romain Biard",
"email": "romain.biard@gmail.com",
"homepage": "https://www.strime.io/"
},
{
"name": "Jens Hausdorf",
"email": "hello@jens-hausdorf.de",
"homepage": "https://jens-hausdorf.de"
}
],
"description": "FFMpeg PHP, an Object Oriented library to communicate with AVconv / ffmpeg",
"keywords": [
"audio",
"audio processing",
"avconv",
"avprobe",
"ffmpeg",
"ffprobe",
"video",
"video processing"
],
"time": "2020-03-29T09:57:10+00:00"
},
{
"name": "phpseclib/phpseclib",
"version": "dev-master",
@ -1194,6 +1519,53 @@
],
"time": "2016-08-06T14:39:51+00:00"
},
{
"name": "psr/log",
"version": "1.1.3",
"source": {
"type": "git",
"url": "https://github.com/php-fig/log.git",
"reference": "0f73288fd15629204f9d42b7055f72dacbe811fc"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/log/zipball/0f73288fd15629204f9d42b7055f72dacbe811fc",
"reference": "0f73288fd15629204f9d42b7055f72dacbe811fc",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.1.x-dev"
}
},
"autoload": {
"psr-4": {
"Psr\\Log\\": "Psr/Log/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "http://www.php-fig.org/"
}
],
"description": "Common interface for logging libraries",
"homepage": "https://github.com/php-fig/log",
"keywords": [
"log",
"psr",
"psr-3"
],
"time": "2020-03-23T09:12:05+00:00"
},
{
"name": "ralouphie/getallheaders",
"version": "3.0.3",
@ -1302,6 +1674,104 @@
}
],
"time": "2020-06-15T18:01:49+00:00"
},
{
"name": "symfony/filesystem",
"version": "v3.0.9",
"source": {
"type": "git",
"url": "https://github.com/symfony/filesystem.git",
"reference": "b2da5009d9bacbd91d83486aa1f44c793a8c380d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/filesystem/zipball/b2da5009d9bacbd91d83486aa1f44c793a8c380d",
"reference": "b2da5009d9bacbd91d83486aa1f44c793a8c380d",
"shasum": ""
},
"require": {
"php": ">=5.5.9"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.0-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Component\\Filesystem\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony Filesystem Component",
"homepage": "https://symfony.com",
"time": "2016-07-20T05:43:46+00:00"
},
{
"name": "symfony/process",
"version": "v2.8.52",
"source": {
"type": "git",
"url": "https://github.com/symfony/process.git",
"reference": "c3591a09c78639822b0b290d44edb69bf9f05dc8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/process/zipball/c3591a09c78639822b0b290d44edb69bf9f05dc8",
"reference": "c3591a09c78639822b0b290d44edb69bf9f05dc8",
"shasum": ""
},
"require": {
"php": ">=5.3.9"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.8-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Component\\Process\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony Process Component",
"homepage": "https://symfony.com",
"time": "2018-11-11T11:18:13+00:00"
}
],
"packages-dev": [
@ -3356,53 +3826,6 @@
],
"time": "2017-02-14T16:28:37+00:00"
},
{
"name": "psr/log",
"version": "1.1.3",
"source": {
"type": "git",
"url": "https://github.com/php-fig/log.git",
"reference": "0f73288fd15629204f9d42b7055f72dacbe811fc"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/log/zipball/0f73288fd15629204f9d42b7055f72dacbe811fc",
"reference": "0f73288fd15629204f9d42b7055f72dacbe811fc",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.1.x-dev"
}
},
"autoload": {
"psr-4": {
"Psr\\Log\\": "Psr/Log/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "http://www.php-fig.org/"
}
],
"description": "Common interface for logging libraries",
"homepage": "https://github.com/php-fig/log",
"keywords": [
"log",
"psr",
"psr-3"
],
"time": "2020-03-23T09:12:05+00:00"
},
{
"name": "psr/simple-cache",
"version": "1.0.1",
@ -4375,55 +4798,6 @@
"homepage": "https://symfony.com",
"time": "2018-11-21T14:20:20+00:00"
},
{
"name": "symfony/filesystem",
"version": "v3.0.9",
"source": {
"type": "git",
"url": "https://github.com/symfony/filesystem.git",
"reference": "b2da5009d9bacbd91d83486aa1f44c793a8c380d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/filesystem/zipball/b2da5009d9bacbd91d83486aa1f44c793a8c380d",
"reference": "b2da5009d9bacbd91d83486aa1f44c793a8c380d",
"shasum": ""
},
"require": {
"php": ">=5.5.9"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.0-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Component\\Filesystem\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony Filesystem Component",
"homepage": "https://symfony.com",
"time": "2016-07-20T05:43:46+00:00"
},
{
"name": "symfony/finder",
"version": "v2.8.52",
@ -4699,55 +5073,6 @@
],
"time": "2020-07-14T12:35:20+00:00"
},
{
"name": "symfony/process",
"version": "v2.8.52",
"source": {
"type": "git",
"url": "https://github.com/symfony/process.git",
"reference": "c3591a09c78639822b0b290d44edb69bf9f05dc8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/process/zipball/c3591a09c78639822b0b290d44edb69bf9f05dc8",
"reference": "c3591a09c78639822b0b290d44edb69bf9f05dc8",
"shasum": ""
},
"require": {
"php": ">=5.3.9"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.8-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Component\\Process\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony Process Component",
"homepage": "https://symfony.com",
"time": "2018-11-11T11:18:13+00:00"
},
{
"name": "symfony/stopwatch",
"version": "v2.8.52",

View File

@ -0,0 +1,2 @@
/vendor/
composer.lock

View File

@ -0,0 +1,26 @@
language: php
dist: trusty
sudo: false
php:
- 7.0
- 7.1
- 7.2
- 7.3
matrix:
include:
- php: 7.0
env: COMPOSER_REQUIRE="symfony/process:^2.0"
- php: 7.0
env: COMPOSER_REQUIRE="symfony/process:^3.0"
- php: 7.2
env: COMPOSER_REQUIRE="symfony/process:^5.0"
- php: 7.3
env: COMPOSER_REQUIRE="symfony/process:^5.0"
before_script:
- composer self-update
- if [ -n "$COMPOSER_REQUIRE" ]; then composer require --no-update $COMPOSER_REQUIRE; fi
- composer update $COMPOSER_OPTIONS

View File

@ -0,0 +1,64 @@
CHANGELOG
---------
* 1.6.0 (2015-03-02)
* BC Break: bump minimum PHP versions
* Allow use of evenement v2.0 (thanks @patkar for the P/R)
* 1.5.0 (2013-06-21)
* BC Break : ConfigurationInterface::get does not throw exceptions anymore
in case the key does not exist. Second argument is a default value to return
in case the key does not exist.
* 1.4.1 (2013-05-23)
* Add third parameter to BinaryInterface::command method to pass a listener or
an array of listener that will be registered just the time of the command.
* 1.4.0 (2013-05-11)
* Extract process run management to ProcessRunner.
* Add support for process listeners.
* Provides bundled DebugListener.
* Add BinaryInterface::command method.
* BC break : ProcessRunnerInterface::run now takes an SplObjectStorage containing
listeners as second argument.
* BC break : BinaryInterface no longer implements LoggerAwareInterface
as it is now supported by ProcessRunner.
* 1.3.4 (2013-04-26)
* Add BinaryDriver::run method.
* 1.3.3 (2013-04-26)
* Add BinaryDriver::createProcessMock method.
* 1.3.2 (2013-04-26)
* Add BinaryDriverTestCase for testing BinaryDriver implementations.
* 1.3.1 (2013-04-24)
* Add timeouts handling
* 1.3.0 (2013-04-24)
* Add BinaryInterface and AbstractBinary
* 1.2.1 (2013-04-24)
* Add ConfigurationAwareInterface
* Add ProcessBuilderAwareInterface
* 1.2.0 (2013-04-24)
* Add BinaryDriver\Configuration
* 1.1.0 (2013-04-24)
* Add support for timeouts via `setTimeout` method
* 1.0.0 (2013-04-23)
* First stable version.

21
vendor/alchemy/binary-driver/LICENSE vendored Normal file
View File

@ -0,0 +1,21 @@
BinaryDriver is released with MIT License :
Copyright (c) 2013 Alchemy
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.

190
vendor/alchemy/binary-driver/README.md vendored Normal file
View File

@ -0,0 +1,190 @@
# Binary Driver
Binary-Driver is a set of PHP tools to build binary drivers.
[![Build Status](https://travis-ci.org/alchemy-fr/BinaryDriver.png?branch=master)](https://travis-ci.org/alchemy-fr/BinaryDriver)
## Why ?
You may wonder *Why building a library while I can use `exec` or
[symfony/process](https://github.com/symfony/Process) ?*.
Here is a simple answer :
- If you use `exec`, `passthru`, `system`, `proc_open` or any low level process
handling in PHP, you should have a look to [symfony/process](https://github.com/symfony/Process)
component that will provide an OO portable, testable and secure interface to
deal with this. It seems easy at first approach, but if you look at this
component [unit tests](https://github.com/symfony/Process/tree/master/Tests),
you will see that handling process in a simple interface can easily become a
nightmare.
- If you already use symfony/process, and want to build binary drivers, you
will always have the same common set of methods and objects to configure, log,
debug, and generate processes.
This library is a base to implement any binary driver with this common set of
needs.
## AbstractBinary
`AbstractBinary` provides an abstract class to build a binary driver. It implements
`BinaryInterface`.
Implementation example :
```php
use Alchemy\BinaryDriver\AbstractBinary;
class LsDriver extends AbstractBinary
{
public function getName()
{
return 'ls driver';
}
}
$parser = new LsParser();
$driver = Driver::load('ls');
// will return the output of `ls -a -l`
$parser->parse($driver->command(array('-a', '-l')));
```
### Binary detection troubleshooting
If you are using Nginx with PHP-fpm, executable detection may not work because of an empty `$_ENV['path']`.
To avoid having an empty `PATH` environment variable, add the following line to your `fastcgi_params`
config file (replace `/your/current/path/` with the output of `printenv PATH`) :
```
fastcgi_param PATH /your/current/path
```
## Logging
You can log events with a `Psr\Log\LoggerInterface` by passing it in the load
method as second argument :
```php
$logger = new Monolog\Logger('driver');
$driver = Driver::load('ls', $logger);
```
## Listeners
You can add custom listeners on processes.
Listeners are built on top of [Evenement](https://github.com/igorw/evenement)
and must implement `Alchemy\BinaryDriver\ListenerInterface`.
```php
use Symfony\Component\Process\Process;
class DebugListener extends EventEmitter implements ListenerInterface
{
public function handle($type, $data)
{
foreach (explode(PHP_EOL, $data) as $line) {
$this->emit($type === Process::ERR ? 'error' : 'out', array($line));
}
}
public function forwardedEvents()
{
// forward 'error' events to the BinaryInterface
return array('error');
}
}
$listener = new DebugListener();
$driver = CustomImplementation::load('php');
// adds listener
$driver->listen($listener);
$driver->on('error', function ($line) {
echo '[ERROR] ' . $line . PHP_EOL;
});
// removes listener
$driver->unlisten($listener);
```
### Bundled listeners
The debug listener is a simple listener to catch `stderr` and `stdout` outputs ;
read the implementation for customization.
```php
use Alchemy\BinaryDriver\Listeners\DebugListener;
$driver = CustomImplementation::load('php');
$driver->listen(new DebugListener());
$driver->on('debug', function ($line) {
echo $line;
});
```
## ProcessBuilderFactory
ProcessBuilderFactory ease spawning processes by generating Symfony [Process]
(http://symfony.com/doc/master/components/process.html) objects.
```php
use Alchemy\BinaryDriver\ProcessBuilderFactory;
$factory = new ProcessBuilderFactory('/usr/bin/php');
// return a Symfony\Component\Process\Process
$process = $factory->create('-v');
// echoes '/usr/bin/php' '-v'
echo $process->getCommandLine();
$process = $factory->create(array('-r', 'echo "Hello !";'));
// echoes '/usr/bin/php' '-r' 'echo "Hello !";'
echo $process->getCommandLine();
```
## Configuration
A simple configuration object, providing an `ArrayAccess` and `IteratorAggregate`
interface.
```php
use Alchemy\BinaryDriver\Configuration;
$conf = new Configuration(array('timeout' => 0));
echo $conf->get('timeout');
if ($conf->has('param')) {
$conf->remove('param');
}
$conf->set('timeout', 20);
$conf->all();
```
Same example using the `ArrayAccess` interface :
```php
use Alchemy\BinaryDriver\Configuration;
$conf = new Configuration(array('timeout' => 0));
echo $conf['timeout'];
if (isset($conf['param'])) {
unset($conf['param']);
}
$conf['timeout'] = 20;
```
## License
This project is released under the MIT license.

View File

@ -0,0 +1,43 @@
{
"name": "alchemy/binary-driver",
"type": "library",
"description": "A set of tools to build binary drivers",
"keywords": ["binary", "driver"],
"license": "MIT",
"authors": [
{
"name": "Nicolas Le Goff",
"email": "legoff.n@gmail.com"
},
{
"name": "Romain Neutron",
"email": "imprec@gmail.com",
"homepage": "http://www.lickmychip.com/"
},
{
"name": "Phraseanet Team",
"email": "info@alchemy.fr",
"homepage": "http://www.phraseanet.com/"
},
{
"name": "Jens Hausdorf",
"email": "mail@jens-hausdorf.de",
"homepage": "https://jens-hausdorf.de",
"role": "Maintainer"
}
],
"require": {
"php" : ">=5.5",
"evenement/evenement" : "^3.0|^2.0|^1.0",
"psr/log" : "^1.0",
"symfony/process" : "^2.3|^3.0|^4.0|^5.0"
},
"require-dev": {
"phpunit/phpunit" : "^4.0|^5.0"
},
"autoload": {
"psr-0": {
"Alchemy": "src"
}
}
}

View File

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
backupStaticAttributes="false"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
syntaxCheck="true"
verbose="false"
bootstrap="tests/bootstrap.php"
>
<php>
<ini name="display_errors" value="on"/>
</php>
<testsuites>
<testsuite>
<directory>tests</directory>
</testsuite>
</testsuites>
<filter>
<blacklist>
<directory>vendor</directory>
<directory>tests</directory>
</blacklist>
</filter>
</phpunit>

View File

@ -0,0 +1,218 @@
<?php
/*
* This file is part of Alchemy\BinaryDriver.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\BinaryDriver;
use Alchemy\BinaryDriver\Exception\ExecutableNotFoundException;
use Alchemy\BinaryDriver\Exception\ExecutionFailureException;
use Alchemy\BinaryDriver\Listeners\Listeners;
use Alchemy\BinaryDriver\Listeners\ListenerInterface;
use Evenement\EventEmitter;
use Psr\Log\LoggerInterface;
use Psr\Log\NullLogger;
use Symfony\Component\Process\ExecutableFinder;
use Symfony\Component\Process\Process;
abstract class AbstractBinary extends EventEmitter implements BinaryInterface
{
/** @var ConfigurationInterface */
protected $configuration;
/** @var ProcessBuilderFactoryInterface */
protected $factory;
/** @var ProcessRunner */
private $processRunner;
/** @var Listeners */
private $listenersManager;
public function __construct(ProcessBuilderFactoryInterface $factory, LoggerInterface $logger, ConfigurationInterface $configuration)
{
$this->factory = $factory;
$this->configuration = $configuration;
$this->processRunner = new ProcessRunner($logger, $this->getName());
$this->listenersManager = new Listeners();
$this->applyProcessConfiguration();
}
/**
* {@inheritdoc}
*/
public function listen(ListenerInterface $listener)
{
$this->listenersManager->register($listener, $this);
return $this;
}
/**
* {@inheritdoc}
*/
public function unlisten(ListenerInterface $listener)
{
$this->listenersManager->unregister($listener, $this);
return $this;
}
/**
* {@inheritdoc}
*/
public function getConfiguration()
{
return $this->configuration;
}
/**
* {@inheritdoc}
*
* @return BinaryInterface
*/
public function setConfiguration(ConfigurationInterface $configuration)
{
$this->configuration = $configuration;
$this->applyProcessConfiguration();
return $this;
}
/**
* {@inheritdoc}
*/
public function getProcessBuilderFactory()
{
return $this->factory;
}
/**
* {@inheritdoc}
*
* @return BinaryInterface
*/
public function setProcessBuilderFactory(ProcessBuilderFactoryInterface $factory)
{
$this->factory = $factory;
$this->applyProcessConfiguration();
return $this;
}
/**
* {@inheritdoc}
*/
public function getProcessRunner()
{
return $this->processRunner;
}
/**
* {@inheritdoc}
*/
public function setProcessRunner(ProcessRunnerInterface $runner)
{
$this->processRunner = $runner;
return $this;
}
/**
* {@inheritdoc}
*/
public function command($command, $bypassErrors = false, $listeners = null)
{
if (!is_array($command)) {
$command = array($command);
}
return $this->run($this->factory->create($command), $bypassErrors, $listeners);
}
/**
* {@inheritdoc}
*/
public static function load($binaries, LoggerInterface $logger = null, $configuration = array())
{
$finder = new ExecutableFinder();
$binary = null;
$binaries = is_array($binaries) ? $binaries : array($binaries);
foreach ($binaries as $candidate) {
if (file_exists($candidate) && is_executable($candidate)) {
$binary = $candidate;
break;
}
if (null !== $binary = $finder->find($candidate)) {
break;
}
}
if (null === $binary) {
throw new ExecutableNotFoundException(sprintf(
'Executable not found, proposed : %s', implode(', ', $binaries)
));
}
if (null === $logger) {
$logger = new NullLogger();
}
$configuration = $configuration instanceof ConfigurationInterface ? $configuration : new Configuration($configuration);
return new static(new ProcessBuilderFactory($binary), $logger, $configuration);
}
/**
* Returns the name of the driver
*
* @return string
*/
abstract public function getName();
/**
* Executes a process, logs events
*
* @param Process $process
* @param Boolean $bypassErrors Set to true to disable throwing ExecutionFailureExceptions
* @param ListenerInterface|array $listeners A listener or an array of listener to register for this unique run
*
* @return string The Process output
*
* @throws ExecutionFailureException in case of process failure.
*/
protected function run(Process $process, $bypassErrors = false, $listeners = null)
{
if (null !== $listeners) {
if (!is_array($listeners)) {
$listeners = array($listeners);
}
$listenersManager = clone $this->listenersManager;
foreach ($listeners as $listener) {
$listenersManager->register($listener, $this);
}
} else {
$listenersManager = $this->listenersManager;
}
return $this->processRunner->run($process, $listenersManager->storage, $bypassErrors);
}
private function applyProcessConfiguration()
{
if ($this->configuration->has('timeout')) {
$this->factory->setTimeout($this->configuration->get('timeout'));
}
return $this;
}
}

View File

@ -0,0 +1,76 @@
<?php
namespace Alchemy\BinaryDriver;
use Psr\Log\LoggerInterface;
use Symfony\Component\Process\Process;
/**
* Convenient PHPUnit methods for testing BinaryDriverInterface implementations.
*/
class BinaryDriverTestCase extends \PHPUnit_Framework_TestCase
{
/**
* @return ProcessBuilderFactoryInterface
*/
public function createProcessBuilderFactoryMock()
{
return $this->getMock('Alchemy\BinaryDriver\ProcessBuilderFactoryInterface');
}
/**
* @param integer $runs The number of runs expected
* @param Boolean $success True if the process expects to be successfull
* @param string $commandLine The commandline executed
* @param string $output The process output
* @param string $error The process error output
*
* @return Process
*/
public function createProcessMock($runs = 1, $success = true, $commandLine = null, $output = null, $error = null, $callback = false)
{
$process = $this->getMockBuilder('Symfony\Component\Process\Process')
->disableOriginalConstructor()
->getMock();
$builder = $process->expects($this->exactly($runs))
->method('run');
if (true === $callback) {
$builder->with($this->isInstanceOf('Closure'));
}
$process->expects($this->any())
->method('isSuccessful')
->will($this->returnValue($success));
foreach (array(
'getOutput' => $output,
'getErrorOutput' => $error,
'getCommandLine' => $commandLine,
) as $command => $value) {
$process
->expects($this->any())
->method($command)
->will($this->returnValue($value));
}
return $process;
}
/**
* @return LoggerInterface
*/
public function createLoggerMock()
{
return $this->getMock('Psr\Log\LoggerInterface');
}
/**
* @return ConfigurationInterface
*/
public function createConfigurationMock()
{
return $this->getMock('Alchemy\BinaryDriver\ConfigurationInterface');
}
}

View File

@ -0,0 +1,67 @@
<?php
/*
* This file is part of Alchemy\BinaryDriver.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\BinaryDriver;
use Alchemy\BinaryDriver\Exception\ExecutableNotFoundException;
use Alchemy\BinaryDriver\Exception\ExecutionFailureException;
use Alchemy\BinaryDriver\Listeners\ListenerInterface;
use Psr\Log\LoggerInterface;
use Evenement\EventEmitterInterface;
interface BinaryInterface extends ConfigurationAwareInterface, ProcessBuilderFactoryAwareInterface, ProcessRunnerAwareInterface, EventEmitterInterface
{
/**
* Adds a listener to the binary driver
*
* @param ListenerInterface $listener
*
* @return BinaryInterface
*/
public function listen(ListenerInterface $listener);
/**
* Removes a listener from the binary driver
*
* @param ListenerInterface $listener
*
* @return BinaryInterface
*/
public function unlisten(ListenerInterface $listener);
/**
* Runs a command against the driver.
*
* Calling this method on a `ls` driver with the command `-a` would run `ls -a`.
*
* @param array|string $command A command or an array of command
* @param Boolean $bypassErrors If set to true, an erronous process will not throw an exception
* @param ListenerInterface|array $listeners A listener or an array of listeners to register for this unique run
*
* @return string The command output
*
* @throws ExecutionFailureException in case of process failure.
*/
public function command($command, $bypassErrors = false, $listeners = null);
/**
* Loads a binary
*
* @param string|array $binaries A binary name or an array of binary names
* @param null|LoggerInterface $logger A Logger
* @param array|ConfigurationInterface $configuration The configuration
*
* @throws ExecutableNotFoundException In case none of the binaries were found
*
* @return BinaryInterface
*/
public static function load($binaries, LoggerInterface $logger = null, $configuration = array());
}

View File

@ -0,0 +1,107 @@
<?php
/*
* This file is part of Alchemy\BinaryDriver.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\BinaryDriver;
class Configuration implements ConfigurationInterface
{
private $data;
public function __construct(array $data = array())
{
$this->data = $data;
}
/**
* {@inheritdoc}
*/
public function getIterator()
{
return new \ArrayIterator($this->data);
}
/**
* {@inheritdoc}
*/
public function get($key, $default = null)
{
return isset($this->data[$key]) ? $this->data[$key] : $default;
}
/**
* {@inheritdoc}
*/
public function set($key, $value)
{
$this->data[$key] = $value;
return $this;
}
/**
* {@inheritdoc}
*/
public function has($key)
{
return array_key_exists($key, $this->data);
}
/**
* {@inheritdoc}
*/
public function remove($key)
{
$value = $this->get($key);
unset($this->data[$key]);
return $value;
}
/**
* {@inheritdoc}
*/
public function all()
{
return $this->data;
}
/**
* {@inheritdoc}
*/
public function offsetExists($offset)
{
return $this->has($offset);
}
/**
* {@inheritdoc}
*/
public function offsetGet($offset)
{
return $this->get($offset);
}
/**
* {@inheritdoc}
*/
public function offsetSet($offset, $value)
{
$this->set($offset, $value);
}
/**
* {@inheritdoc}
*/
public function offsetUnset($offset)
{
$this->remove($offset);
}
}

View File

@ -0,0 +1,29 @@
<?php
/*
* This file is part of Alchemy\BinaryDriver.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\BinaryDriver;
interface ConfigurationAwareInterface
{
/**
* Returns the configuration
*
* @return ConfigurationInterface
*/
public function getConfiguration();
/**
* Set the configuration
*
* @param ConfigurationInterface $configuration
*/
public function setConfiguration(ConfigurationInterface $configuration);
}

View File

@ -0,0 +1,58 @@
<?php
/*
* This file is part of Alchemy\BinaryDriver.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\BinaryDriver;
interface ConfigurationInterface extends \ArrayAccess, \IteratorAggregate
{
/**
* Returns the value given a key from configuration
*
* @param string $key
* @param mixed $default The default value in case the key does not exist
*
* @return mixed
*/
public function get($key, $default = null);
/**
* Set a value to configuration
*
* @param string $key The key
* @param mixed $value The value corresponding to the key
*/
public function set($key, $value);
/**
* Tells if Configuration contains `$key`
*
* @param string $key
*
* @return Boolean
*/
public function has($key);
/**
* Removes a value given a key
*
* @param string $key
*
* @return mixed The previous value
*/
public function remove($key);
/**
* Returns all values set in the configuration
*
* @return array
*/
public function all();
}

View File

@ -0,0 +1,16 @@
<?php
/*
* This file is part of Alchemy\BinaryDriver.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\BinaryDriver\Exception;
interface ExceptionInterface
{
}

View File

@ -0,0 +1,16 @@
<?php
/*
* This file is part of Alchemy\BinaryDriver.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\BinaryDriver\Exception;
class ExecutableNotFoundException extends \RuntimeException implements ExceptionInterface
{
}

View File

@ -0,0 +1,37 @@
<?php
/*
* This file is part of Alchemy\BinaryDriver.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\BinaryDriver\Exception;
class ExecutionFailureException extends \RuntimeException implements ExceptionInterface
{
/** @var string */
protected $command;
/** @var string */
protected $errorOutput;
public function __construct($binaryName, $command, $errorOutput = null, $code = 0, $previous = null)
{
$message = sprintf("%s failed to execute command %s:\n\nError Output:\n\n %s", $binaryName, $command, $errorOutput);
parent::__construct($message, $code, $previous);
$this->command = $command;
$this->errorOutput = $errorOutput;
}
public function getCommand(){
return $this->command;
}
public function getErrorOutput(){
return $this->errorOutput;
}
}

View File

@ -0,0 +1,16 @@
<?php
/*
* This file is part of Alchemy\BinaryDriver.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\BinaryDriver\Exception;
class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface
{
}

View File

@ -0,0 +1,58 @@
<?php
/*
* This file is part of Alchemy\BinaryDriver.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\BinaryDriver\Listeners;
use Evenement\EventEmitter;
use Symfony\Component\Process\Process;
class DebugListener extends EventEmitter implements ListenerInterface
{
private $prefixOut;
private $prefixErr;
private $eventOut;
private $eventErr;
public function __construct($prefixOut = '[OUT] ', $prefixErr = '[ERROR] ', $eventOut = 'debug', $eventErr = 'debug')
{
$this->prefixOut = $prefixOut;
$this->prefixErr = $prefixErr;
$this->eventOut = $eventOut;
$this->eventErr = $eventErr;
}
/**
* {@inheritdoc}
*/
public function handle($type, $data)
{
if (Process::ERR === $type) {
$this->emitLines($this->eventErr, $this->prefixErr, $data);
} elseif (Process::OUT === $type) {
$this->emitLines($this->eventOut, $this->prefixOut, $data);
}
}
/**
* {@inheritdoc}
*/
public function forwardedEvents()
{
return array_unique(array($this->eventErr, $this->eventOut));
}
private function emitLines($event, $prefix, $lines)
{
foreach (explode("\n", $lines) as $line) {
$this->emit($event, array($prefix . $line));
}
}
}

View File

@ -0,0 +1,32 @@
<?php
/*
* This file is part of Alchemy\BinaryDriver.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\BinaryDriver\Listeners;
use Evenement\EventEmitterInterface;
interface ListenerInterface extends EventEmitterInterface
{
/**
* Handle the output of a ProcessRunner
*
* @param string $type The data type, one of Process::ERR, Process::OUT constants
* @param string $data The output
*/
public function handle($type, $data);
/**
* An array of events that should be forwarded to BinaryInterface
*
* @return array
*/
public function forwardedEvents();
}

View File

@ -0,0 +1,88 @@
<?php
namespace Alchemy\BinaryDriver\Listeners;
use SplObjectStorage;
use Evenement\EventEmitter;
class Listeners extends EventEmitter
{
/** @var SplObjectStorage */
public $storage;
public function __construct()
{
$this->storage = new SplObjectStorage();
}
public function __clone()
{
$storage = $this->storage;
$this->storage = new SplObjectStorage();
$this->storage->addAll($storage);
}
/**
* Registers a listener, pass the listener events to the target.
*
* @param ListenerInterface $listener
* @param null|EventEmitter $target
*
* @return ListenersInterface
*/
public function register(ListenerInterface $listener, EventEmitter $target = null)
{
$EElisteners = array();
if (null !== $target) {
$EElisteners = $this->forwardEvents($listener, $target, $listener->forwardedEvents());
}
$this->storage->attach($listener, $EElisteners);
return $this;
}
/**
* Unregisters a listener, removes the listener events from the target.
*
* @param ListenerInterface $listener
*
* @return ListenersInterface
*
* @throws InvalidArgumentException In case the listener is not registered
*/
public function unregister(ListenerInterface $listener)
{
if (!isset($this->storage[$listener])) {
throw new InvalidArgumentException('Listener is not registered.');
}
foreach ($this->storage[$listener] as $event => $EElistener) {
$listener->removeListener($event, $EElistener);
}
$this->storage->detach($listener);
return $this;
}
private function forwardEvents($source, $target, array $events)
{
$EElisteners = array();
foreach ($events as $event) {
$listener = $this->createListener($event, $target);
$source->on($event, $EElisteners[$event] = $listener);
}
return $EElisteners;
}
private function createListener($event, $target)
{
return function () use ($event, $target) {
$target->emit($event, func_get_args());
};
}
}

View File

@ -0,0 +1,186 @@
<?php
/*
* This file is part of Alchemy\BinaryDriver.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\BinaryDriver;
use Alchemy\BinaryDriver\Exception\InvalidArgumentException;
use Symfony\Component\Process\Process;
use Symfony\Component\Process\ProcessBuilder;
class ProcessBuilderFactory implements ProcessBuilderFactoryInterface
{
/**
* The binary path
*
* @var String
*/
protected $binary;
/**
* The timeout for the generated processes
*
* @var integer|float
*/
private $timeout;
/**
* An internal ProcessBuilder.
*
* Note that this one is used only if Symfony ProcessBuilder has method
* setPrefix (2.3)
*
* @var ProcessBuilder
*/
private $builder;
/**
* Tells whether Symfony LTS ProcessBuilder should be emulated or not.
*
* This symfony version provided a brand new ::setPrefix method.
*
* @var Boolean
*/
public static $emulateSfLTS;
/**
* Constructor
*
* @param String $binary The path to the binary
*
* @throws InvalidArgumentException In case binary path is invalid
*/
public function __construct($binary)
{
$this->detectEmulation();
if (!self::$emulateSfLTS) {
$this->builder = new ProcessBuilder();
}
$this->useBinary($binary);
}
/**
* Covenient method for unit testing
*
* @return type
*/
public function getBuilder()
{
return $this->builder;
}
/**
* Covenient method for unit testing
*
* @param ProcessBuilder $builder
* @return ProcessBuilderFactory
*/
public function setBuilder(ProcessBuilder $builder)
{
$this->builder = $builder;
return $this;
}
/**
* @inheritdoc
*/
public function getBinary()
{
return $this->binary;
}
/**
* @inheritdoc
*/
public function useBinary($binary)
{
if (!is_executable($binary)) {
throw new InvalidArgumentException(sprintf('`%s` is not an executable binary', $binary));
}
$this->binary = $binary;
if (!static::$emulateSfLTS) {
$this->builder->setPrefix($binary);
}
return $this;
}
/**
* @inheritdoc
*/
public function setTimeout($timeout)
{
$this->timeout = $timeout;
if (!static::$emulateSfLTS) {
$this->builder->setTimeout($this->timeout);
}
return $this;
}
/**
* @inheritdoc
*/
public function getTimeout()
{
return $this->timeout;
}
/**
* @inheritdoc
*/
public function create($arguments = array())
{
if (null === $this->binary) {
throw new InvalidArgumentException('No binary set');
}
if (!is_array($arguments)) {
$arguments = array($arguments);
}
if (static::$emulateSfLTS) {
array_unshift($arguments, $this->binary);
if (method_exists('Symfony\Component\Process\ProcessUtils', 'escapeArgument')) {
$script = implode(' ', array_map(array('Symfony\Component\Process\ProcessUtils', 'escapeArgument'), $arguments));
} else {
$script = $arguments;
}
$env = array_replace($_ENV, $_SERVER);
$env = array_filter($env, function ($value) {
return !is_array($value);
});
return new Process($script, null, $env, null, $this->timeout);
} else {
return $this->builder
->setArguments($arguments)
->getProcess();
}
}
private function detectEmulation()
{
if (null !== static::$emulateSfLTS) {
return $this;
}
static::$emulateSfLTS = !method_exists('Symfony\Component\Process\ProcessBuilder', 'setPrefix');
return $this;
}
}

View File

@ -0,0 +1,29 @@
<?php
/*
* This file is part of Alchemy\BinaryDriver.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\BinaryDriver;
interface ProcessBuilderFactoryAwareInterface
{
/**
* Returns the current process builder factory
*
* @return ProcessBuilderFactoryInterface
*/
public function getProcessBuilderFactory();
/**
* Set a process builder factory
*
* @param ProcessBuilderFactoryInterface $factory
*/
public function setProcessBuilderFactory(ProcessBuilderFactoryInterface $factory);
}

View File

@ -0,0 +1,65 @@
<?php
/*
* This file is part of Alchemy\BinaryDriver.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\BinaryDriver;
use Alchemy\BinaryDriver\Exception\InvalidArgumentException;
use Symfony\Component\Process\Process;
interface ProcessBuilderFactoryInterface
{
/**
* Returns a new instance of Symfony Process
*
* @param string|array $arguments An argument or an array of arguments
*
* @return Process
*
* @throws InvalidArgumentException
*/
public function create($arguments = array());
/**
* Returns the path to the binary that is used
*
* @return String
*/
public function getBinary();
/**
* Sets the path to the binary
*
* @param String $binary A path to a binary
*
* @return ProcessBuilderFactoryInterface
*
* @throws InvalidArgumentException In case binary is not executable
*/
public function useBinary($binary);
/**
* Set the default timeout to apply on created processes.
*
* @param integer|float $timeout
*
* @return ProcessBuilderFactoryInterface
*
* @throws InvalidArgumentException In case the timeout is not valid
*/
public function setTimeout($timeout);
/**
* Returns the current timeout applied to the created processes.
*
* @return integer|float
*/
public function getTimeout();
}

View File

@ -0,0 +1,102 @@
<?php
/*
* This file is part of Alchemy\BinaryDriver.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\BinaryDriver;
use Alchemy\BinaryDriver\Exception\ExecutionFailureException;
use Psr\Log\LoggerInterface;
use SplObjectStorage;
use Symfony\Component\Process\Exception\RuntimeException;
use Symfony\Component\Process\Process;
class ProcessRunner implements ProcessRunnerInterface
{
/** @var LoggerInterface */
private $logger;
/** @var string */
private $name;
public function __construct(LoggerInterface $logger, $name)
{
$this->logger = $logger;
$this->name = $name;
}
/**
* {@inheritdoc}
*
* @return ProcessRunner
*/
public function setLogger(LoggerInterface $logger)
{
$this->logger = $logger;
return $this;
}
/**
* @return LoggerInterface
*/
public function getLogger()
{
return $this->logger;
}
/**
* {@inheritdoc}
*/
public function run(Process $process, SplObjectStorage $listeners, $bypassErrors)
{
$this->logger->info(sprintf(
'%s running command %s', $this->name, $process->getCommandLine()
));
try {
$process->run($this->buildCallback($listeners));
} catch (RuntimeException $e) {
if (!$bypassErrors) {
$this->doExecutionFailure($process->getCommandLine(), $process->getErrorOutput(), $e);
}
}
if (!$bypassErrors && !$process->isSuccessful()) {
$this->doExecutionFailure($process->getCommandLine(), $process->getErrorOutput());
} elseif (!$process->isSuccessful()) {
$this->logger->error($this->createErrorMessage($process->getCommandLine(), $process->getErrorOutput()));
return;
} else {
$this->logger->info(sprintf('%s executed command successfully', $this->name));
return $process->getOutput();
}
}
private function buildCallback(SplObjectStorage $listeners)
{
return function ($type, $data) use ($listeners) {
foreach ($listeners as $listener) {
$listener->handle($type, $data);
}
};
}
private function doExecutionFailure($command, $errorOutput, \Exception $e = null)
{
$this->logger->error($this->createErrorMessage($command, $errorOutput));
throw new ExecutionFailureException($this->name, $command, $errorOutput,
$e ? $e->getCode() : 0, $e ?: null);
}
private function createErrorMessage($command, $errorOutput){
return sprintf('%s failed to execute command %s: %s', $this->name, $command, $errorOutput);
}
}

View File

@ -0,0 +1,29 @@
<?php
/*
* This file is part of Alchemy\BinaryDriver.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\BinaryDriver;
interface ProcessRunnerAwareInterface
{
/**
* Returns the current process runner
*
* @return ProcessRunnerInterface
*/
public function getProcessRunner();
/**
* Sets a process runner
*
* @param ProcessRunnerInterface $runner
*/
public function setProcessRunner(ProcessRunnerInterface $runner);
}

View File

@ -0,0 +1,33 @@
<?php
/*
* This file is part of Alchemy\BinaryDriver.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\BinaryDriver;
use Alchemy\BinaryDriver\Exception\ExecutionFailureException;
use Psr\Log\LoggerAwareInterface;
use SplObjectStorage;
use Symfony\Component\Process\Process;
interface ProcessRunnerInterface extends LoggerAwareInterface
{
/**
* Executes a process, logs events
*
* @param Process $process
* @param SplObjectStorage $listeners Some listeners
* @param Boolean $bypassErrors Set to true to disable throwing ExecutionFailureExceptions
*
* @return string The Process output
*
* @throws ExecutionFailureException in case of process failure.
*/
public function run(Process $process, SplObjectStorage $listeners, $bypassErrors);
}

View File

@ -0,0 +1,300 @@
<?php
namespace Alchemy\Tests\BinaryDriver;
use Alchemy\BinaryDriver\AbstractBinary;
use Alchemy\BinaryDriver\BinaryDriverTestCase;
use Alchemy\BinaryDriver\Configuration;
use Alchemy\BinaryDriver\Exception\ExecutableNotFoundException;
use Alchemy\BinaryDriver\Listeners\ListenerInterface;
use Symfony\Component\Process\ExecutableFinder;
class AbstractBinaryTest extends BinaryDriverTestCase
{
protected function getPhpBinary()
{
$finder = new ExecutableFinder();
$php = $finder->find('php');
if (null === $php) {
$this->markTestSkipped('Unable to find a php binary');
}
return $php;
}
public function testSimpleLoadWithBinaryPath()
{
$php = $this->getPhpBinary();
$imp = Implementation::load($php);
$this->assertInstanceOf('Alchemy\Tests\BinaryDriver\Implementation', $imp);
$this->assertEquals($php, $imp->getProcessBuilderFactory()->getBinary());
}
public function testMultipleLoadWithBinaryPath()
{
$php = $this->getPhpBinary();
$imp = Implementation::load(array('/zz/path/to/unexisting/command', $php));
$this->assertInstanceOf('Alchemy\Tests\BinaryDriver\Implementation', $imp);
$this->assertEquals($php, $imp->getProcessBuilderFactory()->getBinary());
}
public function testSimpleLoadWithBinaryName()
{
$php = $this->getPhpBinary();
$imp = Implementation::load('php');
$this->assertInstanceOf('Alchemy\Tests\BinaryDriver\Implementation', $imp);
$this->assertEquals($php, $imp->getProcessBuilderFactory()->getBinary());
}
public function testMultipleLoadWithBinaryName()
{
$php = $this->getPhpBinary();
$imp = Implementation::load(array('bachibouzouk', 'php'));
$this->assertInstanceOf('Alchemy\Tests\BinaryDriver\Implementation', $imp);
$this->assertEquals($php, $imp->getProcessBuilderFactory()->getBinary());
}
public function testLoadWithMultiplePathExpectingAFailure()
{
$this->setExpectedException(ExecutableNotFoundException::class);
Implementation::load(array('bachibouzouk', 'moribon'));
}
public function testLoadWithUniquePathExpectingAFailure()
{
$this->setExpectedException(ExecutableNotFoundException::class);
Implementation::load('bachibouzouk');
}
public function testLoadWithCustomLogger()
{
$logger = $this->getMock('Psr\Log\LoggerInterface');
$imp = Implementation::load('php', $logger);
$this->assertEquals($logger, $imp->getProcessRunner()->getLogger());
}
public function testLoadWithCustomConfigurationAsArray()
{
$conf = array('timeout' => 200);
$imp = Implementation::load('php', null, $conf);
$this->assertEquals($conf, $imp->getConfiguration()->all());
}
public function testLoadWithCustomConfigurationAsObject()
{
$conf = $this->getMock('Alchemy\BinaryDriver\ConfigurationInterface');
$imp = Implementation::load('php', null, $conf);
$this->assertEquals($conf, $imp->getConfiguration());
}
public function testProcessBuilderFactoryGetterAndSetters()
{
$imp = Implementation::load('php');
$factory = $this->getMock('Alchemy\BinaryDriver\ProcessBuilderFactoryInterface');
$imp->setProcessBuilderFactory($factory);
$this->assertEquals($factory, $imp->getProcessBuilderFactory());
}
public function testConfigurationGetterAndSetters()
{
$imp = Implementation::load('php');
$conf = $this->getMock('Alchemy\BinaryDriver\ConfigurationInterface');
$imp->setConfiguration($conf);
$this->assertEquals($conf, $imp->getConfiguration());
}
public function testTimeoutIsSetOnConstruction()
{
$imp = Implementation::load('php', null, array('timeout' => 42));
$this->assertEquals(42, $imp->getProcessBuilderFactory()->getTimeout());
}
public function testTimeoutIsSetOnConfigurationSetting()
{
$imp = Implementation::load('php', null);
$imp->setConfiguration(new Configuration(array('timeout' => 42)));
$this->assertEquals(42, $imp->getProcessBuilderFactory()->getTimeout());
}
public function testTimeoutIsSetOnProcessBuilderSetting()
{
$imp = Implementation::load('php', null, array('timeout' => 42));
$factory = $this->getMock('Alchemy\BinaryDriver\ProcessBuilderFactoryInterface');
$factory->expects($this->once())
->method('setTimeout')
->with(42);
$imp->setProcessBuilderFactory($factory);
}
public function testListenRegistersAListener()
{
$imp = Implementation::load('php');
$listeners = $this->getMockBuilder('Alchemy\BinaryDriver\Listeners\Listeners')
->disableOriginalConstructor()
->getMock();
$listener = $this->getMock('Alchemy\BinaryDriver\Listeners\ListenerInterface');
$listeners->expects($this->once())
->method('register')
->with($this->equalTo($listener), $this->equalTo($imp));
$reflexion = new \ReflectionClass('Alchemy\BinaryDriver\AbstractBinary');
$prop = $reflexion->getProperty('listenersManager');
$prop->setAccessible(true);
$prop->setValue($imp, $listeners);
$imp->listen($listener);
}
/**
* @dataProvider provideCommandParameters
*/
public function testCommandRunsAProcess($parameters, $bypassErrors, $expectedParameters, $output)
{
$imp = Implementation::load('php');
$factory = $this->getMock('Alchemy\BinaryDriver\ProcessBuilderFactoryInterface');
$processRunner = $this->getMock('Alchemy\BinaryDriver\ProcessRunnerInterface');
$process = $this->getMockBuilder('Symfony\Component\Process\Process')
->disableOriginalConstructor()
->getMock();
$processRunner->expects($this->once())
->method('run')
->with($this->equalTo($process), $this->isInstanceOf('SplObjectStorage'), $this->equalTo($bypassErrors))
->will($this->returnValue($output));
$factory->expects($this->once())
->method('create')
->with($expectedParameters)
->will($this->returnValue($process));
$imp->setProcessBuilderFactory($factory);
$imp->setProcessRunner($processRunner);
$this->assertEquals($output, $imp->command($parameters, $bypassErrors));
}
/**
* @dataProvider provideCommandWithListenersParameters
*/
public function testCommandWithTemporaryListeners($parameters, $bypassErrors, $expectedParameters, $output, $count, $listeners)
{
$imp = Implementation::load('php');
$factory = $this->getMock('Alchemy\BinaryDriver\ProcessBuilderFactoryInterface');
$processRunner = $this->getMock('Alchemy\BinaryDriver\ProcessRunnerInterface');
$process = $this->getMockBuilder('Symfony\Component\Process\Process')
->disableOriginalConstructor()
->getMock();
$firstStorage = $secondStorage = null;
$processRunner->expects($this->exactly(2))
->method('run')
->with($this->equalTo($process), $this->isInstanceOf('SplObjectStorage'), $this->equalTo($bypassErrors))
->will($this->returnCallback(function ($process, $storage, $errors) use ($output, &$firstStorage, &$secondStorage) {
if (null === $firstStorage) {
$firstStorage = $storage;
} else {
$secondStorage = $storage;
}
return $output;
}));
$factory->expects($this->exactly(2))
->method('create')
->with($expectedParameters)
->will($this->returnValue($process));
$imp->setProcessBuilderFactory($factory);
$imp->setProcessRunner($processRunner);
$this->assertEquals($output, $imp->command($parameters, $bypassErrors, $listeners));
$this->assertCount($count, $firstStorage);
$this->assertEquals($output, $imp->command($parameters, $bypassErrors));
$this->assertCount(0, $secondStorage);
}
public function provideCommandWithListenersParameters()
{
return array(
array('-a', false, array('-a'), 'loubda', 2, array($this->getMockListener(), $this->getMockListener())),
array('-a', false, array('-a'), 'loubda', 1, array($this->getMockListener())),
array('-a', false, array('-a'), 'loubda', 1, $this->getMockListener()),
array('-a', false, array('-a'), 'loubda', 0, array()),
);
}
public function provideCommandParameters()
{
return array(
array('-a', false, array('-a'), 'loubda'),
array('-a', true, array('-a'), 'loubda'),
array('-a -b', false, array('-a -b'), 'loubda'),
array(array('-a'), false, array('-a'), 'loubda'),
array(array('-a'), true, array('-a'), 'loubda'),
array(array('-a', '-b'), false, array('-a', '-b'), 'loubda'),
);
}
public function testUnlistenUnregistersAListener()
{
$imp = Implementation::load('php');
$listeners = $this->getMockBuilder('Alchemy\BinaryDriver\Listeners\Listeners')
->disableOriginalConstructor()
->getMock();
$listener = $this->getMock('Alchemy\BinaryDriver\Listeners\ListenerInterface');
$listeners->expects($this->once())
->method('unregister')
->with($this->equalTo($listener), $this->equalTo($imp));
$reflexion = new \ReflectionClass('Alchemy\BinaryDriver\AbstractBinary');
$prop = $reflexion->getProperty('listenersManager');
$prop->setAccessible(true);
$prop->setValue($imp, $listeners);
$imp->unlisten($listener);
}
/**
* @return \PHPUnit_Framework_MockObject_MockObject
*/
private function getMockListener()
{
$listener = $this->getMock(ListenerInterface::class);
$listener->expects($this->any())
->method('forwardedEvents')
->willReturn(array());
return $listener;
}
}
class Implementation extends AbstractBinary
{
public function getName()
{
return 'Implementation';
}
}

View File

@ -0,0 +1,97 @@
<?php
namespace Alchemy\Tests\BinaryDriver;
use Symfony\Component\Process\ExecutableFinder;
use Alchemy\BinaryDriver\ProcessBuilderFactory;
abstract class AbstractProcessBuilderFactoryTest extends \PHPUnit_Framework_TestCase
{
public static $phpBinary;
private $original;
/**
* @return ProcessBuilderFactory
*/
abstract protected function getProcessBuilderFactory($binary);
public function setUp()
{
ProcessBuilderFactory::$emulateSfLTS = null;
if (null === static::$phpBinary) {
$this->markTestSkipped('Unable to detect php binary, skipping');
}
}
public static function setUpBeforeClass()
{
$finder = new ExecutableFinder();
static::$phpBinary = $finder->find('php');
}
public function testThatBinaryIsSetOnConstruction()
{
$factory = $this->getProcessBuilderFactory(static::$phpBinary);
$this->assertEquals(static::$phpBinary, $factory->getBinary());
}
public function testGetSetBinary()
{
$finder = new ExecutableFinder();
$phpUnit = $finder->find('phpunit');
if (null === $phpUnit) {
$this->markTestSkipped('Unable to detect phpunit binary, skipping');
}
$factory = $this->getProcessBuilderFactory(static::$phpBinary);
$factory->useBinary($phpUnit);
$this->assertEquals($phpUnit, $factory->getBinary());
}
/**
* @expectedException Alchemy\BinaryDriver\Exception\InvalidArgumentException
*/
public function testUseNonExistantBinary()
{
$factory = $this->getProcessBuilderFactory(static::$phpBinary);
$factory->useBinary('itissureitdoesnotexist');
}
public function testCreateShouldReturnAProcess()
{
$factory = $this->getProcessBuilderFactory(static::$phpBinary);
$process = $factory->create();
$this->assertInstanceOf('Symfony\Component\Process\Process', $process);
$this->assertEquals("'".static::$phpBinary."'", $process->getCommandLine());
}
public function testCreateWithStringArgument()
{
$factory = $this->getProcessBuilderFactory(static::$phpBinary);
$process = $factory->create('-v');
$this->assertInstanceOf('Symfony\Component\Process\Process', $process);
$this->assertEquals("'".static::$phpBinary."' '-v'", $process->getCommandLine());
}
public function testCreateWithArrayArgument()
{
$factory = $this->getProcessBuilderFactory(static::$phpBinary);
$process = $factory->create(array('-r', 'echo "Hello !";'));
$this->assertInstanceOf('Symfony\Component\Process\Process', $process);
$this->assertEquals("'".static::$phpBinary."' '-r' 'echo \"Hello !\";'", $process->getCommandLine());
}
public function testCreateWithTimeout()
{
$factory = $this->getProcessBuilderFactory(static::$phpBinary);
$factory->setTimeout(200);
$process = $factory->create(array('-i'));
$this->assertInstanceOf('Symfony\Component\Process\Process', $process);
$this->assertEquals(200, $process->getTimeout());
}
}

View File

@ -0,0 +1,78 @@
<?php
namespace Alchemy\Tests\BinaryDriver;
use Alchemy\BinaryDriver\Configuration;
class ConfigurationTest extends \PHPUnit_Framework_TestCase
{
public function testArrayAccessImplementation()
{
$configuration = new Configuration(array('key' => 'value'));
$this->assertTrue(isset($configuration['key']));
$this->assertEquals('value', $configuration['key']);
$this->assertFalse(isset($configuration['key2']));
unset($configuration['key']);
$this->assertFalse(isset($configuration['key']));
$configuration['key2'] = 'value2';
$this->assertTrue(isset($configuration['key2']));
$this->assertEquals('value2', $configuration['key2']);
}
public function testGetOnNonExistentKeyShouldReturnDefaultValue()
{
$conf = new Configuration();
$this->assertEquals('booba', $conf->get('hooba', 'booba'));
$this->assertEquals(null, $conf->get('hooba'));
}
public function testSetHasGetRemove()
{
$configuration = new Configuration(array('key' => 'value'));
$this->assertTrue($configuration->has('key'));
$this->assertEquals('value', $configuration->get('key'));
$this->assertFalse($configuration->has('key2'));
$configuration->remove('key');
$this->assertFalse($configuration->has('key'));
$configuration->set('key2', 'value2');
$this->assertTrue($configuration->has('key2'));
$this->assertEquals('value2', $configuration->get('key2'));
}
public function testIterator()
{
$data = array(
'key1' => 'value1',
'key2' => 'value2',
'key3' => 'value3',
);
$captured = array();
$conf = new Configuration($data);
foreach ($conf as $key => $value) {
$captured[$key] = $value;
}
$this->assertEquals($data, $captured);
}
public function testAll()
{
$data = array(
'key1' => 'value1',
'key2' => 'value2',
'key3' => 'value3',
);
$conf = new Configuration($data);
$this->assertEquals($data, $conf->all());
}
}

View File

@ -0,0 +1,33 @@
<?php
namespace Alchemy\Tests\BinaryDriver\Exceptions;
use Alchemy\BinaryDriver\BinaryDriverTestCase;
use Alchemy\BinaryDriver\Exception\ExecutionFailureException;
use Alchemy\BinaryDriver\ProcessRunner;
class ExecutionFailureExceptionTest extends BinaryDriverTestCase
{
public function getProcessRunner($logger)
{
return new ProcessRunner($logger, 'test-runner');
}
public function testGetExceptionInfo(){
$logger = $this->createLoggerMock();
$runner = $this->getProcessRunner($logger);
$process = $this->createProcessMock(1, false, '--helloworld--', null, "Error Output", true);
try{
$runner->run($process, new \SplObjectStorage(), false);
$this->fail('An exception should have been raised');
}
catch (ExecutionFailureException $e){
$this->assertEquals("--helloworld--", $e->getCommand());
$this->assertEquals("Error Output", $e->getErrorOutput());
}
}
}

View File

@ -0,0 +1,54 @@
<?php
namespace Alchemy\Tests\BinaryDriver;
use Alchemy\BinaryDriver\ProcessBuilderFactory;
use LogicException;
use Symfony\Component\Process\Process;
use Symfony\Component\Process\ProcessBuilder;
class LTSProcessBuilder extends ProcessBuilder
{
private $arguments;
private $prefix;
private $timeout;
public function __construct(array $arguments = array())
{
$this->arguments = $arguments;
parent::__construct($arguments);
}
public function setArguments(array $arguments)
{
$this->arguments = $arguments;
return $this;
}
public function setPrefix($prefix)
{
$this->prefix = $prefix;
return $this;
}
public function setTimeout($timeout)
{
$this->timeout = $timeout;
return $this;
}
public function getProcess()
{
if (!$this->prefix && !count($this->arguments)) {
throw new LogicException('You must add() command arguments before calling getProcess().');
}
$args = $this->prefix ? array_merge(array($this->prefix), $this->arguments) : $this->arguments;
$script = implode(' ', array_map('escapeshellarg', $args));
return new Process($script, null, null, null, $this->timeout);
}
}

View File

@ -0,0 +1,28 @@
<?php
namespace Alchemy\Tests\BinaryDriver;
use Alchemy\BinaryDriver\ProcessBuilderFactory;
class LTSProcessBuilderFactoryTest extends AbstractProcessBuilderFactoryTest
{
public function setUp()
{
if (!class_exists('Symfony\Component\Process\ProcessBuilder')) {
$this->markTestSkipped('ProcessBuilder is not available.');
return;
}
parent::setUp();
}
protected function getProcessBuilderFactory($binary)
{
$factory = new ProcessBuilderFactory($binary);
$factory->setBuilder(new LTSProcessBuilder());
ProcessBuilderFactory::$emulateSfLTS = false;
$factory->useBinary($binary);
return $factory;
}
}

View File

@ -0,0 +1,33 @@
<?php
namespace Alchemy\Tests\BinaryDriver\Listeners;
use Alchemy\BinaryDriver\Listeners\DebugListener;
use Symfony\Component\Process\Process;
class DebugListenerTest extends \PHPUnit_Framework_TestCase
{
public function testHandle()
{
$listener = new DebugListener();
$lines = array();
$listener->on('debug', function ($line) use (&$lines) {
$lines[] = $line;
});
$listener->handle(Process::ERR, "first line\nsecond line");
$listener->handle(Process::OUT, "cool output");
$listener->handle('unknown', "lalala");
$listener->handle(Process::OUT, "another output\n");
$expected = array(
'[ERROR] first line',
'[ERROR] second line',
'[OUT] cool output',
'[OUT] another output',
'[OUT] ',
);
$this->assertEquals($expected, $lines);
}
}

View File

@ -0,0 +1,92 @@
<?php
namespace Alchemy\Tests\BinaryDriver\Listeners;
use Alchemy\BinaryDriver\Listeners\Listeners;
use Evenement\EventEmitter;
use Alchemy\BinaryDriver\Listeners\ListenerInterface;
class ListenersTest extends \PHPUnit_Framework_TestCase
{
public function testRegister()
{
$listener = new MockListener();
$listeners = new Listeners();
$listeners->register($listener);
$n = 0;
$listener->on('received', function ($type, $data) use (&$n, &$capturedType, &$capturedData) {
$n++;
$capturedData = $data;
$capturedType = $type;
});
$type = 'type';
$data = 'data';
$listener->handle($type, $data);
$listener->handle($type, $data);
$listeners->unregister($listener);
$listener->handle($type, $data);
$this->assertEquals(3, $n);
$this->assertEquals($type, $capturedType);
$this->assertEquals($data, $capturedData);
}
public function testRegisterAndForwardThenUnregister()
{
$listener = new MockListener();
$target = new EventEmitter();
$n = 0;
$target->on('received', function ($type, $data) use (&$n, &$capturedType, &$capturedData) {
$n++;
$capturedData = $data;
$capturedType = $type;
});
$m = 0;
$listener->on('received', function ($type, $data) use (&$m, &$capturedType2, &$capturedData2) {
$m++;
$capturedData2 = $data;
$capturedType2 = $type;
});
$listeners = new Listeners();
$listeners->register($listener, $target);
$type = 'type';
$data = 'data';
$listener->handle($type, $data);
$listener->handle($type, $data);
$listeners->unregister($listener, $target);
$listener->handle($type, $data);
$this->assertEquals(2, $n);
$this->assertEquals(3, $m);
$this->assertEquals($type, $capturedType);
$this->assertEquals($data, $capturedData);
$this->assertEquals($type, $capturedType2);
$this->assertEquals($data, $capturedData2);
}
}
class MockListener extends EventEmitter implements ListenerInterface
{
public function handle($type, $data)
{
$this->emit('received', array($type, $data));
}
public function forwardedEvents()
{
return array('received');
}
}

View File

@ -0,0 +1,15 @@
<?php
namespace Alchemy\Tests\BinaryDriver;
use Alchemy\BinaryDriver\ProcessBuilderFactory;
class NONLTSProcessBuilderFactoryTest extends AbstractProcessBuilderFactoryTest
{
protected function getProcessBuilderFactory($binary)
{
ProcessBuilderFactory::$emulateSfLTS = true;
return new ProcessBuilderFactory($binary);
}
}

View File

@ -0,0 +1,208 @@
<?php
/*
* This file is part of Alchemy\BinaryDriver.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Tests\BinaryDriver;
use Alchemy\BinaryDriver\ProcessRunner;
use Alchemy\BinaryDriver\BinaryDriverTestCase;
use Alchemy\BinaryDriver\Exception\ExecutionFailureException;
use Alchemy\BinaryDriver\Listeners\ListenerInterface;
use Evenement\EventEmitter;
use Symfony\Component\Process\Exception\RuntimeException as ProcessRuntimeException;
class ProcessRunnerTest extends BinaryDriverTestCase
{
public function getProcessRunner($logger)
{
return new ProcessRunner($logger, 'test-runner');
}
public function testRunSuccessFullProcess()
{
$logger = $this->createLoggerMock();
$runner = $this->getProcessRunner($logger);
$process = $this->createProcessMock(1, true, '--helloworld--', "Kikoo Romain", null, true);
$logger
->expects($this->never())
->method('error');
$logger
->expects($this->exactly(2))
->method('info');
$this->assertEquals('Kikoo Romain', $runner->run($process, new \SplObjectStorage(), false));
}
public function testRunSuccessFullProcessBypassingErrors()
{
$logger = $this->createLoggerMock();
$runner = $this->getProcessRunner($logger);
$process = $this->createProcessMock(1, true, '--helloworld--', "Kikoo Romain", null, true);
$logger
->expects($this->never())
->method('error');
$logger
->expects($this->exactly(2))
->method('info');
$this->assertEquals('Kikoo Romain', $runner->run($process, new \SplObjectStorage(), true));
}
public function testRunFailingProcess()
{
$logger = $this->createLoggerMock();
$runner = $this->getProcessRunner($logger);
$process = $this->createProcessMock(1, false, '--helloworld--', null, null, true);
$logger
->expects($this->once())
->method('error');
$logger
->expects($this->once())
->method('info');
try {
$runner->run($process, new \SplObjectStorage(), false);
$this->fail('An exception should have been raised');
} catch (ExecutionFailureException $e) {
}
}
public function testRunFailingProcessWithException()
{
$logger = $this->createLoggerMock();
$runner = $this->getProcessRunner($logger);
$exception = new ProcessRuntimeException('Process Failed');
$process = $this->getMockBuilder('Symfony\Component\Process\Process')
->disableOriginalConstructor()
->getMock();
$process->expects($this->once())
->method('run')
->will($this->throwException($exception));
$logger
->expects($this->once())
->method('error');
$logger
->expects($this->once())
->method('info');
try {
$runner->run($process, new \SplObjectStorage(), false);
$this->fail('An exception should have been raised');
} catch (ExecutionFailureException $e) {
$this->assertEquals($exception, $e->getPrevious());
}
}
public function testRunfailingProcessBypassingErrors()
{
$logger = $this->createLoggerMock();
$runner = $this->getProcessRunner($logger);
$process = $this->createProcessMock(1, false, '--helloworld--', 'Hello output', null, true);
$logger
->expects($this->once())
->method('error');
$logger
->expects($this->once())
->method('info');
$this->assertNull($runner->run($process, new \SplObjectStorage(), true));
}
public function testRunFailingProcessWithExceptionBypassingErrors()
{
$logger = $this->createLoggerMock();
$runner = $this->getProcessRunner($logger);
$exception = new ProcessRuntimeException('Process Failed');
$process = $this->getMockBuilder('Symfony\Component\Process\Process')
->disableOriginalConstructor()
->getMock();
$process->expects($this->once())
->method('run')
->will($this->throwException($exception));
$logger
->expects($this->once())
->method('error');
$logger
->expects($this->once())
->method('info');
$this->assertNull($runner->run($process, new \SplObjectStorage(), true));
}
public function testRunSuccessFullProcessWithHandlers()
{
$logger = $this->createLoggerMock();
$runner = $this->getProcessRunner($logger);
$capturedCallback = null;
$process = $this->createProcessMock(1, true, '--helloworld--', "Kikoo Romain", null, true);
$process->expects($this->once())
->method('run')
->with($this->isInstanceOf('Closure'))
->will($this->returnCallback(function ($callback) use (&$capturedCallback) {
$capturedCallback = $callback;
}));
$logger
->expects($this->never())
->method('error');
$logger
->expects($this->exactly(2))
->method('info');
$listener = new TestListener();
$storage = new \SplObjectStorage();
$storage->attach($listener);
$capturedType = $capturedData = null;
$listener->on('received', function ($type, $data) use (&$capturedType, &$capturedData) {
$capturedData = $data;
$capturedType = $type;
});
$this->assertEquals('Kikoo Romain', $runner->run($process, $storage, false));
$type = 'err';
$data = 'data';
$capturedCallback($type, $data);
$this->assertEquals($data, $capturedData);
$this->assertEquals($type, $capturedType);
}
}
class TestListener extends EventEmitter implements ListenerInterface
{
public function handle($type, $data)
{
return $this->emit('received', array($type, $data));
}
public function forwardedEvents()
{
return array();
}
}

View File

@ -0,0 +1,4 @@
<?php
$loader = require __DIR__.'/../vendor/autoload.php';
$loader->add('Alchemy\Tests', __DIR__);

1
vendor/bin/phpdoc vendored Symbolic link
View File

@ -0,0 +1 @@
../phpdocumentor/phpdocumentor/bin/phpdoc

1
vendor/bin/phpdoc.php vendored Symbolic link
View File

@ -0,0 +1 @@
../phpdocumentor/phpdocumentor/bin/phpdoc.php

1
vendor/bin/phpunit vendored Symbolic link
View File

@ -0,0 +1 @@
../phpunit/phpunit/phpunit

1
vendor/bin/psysh vendored Symbolic link
View File

@ -0,0 +1 @@
../psy/psysh/bin/psysh

7
vendor/cilex/cilex/.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
vendor
phpunit.xml
cilex.phar
.idea
composer.phar
.vagrant
docs/.build

View File

@ -0,0 +1,32 @@
group { "puppet":
ensure => "present",
}
node default {
exec { "apt-get update":
command => "/usr/bin/apt-get update"
}
package{["git", "python-setuptools", "wget", "make", "texlive-latex-recommended", "texlive-latex-extra", "texlive-fonts-recommended", "openjdk-6-jre", "php5-cli", "php5-xdebug"]:
ensure => present,
require => Exec["apt-get update"]
}
exec { "sudo easy_install -U sphinx":
command => "/usr/bin/sudo /usr/bin/easy_install -U sphinx",
require => [ Package["python-setuptools"] ],
timeout => 0
}
exec { "get-composer":
command => "/usr/bin/wget -N http://getcomposer.org/composer.phar",
require => Package["wget"],
cwd => "/vagrant"
}
exec { "composer-install":
command => "/usr/bin/php composer.phar install --dev",
require => Exec["get-composer"],
cwd => "/vagrant"
}
}

13
vendor/cilex/cilex/.travis.yml vendored Normal file
View File

@ -0,0 +1,13 @@
language: php
php:
- 5.3
- 5.4
before_script:
- composer install
script: phpunit --coverage-text
notifications:
email:
- mike.vanriel@naenius.com

19
vendor/cilex/cilex/LICENSE vendored Normal file
View File

@ -0,0 +1,19 @@
Copyright (c) 2012 Mike van Riel
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.

70
vendor/cilex/cilex/README.md vendored Normal file
View File

@ -0,0 +1,70 @@
Cilex, a simple Command Line Interface framework
================================================
Cilex is a simple command line application framework to develop simple tools
based on [Symfony2][1] components:
```php
<?php
require_once __DIR__.'/cilex.phar';
$app = new \Cilex\Application('Cilex');
$app->command(new \Cilex\Command\GreetCommand());
$app->run();
```
Cilex works with PHP 5.3.3 or later and is heavily inspired on the [Silex][2]
web micro-framework by Fabien Potencier.
## Installation
1. `git clone` _this_ repository.
2. Download composer: `curl -s https://getcomposer.org/installer | php`
3. Install Cilex' dependencies: `php composer.phar install`
4. Create the phar: `php ./compile`
<!--
## More Information
Read the [documentation][4] for more information.
-->
## Usage
- Download composer: `curl -s https://getcomposer.org/installer | php`
- Add to your dependencies: `php composer.phar require cilex/cilex dev-master`
- Update the dependencies
- Create a `run.php` file
```php
<?php
if (!$loader = include __DIR__.'/vendor/autoload.php') {
die('You must set up the project dependencies.');
}
$app = new \Cilex\Application('Cilex');
$app->command(new \Cilex\Command\GreetCommand());
$app->run();
```
- `php run.php`
- enjoy a lot.
## License
Cilex is licensed under the MIT license.
[1]: http://symfony.com
[2]: http://silex.sensiolabs.org
[3]: http://cilex.github.com/get/cilex.phar
[4]: http://cilex.github.com/documentation
## FAQ
Q: How do I pass configuration into the application?
A: You can do this by adding the following line, where $configPath is the path to the configuration file you want to use:
```php
$app->register(new \Cilex\Provider\ConfigServiceProvider(), array('config.path' => $configPath));
```
The formats currently supported are: YAML, XML and JSON

13
vendor/cilex/cilex/Vagrantfile vendored Normal file
View File

@ -0,0 +1,13 @@
# -*- mode: ruby -*-
# vi: set ft=ruby :
Vagrant::Config.run do |config|
config.vm.box = "precise64"
config.vm.box_url = "http://files.vagrantup.com/precise64.box"
config.vm.provision :puppet do |puppet|
puppet.manifests_path = ".puppet/manifests"
puppet.manifest_file = "manifest.pp"
puppet.options = [ '--verbose' ]
end
end

13
vendor/cilex/cilex/bin/compile vendored Normal file
View File

@ -0,0 +1,13 @@
#!/usr/bin/env php
<?php
$autoload = __DIR__ . '/../vendor/autoload.php';
if (!file_exists($autoload)) {
echo "Please run `php composer.phar install` first." . PHP_EOL;
exit(1);
}
require_once $autoload;
use Cilex\Compiler;
$compiler = new Compiler();
$compiler->compile();

32
vendor/cilex/cilex/composer.json vendored Normal file
View File

@ -0,0 +1,32 @@
{
"name": "cilex/cilex",
"description": "The PHP micro-framework for Command line tools based on the Symfony2 Components",
"keywords": ["microframework","cli"],
"homepage": "http://cilex.github.com",
"license": "MIT",
"autoload":{
"psr-0":{ "Cilex": "src/" }
},
"authors":[
{ "name": "Mike van Riel", "email": "mike.vanriel@naenius.com" }
],
"require":{
"php": ">=5.3.3",
"pimple/pimple": "~1.0",
"symfony/process": "~2.1",
"symfony/finder": "~2.1",
"cilex/console-service-provider": "1.*"
},
"require-dev":{
"symfony/validator": "~2.1",
"phpunit/phpunit": "3.7.*"
},
"suggest":{
"monolog/monolog": ">=1.0.0",
"symfony/yaml": ">=1.0.0",
"symfony/validator": ">=1.0.0"
},
"extra":{
"branch-alias": { "dev-master": "1.0-dev" }
}
}

884
vendor/cilex/cilex/composer.lock generated vendored Normal file
View File

@ -0,0 +1,884 @@
{
"_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"
],
"hash": "028ed7b39a4c2a5ccba9e34589868ee4",
"packages": [
{
"name": "cilex/console-service-provider",
"version": "1.0.0",
"source": {
"type": "git",
"url": "https://github.com/Cilex/console-service-provider.git",
"reference": "1.0.0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Cilex/console-service-provider/zipball/1.0.0",
"reference": "1.0.0",
"shasum": ""
},
"require": {
"php": ">=5.3.3",
"pimple/pimple": "1.*@dev",
"symfony/console": "~2.1"
},
"require-dev": {
"cilex/cilex": "1.*@dev",
"silex/silex": "1.*@dev"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0-dev"
}
},
"autoload": {
"psr-0": {
"Cilex\\Provider\\Console": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Beau Simensen",
"email": "beau@dflydev.com",
"homepage": "http://beausimensen.com"
},
{
"name": "Mike van Riel",
"email": "mike.vanriel@naenius.com"
}
],
"description": "Console Service Provider",
"keywords": [
"cilex",
"console",
"pimple",
"service-provider",
"silex"
],
"time": "2012-12-19 10:50:58"
},
{
"name": "pimple/pimple",
"version": "v1.1.1",
"source": {
"type": "git",
"url": "https://github.com/fabpot/Pimple.git",
"reference": "2019c145fe393923f3441b23f29bbdfaa5c58c4d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/fabpot/Pimple/zipball/2019c145fe393923f3441b23f29bbdfaa5c58c4d",
"reference": "2019c145fe393923f3441b23f29bbdfaa5c58c4d",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.1.x-dev"
}
},
"autoload": {
"psr-0": {
"Pimple": "lib/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com",
"homepage": "http://fabien.potencier.org",
"role": "Lead Developer"
}
],
"description": "Pimple is a simple Dependency Injection Container for PHP 5.3",
"homepage": "http://pimple.sensiolabs.org",
"keywords": [
"container",
"dependency injection"
],
"time": "2013-11-22 08:30:29"
},
{
"name": "symfony/console",
"version": "v2.4.2",
"target-dir": "Symfony/Component/Console",
"source": {
"type": "git",
"url": "https://github.com/symfony/Console.git",
"reference": "940f217cbc3c8a33e5403e7c595495c4884400fe"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Console/zipball/940f217cbc3c8a33e5403e7c595495c4884400fe",
"reference": "940f217cbc3c8a33e5403e7c595495c4884400fe",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"require-dev": {
"symfony/event-dispatcher": "~2.1"
},
"suggest": {
"symfony/event-dispatcher": ""
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.4-dev"
}
},
"autoload": {
"psr-0": {
"Symfony\\Component\\Console\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com",
"homepage": "http://fabien.potencier.org",
"role": "Lead Developer"
},
{
"name": "Symfony Community",
"homepage": "http://symfony.com/contributors"
}
],
"description": "Symfony Console Component",
"homepage": "http://symfony.com",
"time": "2014-02-11 13:52:09"
},
{
"name": "symfony/finder",
"version": "v2.4.2",
"target-dir": "Symfony/Component/Finder",
"source": {
"type": "git",
"url": "https://github.com/symfony/Finder.git",
"reference": "b6735d1fc16da13c4c7dddfe78366a4a098cf011"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Finder/zipball/b6735d1fc16da13c4c7dddfe78366a4a098cf011",
"reference": "b6735d1fc16da13c4c7dddfe78366a4a098cf011",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.4-dev"
}
},
"autoload": {
"psr-0": {
"Symfony\\Component\\Finder\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com",
"homepage": "http://fabien.potencier.org",
"role": "Lead Developer"
},
{
"name": "Symfony Community",
"homepage": "http://symfony.com/contributors"
}
],
"description": "Symfony Finder Component",
"homepage": "http://symfony.com",
"time": "2014-01-07 13:28:54"
},
{
"name": "symfony/process",
"version": "v2.4.2",
"target-dir": "Symfony/Component/Process",
"source": {
"type": "git",
"url": "https://github.com/symfony/Process.git",
"reference": "c175448bac997556f8ab972908a4e14c7291fb03"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Process/zipball/c175448bac997556f8ab972908a4e14c7291fb03",
"reference": "c175448bac997556f8ab972908a4e14c7291fb03",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.4-dev"
}
},
"autoload": {
"psr-0": {
"Symfony\\Component\\Process\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com",
"homepage": "http://fabien.potencier.org",
"role": "Lead Developer"
},
{
"name": "Symfony Community",
"homepage": "http://symfony.com/contributors"
}
],
"description": "Symfony Process Component",
"homepage": "http://symfony.com",
"time": "2014-02-11 13:52:09"
}
],
"packages-dev": [
{
"name": "phpunit/php-code-coverage",
"version": "1.2.17",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
"reference": "6ef2bf3a1c47eca07ea95f0d8a902a6340390b34"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/6ef2bf3a1c47eca07ea95f0d8a902a6340390b34",
"reference": "6ef2bf3a1c47eca07ea95f0d8a902a6340390b34",
"shasum": ""
},
"require": {
"php": ">=5.3.3",
"phpunit/php-file-iterator": ">=1.3.0@stable",
"phpunit/php-text-template": ">=1.2.0@stable",
"phpunit/php-token-stream": ">=1.1.3@stable"
},
"require-dev": {
"phpunit/phpunit": "3.7.*@dev"
},
"suggest": {
"ext-dom": "*",
"ext-xdebug": ">=2.0.5"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.2.x-dev"
}
},
"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": "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": "2014-03-28 10:53:45"
},
{
"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.2.2",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-token-stream.git",
"reference": "ad4e1e23ae01b483c16f600ff1bebec184588e32"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/ad4e1e23ae01b483c16f600ff1bebec184588e32",
"reference": "ad4e1e23ae01b483c16f600ff1bebec184588e32",
"shasum": ""
},
"require": {
"ext-tokenizer": "*",
"php": ">=5.3.3"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.2-dev"
}
},
"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": "Wrapper around PHP's tokenizer extension.",
"homepage": "https://github.com/sebastianbergmann/php-token-stream/",
"keywords": [
"tokenizer"
],
"time": "2014-03-03 05:10:30"
},
{
"name": "phpunit/phpunit",
"version": "3.7.34",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "248d6ce95e6ca7f0e3135252c0b984bbe1f52f19"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/248d6ce95e6ca7f0e3135252c0b984bbe1f52f19",
"reference": "248d6ce95e6ca7f0e3135252c0b984bbe1f52f19",
"shasum": ""
},
"require": {
"ext-dom": "*",
"ext-pcre": "*",
"ext-reflection": "*",
"ext-spl": "*",
"php": ">=5.3.3",
"phpunit/php-code-coverage": "~1.2",
"phpunit/php-file-iterator": "~1.3",
"phpunit/php-text-template": "~1.1",
"phpunit/php-timer": "~1.0",
"phpunit/phpunit-mock-objects": "~1.2",
"symfony/yaml": "~2.0"
},
"require-dev": {
"pear-pear.php.net/pear": "1.9.4"
},
"suggest": {
"ext-json": "*",
"ext-simplexml": "*",
"ext-tokenizer": "*",
"phpunit/php-invoker": "~1.1"
},
"bin": [
"composer/bin/phpunit"
],
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.7.x-dev"
}
},
"autoload": {
"classmap": [
"PHPUnit/"
]
},
"notification-url": "https://packagist.org/downloads/",
"include-path": [
"",
"../../symfony/yaml/"
],
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de",
"role": "lead"
}
],
"description": "The PHP Unit Testing framework.",
"homepage": "http://www.phpunit.de/",
"keywords": [
"phpunit",
"testing",
"xunit"
],
"time": "2014-03-28 11:04:36"
},
{
"name": "phpunit/phpunit-mock-objects",
"version": "1.2.3",
"source": {
"type": "git",
"url": "git://github.com/sebastianbergmann/phpunit-mock-objects.git",
"reference": "1.2.3"
},
"dist": {
"type": "zip",
"url": "https://github.com/sebastianbergmann/phpunit-mock-objects/archive/1.2.3.zip",
"reference": "1.2.3",
"shasum": ""
},
"require": {
"php": ">=5.3.3",
"phpunit/php-text-template": ">=1.1.1@stable"
},
"suggest": {
"ext-soap": "*"
},
"type": "library",
"autoload": {
"classmap": [
"PHPUnit/"
]
},
"notification-url": "https://packagist.org/downloads/",
"include-path": [
""
],
"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": "2013-01-13 10:24:48"
},
{
"name": "symfony/property-access",
"version": "v2.4.2",
"target-dir": "Symfony/Component/PropertyAccess",
"source": {
"type": "git",
"url": "https://github.com/symfony/PropertyAccess.git",
"reference": "37fe0c2dc494b47db4b0850e9dcba3a27cc45c0c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/PropertyAccess/zipball/37fe0c2dc494b47db4b0850e9dcba3a27cc45c0c",
"reference": "37fe0c2dc494b47db4b0850e9dcba3a27cc45c0c",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.4-dev"
}
},
"autoload": {
"psr-0": {
"Symfony\\Component\\PropertyAccess\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com",
"homepage": "http://fabien.potencier.org",
"role": "Lead Developer"
},
{
"name": "Symfony Community",
"homepage": "http://symfony.com/contributors"
}
],
"description": "Symfony PropertyAccess Component",
"homepage": "http://symfony.com",
"keywords": [
"access",
"array",
"extraction",
"index",
"injection",
"object",
"property",
"property path",
"reflection"
],
"time": "2014-02-11 15:39:28"
},
{
"name": "symfony/translation",
"version": "v2.4.2",
"target-dir": "Symfony/Component/Translation",
"source": {
"type": "git",
"url": "https://github.com/symfony/Translation.git",
"reference": "b00fd07417e493e08488e87bcebeb9681fc7323b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Translation/zipball/b00fd07417e493e08488e87bcebeb9681fc7323b",
"reference": "b00fd07417e493e08488e87bcebeb9681fc7323b",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"require-dev": {
"symfony/config": "~2.0",
"symfony/yaml": "~2.2"
},
"suggest": {
"symfony/config": "",
"symfony/yaml": ""
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.4-dev"
}
},
"autoload": {
"psr-0": {
"Symfony\\Component\\Translation\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com",
"homepage": "http://fabien.potencier.org",
"role": "Lead Developer"
},
{
"name": "Symfony Community",
"homepage": "http://symfony.com/contributors"
}
],
"description": "Symfony Translation Component",
"homepage": "http://symfony.com",
"time": "2014-02-03 17:15:33"
},
{
"name": "symfony/validator",
"version": "v2.4.2",
"target-dir": "Symfony/Component/Validator",
"source": {
"type": "git",
"url": "https://github.com/symfony/Validator.git",
"reference": "64d7a54f97a5f53a98864a7461822e341f9416b5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Validator/zipball/64d7a54f97a5f53a98864a7461822e341f9416b5",
"reference": "64d7a54f97a5f53a98864a7461822e341f9416b5",
"shasum": ""
},
"require": {
"php": ">=5.3.3",
"symfony/property-access": "~2.2",
"symfony/translation": "~2.0"
},
"require-dev": {
"doctrine/annotations": "~1.0",
"doctrine/cache": "~1.0",
"symfony/config": "~2.2",
"symfony/http-foundation": "~2.1",
"symfony/intl": "~2.3",
"symfony/yaml": "~2.0"
},
"suggest": {
"doctrine/annotations": "For using the annotation mapping. You will also need doctrine/cache.",
"doctrine/cache": "For using the default cached annotation reader",
"symfony/config": "",
"symfony/http-foundation": "",
"symfony/intl": "",
"symfony/yaml": ""
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.4-dev"
}
},
"autoload": {
"psr-0": {
"Symfony\\Component\\Validator\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com",
"homepage": "http://fabien.potencier.org",
"role": "Lead Developer"
},
{
"name": "Symfony Community",
"homepage": "http://symfony.com/contributors"
}
],
"description": "Symfony Validator Component",
"homepage": "http://symfony.com",
"time": "2014-02-11 13:52:09"
},
{
"name": "symfony/yaml",
"version": "v2.4.2",
"target-dir": "Symfony/Component/Yaml",
"source": {
"type": "git",
"url": "https://github.com/symfony/Yaml.git",
"reference": "bb6ddaf8956139d1b8c360b4b713ed0138e876b3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Yaml/zipball/bb6ddaf8956139d1b8c360b4b713ed0138e876b3",
"reference": "bb6ddaf8956139d1b8c360b4b713ed0138e876b3",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.4-dev"
}
},
"autoload": {
"psr-0": {
"Symfony\\Component\\Yaml\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com",
"homepage": "http://fabien.potencier.org",
"role": "Lead Developer"
},
{
"name": "Symfony Community",
"homepage": "http://symfony.com/contributors"
}
],
"description": "Symfony Yaml Component",
"homepage": "http://symfony.com",
"time": "2014-01-07 13:28:54"
}
],
"aliases": [
],
"minimum-stability": "stable",
"stability-flags": [
],
"platform": {
"php": ">=5.3.3"
},
"platform-dev": [
]
}

BIN
vendor/cilex/cilex/docs/.static/logo.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

153
vendor/cilex/cilex/docs/Makefile vendored Normal file
View File

@ -0,0 +1,153 @@
# Makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
PAPER =
BUILDDIR = .build
# Internal variables.
PAPEROPT_a4 = -D latex_paper_size=a4
PAPEROPT_letter = -D latex_paper_size=letter
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
# the i18n builder cannot share the environment and doctrees with the others
I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
help:
@echo "Please use \`make <target>' where <target> is one of"
@echo " html to make standalone HTML files"
@echo " dirhtml to make HTML files named index.html in directories"
@echo " singlehtml to make a single large HTML file"
@echo " pickle to make pickle files"
@echo " json to make JSON files"
@echo " htmlhelp to make HTML files and a HTML help project"
@echo " qthelp to make HTML files and a qthelp project"
@echo " devhelp to make HTML files and a Devhelp project"
@echo " epub to make an epub"
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
@echo " latexpdf to make LaTeX files and run them through pdflatex"
@echo " text to make text files"
@echo " man to make manual pages"
@echo " texinfo to make Texinfo files"
@echo " info to make Texinfo files and run them through makeinfo"
@echo " gettext to make PO message catalogs"
@echo " changes to make an overview of all changed/added/deprecated items"
@echo " linkcheck to check all external links for integrity"
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
clean:
-rm -rf $(BUILDDIR)/*
html:
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
dirhtml:
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
singlehtml:
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
@echo
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
pickle:
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
@echo
@echo "Build finished; now you can process the pickle files."
json:
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
@echo
@echo "Build finished; now you can process the JSON files."
htmlhelp:
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
@echo
@echo "Build finished; now you can run HTML Help Workshop with the" \
".hhp project file in $(BUILDDIR)/htmlhelp."
qthelp:
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
@echo
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Cilex.qhcp"
@echo "To view the help file:"
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Cilex.qhc"
devhelp:
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
@echo
@echo "Build finished."
@echo "To view the help file:"
@echo "# mkdir -p $$HOME/.local/share/devhelp/Cilex"
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Cilex"
@echo "# devhelp"
epub:
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
@echo
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
latex:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
@echo "Run \`make' in that directory to run these through (pdf)latex" \
"(use \`make latexpdf' here to do that automatically)."
latexpdf:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo "Running LaTeX files through pdflatex..."
$(MAKE) -C $(BUILDDIR)/latex all-pdf
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
text:
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
@echo
@echo "Build finished. The text files are in $(BUILDDIR)/text."
man:
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
@echo
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
texinfo:
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
@echo
@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
@echo "Run \`make' in that directory to run these through makeinfo" \
"(use \`make info' here to do that automatically)."
info:
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
@echo "Running Texinfo files through makeinfo..."
make -C $(BUILDDIR)/texinfo info
@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
gettext:
$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
@echo
@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
changes:
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
@echo
@echo "The overview file is in $(BUILDDIR)/changes."
linkcheck:
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
@echo
@echo "Link check complete; look for any errors in the above output " \
"or in $(BUILDDIR)/linkcheck/output.txt."
doctest:
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
@echo "Testing of doctests in the sources finished, look at the " \
"results in $(BUILDDIR)/doctest/output.txt."

45
vendor/cilex/cilex/docs/conf.py vendored Normal file
View File

@ -0,0 +1,45 @@
import sys, os
templates_path = ['.templates']
source_suffix = '.rst'
master_doc = 'index'
project = u'Cilex'
copyright = u'2013, Mike van Riel'
version = '1.0'
release = '1.0.0-alpha1'
pygments_style = 'sphinx'
html_theme = 'agogo'
html_static_path = ['.static']
htmlhelp_basename = 'Cilexdoc'
# -- Options for LaTeX output --------------------------------------------------
latex_elements = {
'papersize': 'a4paper',
'pointsize': '10pt',
}
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
('index', 'Cilex.tex', u'Cilex Documentation', u'Mike van Riel', 'manual'),
]
latex_logo = '.static/logo.png'
# -- Options for manual page output --------------------------------------------
# (source start file, name, description, authors, manual section).
man_pages = [
('index', 'cilex', u'Cilex Documentation', [u'Mike van Riel'], 1)
]
# -- Options for Texinfo output ------------------------------------------------
# (source start file, target name, title, author, dir menu entry, description,
# category)
texinfo_documents = [
('index', 'Cilex', u'Cilex Documentation', u'Mike van Riel', 'Cilex', 'One line description of project.',
'Miscellaneous'),
]

9
vendor/cilex/cilex/docs/index.rst vendored Normal file
View File

@ -0,0 +1,9 @@
Cilex' documentation
====================
Contents:
.. toctree::
:maxdepth: 2
usage

82
vendor/cilex/cilex/docs/usage.rst vendored Normal file
View File

@ -0,0 +1,82 @@
Usage
=====
Installation
------------
The easiest way to get started is to use Composer_ using the following steps:
1. Create a file name ``composer.json`` with the following contents:
.. code-block:: json
{
"require": {
"cilex/cilex": "1.0.*@dev"
}
}
2. Download Composer_ and install the project
.. code-block:: bash
$ curl -s http://getcomposer.org/installer | php
$ php composer.phar install
Upgrading
---------
Cilex can be upgraded using Composer_ by calling the update command:
.. code-block:: bash
$ php composer.phar update
Bootstrap
---------
The simplest example of a Cilex application is by including the autoloader, instantiating the Cilex application class
and calling the ``run`` method. The following example demonstrates this principle:
.. code-block:: php
<?php
// application.php
require_once __DIR__ . '/vendor/autoload.php';
$app = new \Cilex\Application('NameOfMyApplication');
// list of commands
$app->run();
Calling your new application is as simple as the following line:
.. code-block:: bash
$ php application.php
Even without actual commands you are greeted with an informative description what to do next:
.. code-block:: bash
NameOfMyApplication version
Usage:
[options] command [arguments]
Options:
--help -h Display this help message.
--quiet -q Do not output any message.
--verbose -v Increase verbosity of messages.
--version -V Display this application version.
--ansi Force ANSI output.
--no-ansi Disable ANSI output.
--no-interaction -n Do not ask any interactive question.
Available commands:
help Displays help for a command
list Lists commands
.. _Composer: http://getcomposer.org

6
vendor/cilex/cilex/example.php vendored Normal file
View File

@ -0,0 +1,6 @@
<?php
require_once __DIR__ . '/vendor/autoload.php';
$app = new \Cilex\Application('Cilex');
//$app->command(new \Cilex\Command\GreetCommand());
$app->run();

19
vendor/cilex/cilex/phpunit.xml.dist vendored Normal file
View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
backupStaticAttributes="false"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
syntaxCheck="false"
bootstrap="vendor/autoload.php"
>
<testsuites>
<testsuite name="Cilex Test Suite">
<directory>./tests/Cilex/</directory>
</testsuite>
</testsuites>
</phpunit>

View File

@ -0,0 +1,111 @@
<?php
/*
* This file is part of the Cilex framework.
*
* (c) Mike van Riel <mike.vanriel@naenius.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Cilex;
use \Symfony\Component\Console;
use \Cilex\Provider\Console\ConsoleServiceProvider;
/**
* The Cilex framework class.
*
* @author Mike van Riel <mike.vanriel@naenius.com>
*
* @api
*/
class Application extends \Pimple
{
/**
* Version number for Cilex
*/
const VERSION = '1.0.0';
/**
* Registers the autoloader and necessary components.
*
* @param string $name Name for this application.
* @param string|null $version Version number for this application.
*/
public function __construct($name, $version = null, array $values = array())
{
parent::__construct();
$consoleConfig = array('console.name' => $name);
if (null !== $version) {
$consoleConfig['console.version'] = $version;
}
$this->register(new ConsoleServiceProvider(), $consoleConfig);
foreach ($values as $key => $value) {
$this[$key] = $value;
}
}
/**
* Executes this application.
*
* @param bool $interactive runs in an interactive shell if true.
*
* @return void
*/
public function run($interactive = false)
{
$app = $this['console'];
if ($interactive) {
$app = new Console\Shell($app);
}
$app->run();
}
/**
* Adds a command object.
*
* If a command with the same name already exists, it will be overridden.
*
* @param \Cilex\Command\Command $command A Command object
*
* @api
*
* @return void
*/
public function command(Console\Command\Command $command)
{
$this['console']->add($command);
}
/**
* Registers a service provider.
*
* @param \Cilex\ServiceProviderInterface|\Silex\ServiceProviderInterface $provider
* A ServiceProviderInterface instance
* @param mixed[] $values
* An array of values that customizes the provider
*
* @return void
*/
public function register($provider, array $values = array())
{
if ((!$provider instanceof \Cilex\ServiceProviderInterface)
&& (!$provider instanceof \Silex\ServiceProviderInterface)
) {
throw new \InvalidArgumentException(
'Extensions should implement either Cilex or Silex\' ServiceProviderInterface'
);
}
$provider->register($this);
foreach ($values as $key => $value) {
$this[$key] = $value;
}
}
}

View File

@ -0,0 +1,55 @@
<?php
/*
* This file is part of the Cilex framework.
*
* (c) Mike van Riel <mike.vanriel@naenius.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Cilex\Command;
use \Symfony\Component\Console;
/**
* Base class for Cilex commands.
*
* @author Mike van Riel <mike.vanriel@naenius.com>
*
* @api
*/
abstract class Command extends Console\Command\Command
{
/**
* Returns the application container.
*
* @return \Cilex\Application
*/
public function getContainer()
{
return $this->getApplication()->getContainer();
}
/**
* Returns a service contained in the application container or null if none
* is found with that name.
*
* This is a convenience method used to retrieve an element from the
* Application container without having to assign the results of the
* getContainer() method in every call.
*
* @param string $name Name of the service
*
* @see self::getContainer()
*
* @api
*
* @return \stdClass|null
*/
public function getService($name)
{
return $this->getApplication()->getService($name);
}
}

View File

@ -0,0 +1,41 @@
<?php
/*
* This file is part of the Cilex framework.
*
* (c) Mike van Riel <mike.vanriel@naenius.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Cilex\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Command\Command as BaseCommand;
/**
* Example command for testing purposes.
*/
class DemoInfoCommand extends BaseCommand
{
protected function configure()
{
$this
->setName('demo:info')
->setDescription('Get Application Information');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
// This is a contrived example to show accessing services
// from the container without needing the command itself
// to extend from anything but Symfony Console's base Command.
$app = $this->getApplication()->getService('console');
$output->writeln('Name: ' . $app->getName());
$output->writeln('Version: ' . $app->getVersion());
}
}

View File

@ -0,0 +1,47 @@
<?php
/*
* This file is part of the Cilex framework.
*
* (c) Mike van Riel <mike.vanriel@naenius.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Cilex\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Example command for testing purposes.
*/
class GreetCommand extends Command
{
protected function configure()
{
$this
->setName('demo:greet')
->setDescription('Greet someone')
->addArgument('name', InputArgument::OPTIONAL, 'Who do you want to greet?')
->addOption('yell', null, InputOption::VALUE_NONE, 'If set, the task will yell in uppercase letters');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$text = 'Hello';
$name = $input->getArgument('name');
if ($name) {
$text .= ' '.$name;
}
if ($input->getOption('yell')) {
$text = strtoupper($text);
}
$output->writeln($text);
}
}

View File

@ -0,0 +1,184 @@
<?php
/*
* This file is part of the Cilex framework.
*
* (c) Mike van Riel <mike.vanriel@naenius.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Cilex;
use Symfony\Component\Finder\Finder;
use Symfony\Component\Process\Process;
/**
* The Compiler class compiles the Cilex framework.
*
* This is an adapted version of the Silex\Compiler class.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Mike van Riel <mike.vanriel@naenius.com>
*/
class Compiler
{
protected $version;
/**
* Compiles the Cilex source code into one single Phar file.
*
* @param string $pharFile Name of the output Phar file
*/
public function compile($pharFile = 'cilex.phar')
{
if (file_exists($pharFile)) {
unlink($pharFile);
}
$process = new Process('git log --pretty="%h %ci" -n1 HEAD');
if ($process->run() > 0) {
throw new \RuntimeException('The git binary cannot be found.');
}
$this->version = trim($process->getOutput());
$phar = new \Phar($pharFile, 0, 'cilex.phar');
$phar->setSignatureAlgorithm(\Phar::SHA1);
$phar->startBuffering();
$finder = new Finder();
$finder->files()
->ignoreVCS(true)
->name('*.php')
->notName('Compiler.php')
->in(__DIR__.'/..')
->in(__DIR__.'/../../vendor/pimple/pimple/lib')
->in(__DIR__.'/../../vendor/cilex/console-service-provider')
->in(__DIR__.'/../../vendor/symfony/console/Symfony/Component/Console');
foreach ($finder as $file) {
$this->addFile($phar, $file);
}
$this->addFile($phar, new \SplFileInfo(__DIR__.'/../../LICENSE'), false);
$this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/autoload.php'));
$this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/composer/ClassLoader.php'));
$this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/composer/autoload_real.php'));
$this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/composer/autoload_namespaces.php'));
$this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/composer/autoload_classmap.php'));
// Stubs
$phar->setStub($this->getStub());
$phar->stopBuffering();
// $phar->compressFiles(\Phar::GZ);
unset($phar);
}
protected function addFile(\Phar $phar, \splFileInfo $file, $strip = true)
{
$path = str_replace(dirname(dirname(__DIR__)).DIRECTORY_SEPARATOR, '', $file->getRealPath());
$content = file_get_contents($file);
if ($strip) {
$content = self::stripWhitespace($content);
}
$content = str_replace('@package_version@', $this->version, $content);
$phar->addFromString($path, $content);
}
protected function getStub()
{
return <<<'EOF'
<?php
/*
* This file is part of the Cilex framework.
*
* (c) Mike van Riel <mike.vanriel@naenius.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
Phar::mapPhar('cilex.phar');
require_once 'phar://cilex.phar/vendor/autoload.php';
if ('cli' === php_sapi_name() && basename(__FILE__) === basename($_SERVER['argv'][0]) && isset($_SERVER['argv'][1])) {
switch ($_SERVER['argv'][1]) {
case 'update':
$remoteFilename = 'http://cilex.github.com/get/cilex.phar';
$localFilename = __DIR__.'/cilex.phar';
file_put_contents($localFilename, file_get_contents($remoteFilename));
break;
case 'check':
$latest = trim(file_get_contents('http://cilex.github.com/get/version'));
if ($latest != Cilex\Application::VERSION) {
printf("A newer Cilex version is available (%s).\n", $latest);
} else {
print("You are using the latest Cilex version.\n");
}
break;
case 'version':
printf("Cilex version %s\n", Cilex\Application::VERSION);
break;
default:
printf("Unknown command '%s' (available commands: version, check, and update).\n", $_SERVER['argv'][1]);
}
exit(0);
}
__HALT_COMPILER();
EOF;
}
/**
* Removes whitespace from a PHP source string while preserving line numbers.
*
* Based on Kernel::stripComments(), but keeps line numbers intact.
*
* @param string $source A PHP string
*
* @return string The PHP string with the whitespace removed
*/
public static function stripWhitespace($source)
{
if (!function_exists('token_get_all')) {
return $source;
}
$output = '';
foreach (token_get_all($source) as $token) {
if (is_string($token)) {
$output .= $token;
} elseif (in_array($token[0], array(T_COMMENT, T_DOC_COMMENT))) {
$output .= str_repeat("\n", substr_count($token[1], "\n"));
} elseif (T_WHITESPACE === $token[0]) {
// reduce wide spaces
$whitespace = preg_replace('{[ \t]+}', ' ', $token[1]);
// normalize newlines to \n
$whitespace = preg_replace('{(?:\r\n|\r|\n)}', "\n", $whitespace);
// trim leading spaces
$whitespace = preg_replace('{\n +}', "\n", $whitespace);
$output .= $whitespace;
} else {
$output .= $token[1];
}
}
return $output;
}
}

View File

@ -0,0 +1,68 @@
<?php
/*
* This file is part of the Cilex framework.
*
* (c) Mike van Riel <mike.vanriel@naenius.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Cilex\Provider;
use Cilex\Application;
use Cilex\ServiceProviderInterface;
use Symfony\Component\Yaml;
class ConfigServiceProvider implements ServiceProviderInterface
{
public function register(Application $app)
{
$app['config'] = $app->share(
function () use ($app) {
if (!file_exists($app['config.path'])) {
throw new \InvalidArgumentException(
$app['config.path'] . ' is not a valid path to the '
.'configuration'
);
}
$fullpath = explode('.', $app['config.path']);
switch (strtolower(end($fullpath))) {
case 'php':
$result = include($app['config.path']);
break;
case 'yml':
$parser = new Yaml\Parser();
$result = new \ArrayObject(
$parser->parse(file_get_contents($app['config.path']))
);
break;
case 'xml':
$result = simplexml_load_file($app['config.path']);
break;
case 'json':
$result = json_decode(file_get_contents($app['config.path']));
if (null == $result) {
throw new \InvalidArgumentException(
'Unable to decode the configuration file: ' . $app['config.path']
);
}
break;
default:
throw new \InvalidArgumentException(
'Unable to load configuration; the provided file extension was not recognized. '
.'Only yml, xml or json allowed'
);
break;
}
return $result;
}
);
}
}

View File

@ -0,0 +1,148 @@
<?php
/*
* This file is part of the Cilex framework.
*
* (c) Mike van Riel <mike.vanriel@naenius.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Cilex\Provider;
use Cilex\Application;
use Cilex\ServiceProviderInterface;
use Doctrine\DBAL\DriverManager;
use Doctrine\DBAL\Configuration;
use Doctrine\Common\EventManager;
/**
* Doctrine DBAL Provider.
*
* This Service Provider is an adaptation of the DoctrineServiceProvider for
* Silex written by Fabien Potencier.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Mike van Riel <mike.vanriel@naenius.com>
*/
class DoctrineServiceProvider implements ServiceProviderInterface
{
public function register(Application $app)
{
$app['db.default_options'] = array(
'driver' => 'pdo_mysql',
'dbname' => null,
'host' => 'localhost',
'user' => 'root',
'password' => null,
);
$app['dbs.options.initializer'] = $app->protect(
function () use ($app) {
static $initialized = false;
if ($initialized) {
return;
}
$initialized = true;
if (!isset($app['dbs.options'])) {
$app['dbs.options'] = array('default' => isset($app['db.options']) ? $app['db.options'] : array());
}
$tmp = $app['dbs.options'];
foreach ($tmp as $name => &$options) {
$options = array_replace($app['db.default_options'], $options);
if (!isset($app['dbs.default'])) {
$app['dbs.default'] = $name;
}
}
$app['dbs.options'] = $tmp;
}
);
$app['dbs'] = $app->share(
function () use ($app) {
$app['dbs.options.initializer']();
$dbs = new \Pimple();
foreach ($app['dbs.options'] as $name => $options) {
if ($app['dbs.default'] === $name) {
// we use shortcuts here in case the default has been overriden
$config = $app['db.config'];
$manager = $app['db.event_manager'];
} else {
$config = $app['dbs.config'][$name];
$manager = $app['dbs.event_manager'][$name];
}
$dbs[$name] = DriverManager::getConnection($options, $config, $manager);
}
return $dbs;
}
);
$app['dbs.config'] = $app->share(
function () use ($app) {
$app['dbs.options.initializer']();
$configs = new \Pimple();
foreach ($app['dbs.options'] as $name => $options) {
$configs[$name] = new Configuration();
}
return $configs;
}
);
$app['dbs.event_manager'] = $app->share(
function () use ($app) {
$app['dbs.options.initializer']();
$managers = new \Pimple();
foreach ($app['dbs.options'] as $name => $options) {
$managers[$name] = new EventManager();
}
return $managers;
}
);
// shortcuts for the "first" DB
$app['db'] = $app->share(
function () use ($app) {
$dbs = $app['dbs'];
return $dbs[$app['dbs.default']];
}
);
$app['db.config'] = $app->share(
function () use ($app) {
$dbs = $app['dbs.config'];
return $dbs[$app['dbs.default']];
}
);
$app['db.event_manager'] = $app->share(
function () use ($app) {
$dbs = $app['dbs.event_manager'];
return $dbs[$app['dbs.default']];
}
);
if (isset($app['db.dbal.class_path'])) {
$app['autoloader']->registerNamespace('Doctrine\\DBAL', $app['db.dbal.class_path']);
}
if (isset($app['db.common.class_path'])) {
$app['autoloader']->registerNamespace('Doctrine\\Common', $app['db.common.class_path']);
}
}
}

View File

@ -0,0 +1,62 @@
<?php
/*
* This file is part of the Cilex framework.
*
* (c) Mike van Riel <mike.vanriel@naenius.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Cilex\Provider;
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
use Cilex\Application;
use Cilex\ServiceProviderInterface;
/**
* Monolog Provider.
*
* This class is an adaptation of the Silex MonologServiceProvider written by
* Fabien Potencier.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Mike van Riel <mike.vanvriel@naenius.com>
*/
class MonologServiceProvider implements ServiceProviderInterface
{
public function register(Application $app)
{
$app['monolog'] = $app->share(
function () use ($app) {
$log = new Logger(isset($app['monolog.name']) ? $app['monolog.name'] : 'myapp');
$app['monolog.configure']($log);
return $log;
}
);
$app['monolog.configure'] = $app->protect(
function ($log) use ($app) {
$log->pushHandler($app['monolog.handler']);
}
);
$app['monolog.handler'] = function () use ($app) {
return new StreamHandler($app['monolog.logfile'], $app['monolog.level']);
};
if (!isset($app['monolog.level'])) {
$app['monolog.level'] = function () {
return Logger::DEBUG;
};
}
if (isset($app['monolog.class_path'])) {
$app['autoloader']->registerNamespace('Monolog', $app['monolog.class_path']);
}
}
}

View File

@ -0,0 +1,72 @@
<?php
/*
* This file is part of the Cilex framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Cilex\Provider;
use Cilex\Application;
use Cilex\ServiceProviderInterface;
use Symfony\Component\Validator\Validator;
use Symfony\Component\Validator\Mapping\ClassMetadataFactory;
use Symfony\Component\Validator\Mapping\Loader\StaticMethodLoader;
use Symfony\Component\Validator\ConstraintValidatorFactory;
use Symfony\Component\Validator\DefaultTranslator;
/**
* Symfony Validator component Provider.
*
* This class is an adaptation of the Silex MonologServiceProvider written by
* Fabien Potencier.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Mike van Riel <mike.vanvriel@naenius.com>
*/
class ValidatorServiceProvider implements ServiceProviderInterface
{
public function register(Application $app)
{
$app['validator'] = $app->share(
function () use ($app) {
return new Validator(
$app['validator.mapping.class_metadata_factory'],
$app['validator.validator_factory'],
$app['validator.default_translator']
);
}
);
$app['validator.mapping.class_metadata_factory'] = $app->share(
function () use ($app) {
return new ClassMetadataFactory(new StaticMethodLoader());
}
);
$app['validator.validator_factory'] = $app->share(
function () {
return new ConstraintValidatorFactory();
}
);
$app['validator.default_translator'] = $app->share(
function () {
if (!class_exists('Symfony\\Component\\Validator\\DefaultTranslator')){
return array();
}
return new DefaultTranslator();
}
);
if (isset($app['validator.class_path'])) {
$app['autoloader']->registerNamespace('Symfony\\Component\\Validator', $app['validator.class_path']);
}
}
}

View File

@ -0,0 +1,27 @@
<?php
/*
* This file is part of the Cilex framework.
*
* (c) Mike van Riel <mike.vanriel@naenius.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Cilex;
/**
* Interface that must implement all Cilex service providers.
*
* @author Mike van Riel <mike.vanriel@naenius.com>
*/
interface ServiceProviderInterface
{
/**
* Registers services on the given app.
*
* @param Application $app An Application instance
*/
public function register(Application $app);
}

View File

@ -0,0 +1,99 @@
<?php
/*
* This file is part of the Cilex framework.
*
* (c) Mike van Riel <mike.vanriel@naenius.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Cilex\Tests;
use \Cilex\Application;
/**
* Mock class used to test the register method.
*/
class ServiceProviderMock implements \Cilex\ServiceProviderInterface
{
/**
* Mock method to satisfy interface
*
* @param \Cilex\Application $app
*
* @return void
*/
function register(\Cilex\Application $app)
{
$app['mock.param'] = false;
$app['mock'] = $this;
}
}
/**
* Application test cases.
*
* @author Mike van Riel <mike.vanriel@naenius.com>
*/
class ApplicationTest extends \PHPUnit_Framework_TestCase
{
const NAME = 'Test';
const VERSION = '1.0.1';
/** @var \Cilex\Application */
protected $fixture = null;
/**
* Sets up the test fixture.
*/
public function setUp()
{
$this->fixture = new Application(self::NAME, self::VERSION);
}
/**
* Tests whether the constructor instantiates the correct dependencies and
* correctly sets the name on the Console's Application.
*/
public function testConstruct()
{
$this->assertInstanceOf(
'\\Symfony\\Component\\Console\\Application',
$this->fixture['console']
);
$this->assertEquals(self::NAME, $this->fixture['console']->getName());
$this->assertEquals(self::VERSION, $this->fixture['console']->getVersion());
}
/**
* Tests the command method to see if the command is properly set and the
* Cilex application is added as container.
*/
public function testCommand()
{
$this->assertFalse($this->fixture['console']->has('demo:greet'));
$this->fixture->command(new \Cilex\Command\GreetCommand());
$this->assertTrue($this->fixture['console']->has('demo:greet'));
$this->assertSame(
$this->fixture,
$this->fixture['console']->get('demo:greet')->getContainer()
);
}
/**
* Tests whether the register method applies the provided parameters to this
* application and correctly registers the ServiceProvider.
*/
public function testRegister()
{
$provider = new ServiceProviderMock();
$this->fixture->register($provider, array('mock.param' => true));
$this->assertTrue($this->fixture['mock.param']);
$this->assertSame($this->fixture['mock'], $provider);
}
}

View File

@ -0,0 +1,61 @@
<?php
/*
* This file is part of the Cilex framework.
*
* (c) Mike van Riel <mike.vanriel@naenius.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Cilex\Tests\Command;
use \Cilex\Command;
class CommandMock extends Command\Command {}
/**
* Command\Command test cases.
*
* @author Mike van Riel <mike.vanriel@naenius.com>
*/
class CommandTest extends \PHPUnit_Framework_TestCase
{
/** @var \Cilex\Command\Command */
protected $fixture = null;
/**
* Sets up the test fixture.
*/
public function setUp()
{
$this->fixture = new CommandMock('demo:test');
}
/**
* Tests the getContainer method.
*/
public function testContainer()
{
$app = new \Cilex\Application('Test');
$app->command($this->fixture);
$this->assertSame($app, $this->fixture->getContainer());
}
/**
* Tests whether the getService method correctly retrieves an element from
* the container.
*/
public function testGetService()
{
$app = new \Cilex\Application('Test');
$app->command($this->fixture);
$this->assertInstanceOf(
'\Symfony\Component\Console\Application',
$this->fixture->getService('console')
);
}
}

View File

@ -0,0 +1,136 @@
<?php
/**
* This file is part of the Cilex framework.
*
* (c) Mike van Riel <mike.vanriel@naenius.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @author Ben Selby <benmatselby@gmail.com>
*/
namespace Cilex\Tests\Provider;
use Cilex\Application;
use Cilex\Provider\ConfigServiceProvider;
/**
* Test file for ConfigServiceProvider
*
* @author Ben Selby <benmatselby@gmail.com>
*/
class ConfigServiceProviderTest extends \PHPUnit_Framework_TestCase
{
/**
* Test that an exception is thrown if the config path is not present
* or valid
*
* @return void
*/
public function testRegisterWillThrowExceptionIfConfigPathIsNotThere()
{
$this->setExpectedException(
'InvalidArgumentException',
__DIR__.'/../../../data/unknownfile is not a valid path to the configuration'
);
$app = new Application('Test');
$app->register(
new ConfigServiceProvider(),
array(
'config.path' => __DIR__.'/../../../data/unknownfile'
)
);
$config = $app['config'];
}
/**
* Test that the config provider can parse a json
* configuration file
*
* @return void
*/
public function testRegisterCanParseAJsonConfigFile()
{
$app = new Application('Test');
$app->register(
new ConfigServiceProvider(),
array(
'config.path' => __DIR__.'/../../../data/config.json'
)
);
$config = $app['config'];
$this->assertEquals($config->key, 'value');
}
public function testCanParseAPhpConfigFile()
{
$app = new Application('Test');
$app->register(
new ConfigServiceProvider(),
array(
'config.path' => __DIR__.'/../../../data/config.php'
)
);
$config = $app['config'];
$this->assertEquals($config['key'], 'value');
}
/**
* Test that the config provider can throw an exception if
* the json configuration file is invalid
*
* @return void
*/
public function testRegisterThrowsExceptionInCaseOfInvalidJsonConfigFile()
{
$configPath = __DIR__.'/../../../data/config-invalid.json';
$this->setExpectedException(
'InvalidArgumentException',
'Unable to decode the configuration file: ' . $configPath
);
$app = new Application('Test');
$app->register(
new ConfigServiceProvider(),
array(
'config.path' => $configPath
)
);
$config = $app['config'];
}
/**
* Test that register will throw an exception if an unknown
* format is passed in
*
* @return void
*/
public function testRegisterThrowsExceptionIfAnUnknownFormatIsPassed()
{
$this->setExpectedException(
'InvalidArgumentException',
'Unable to load configuration; the provided file extension was not recognized. Only yml, xml or json allowed'
);
$app = new Application('Test');
$app->register(
new ConfigServiceProvider(),
array(
'config.path' => __DIR__.'/../../../data/config.unknownfiletype'
)
);
$config = $app['config'];
}
}

View File

@ -0,0 +1,52 @@
<?php
/*
* This file is part of the Cilex framework.
*
* (c) Mike van Riel <mike.vanriel@naenius.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Cilex\Tests\Provider;
use Cilex\Application;
use Cilex\Provider\ValidatorServiceProvider;
/**
* ValidatorServiceProvider.
*
* Originally provided with the Silex Framework; test has been adapted for Cilex.
*
* @author Javier Lopez <f12loalf@gmail.com>
* @author Mike van Riel <mike.vanriel@naenius.com>
*/
class ValidatorServiceProviderTest extends \PHPUnit_Framework_TestCase
{
public function setUp()
{
if (!is_dir(__DIR__.'/../../../../vendor/symfony/validator/Symfony/Component/Validator')) {
$this->markTestSkipped('Validator submodule was not installed.');
}
}
public function testRegister()
{
$app = new Application('Test');
$app->register(new ValidatorServiceProvider(), array(
'validator.class_path' => __DIR__.'/../../../../vendor/Symfony/Component/Validator'
));
return $app;
}
/**
* @depends testRegister
*/
public function testValidatorServiceIsAValidator($app)
{
$this->assertInstanceOf('Symfony\Component\Validator\Validator', $app['validator']);
}
}

View File

@ -0,0 +1,4 @@
{
"key": "value"
"missing": "comma"
}

View File

@ -0,0 +1,3 @@
{
"key": "value"
}

View File

@ -0,0 +1,4 @@
<?php
return array(
'key' => 'value'
);

View File

View File

@ -0,0 +1,2 @@
vendor
.idea

View File

@ -0,0 +1,163 @@
Console Service Provider
========================
Provides [Console][symfony/console] as a service to [Pimple][pimple] applications.
Requirements
------------
* PHP 5.3+
* Symfony Console ~2.1
Installation
------------
Through [Composer][composer] as [cilex/console-service-provider][cilex/console-service-provider].
Usage
-----
### Pimple
```php
<?php
use Cilex\Provider\Console\BaseConsoleServiceProvider;
$app = new Pimple;
$app['console.name'] = 'MyApp';
$app['console.version'] = '1.0.5';
$consoleServiceProvider = new BaseConsoleServiceProvider;
$consoleServiceProvider->register($app);
$app['console']->run();
```
### Silex
To use the Console Service Provider in a Silex application register the
Console Service Provider Silex adapter.
```php
<?php
use Cilex\Provider\Console\Adapter\Silex\ConsoleServiceProvider;
use Silex\Application;
$app = new Application;
$app->register(new ConsoleServiceProvider(), array(
'console.name' => 'MyApp',
'console.version' => '1.0.5',
));
$app['console']->run();
```
### Cilex
The Console Service Provider is baked into the Cilex Application itself so
there is no need to register it manually.
```php
<?php
use Cilex\Application;
$app = new Application('MyApp', '1.0.5');
$app->run();
```
Configuration
-------------
### Parameters
* **console.name**:
Name for the console application.
* **console.version**:
Version for the console application.
* **console.class**:
Class for the console application to be created.
### Services
* **console**:
Console Application, instance `Symfony\Component\Console\Application`.
The Console Service Provider Application Class
----------------------------------------------
By default Console Service Provider will instantiate an instance of
`Cilex\Pimple\Provider\Console\Application`.
### Methods
#### getContainer() : \Pimple
Returns the Pimple container.
#### getService($name) : \stdClass|null
Returns a service contained in the application container or null if none
is found with that name. Convenience method to avoid repeated calls to
`getContainer()` or having to assign the container.
Accessing the Container and Services from Commands
--------------------------------------------------
Here are some examples of accessing the Container and Services from a Command:
```php
<?php
use Symfony\Component\Console\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class SomeCommand extends Command
{
protected function configure()
{
// configure the command
}
protected function execute(InputInterface $input, OutputInterface $output)
{
// Direct access to the Container.
$container = $this->getApplication()->getContainer();
// Direct access to a service.
$service = $this->getApplication->getService('some.service');
}
}
```
Future
------
In the event that Pimple Service Providers become a reality, `ConsoleServiceProvider`
will implement the appropriate interface. As soon as Cilex and Silex become Pimple
Service Provider aware, their respective adapter classes can be bypassed and the
core `ConsoleServiceProvider` can be used directly.
License
-------
MIT, see LICENSE.
[symfony/console]: http://symfony.com/doc/current/components/console/introduction.html
[pimple]: http://pimple.sensiolabs.org
[composer]: http://getcomposer.org
[cilex/console-service-provider]: https://packagist.org/packages/cilex/console-service-provider

View File

@ -0,0 +1,27 @@
{
"name": "cilex/console-service-provider",
"description": "Console Service Provider",
"keywords": ["pimple", "cilex", "silex", "console", "service-provider"],
"license": "MIT",
"authors": [
{ "name": "Mike van Riel", "email": "mike.vanriel@naenius.com" },
{ "name": "Beau Simensen", "email": "beau@dflydev.com" }
],
"require": {
"php": ">=5.3.3",
"pimple/pimple": "1.*@dev",
"symfony/console": "~2.1"
},
"require-dev": {
"cilex/cilex": "1.*@dev",
"silex/silex": "1.*@dev"
},
"autoload": {
"psr-0": { "Cilex\\Provider\\Console": "src" }
},
"extra": {
"branch-alias": {
"dev-master": "1.0-dev"
}
}
}

110
vendor/cilex/console-service-provider/composer.lock generated vendored Normal file
View File

@ -0,0 +1,110 @@
{
"hash": "52d1f50b5bb3e0f2626a352a0ed2a947",
"packages": [
{
"name": "pimple/pimple",
"version": "dev-master",
"source": {
"type": "git",
"url": "git://github.com/fabpot/Pimple.git",
"reference": "v1.0.1"
},
"dist": {
"type": "zip",
"url": "https://github.com/fabpot/Pimple/archive/v1.0.1.zip",
"reference": "v1.0.1",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"time": "2012-11-11 08:32:34",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"installation-source": "source",
"autoload": {
"psr-0": {
"Pimple": "lib/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
}
],
"description": "Pimple is a simple Dependency Injection Container for PHP 5.3",
"homepage": "http://pimple.sensiolabs.org",
"keywords": [
"dependency injection",
"container"
]
},
{
"name": "symfony/console",
"version": "v2.1.4",
"target-dir": "Symfony/Component/Console",
"source": {
"type": "git",
"url": "https://github.com/symfony/Console",
"reference": "v2.1.4"
},
"dist": {
"type": "zip",
"url": "https://github.com/symfony/Console/archive/v2.1.4.zip",
"reference": "v2.1.4",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"time": "2012-11-09 08:52:51",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.1-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-0": {
"Symfony\\Component\\Console": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "http://symfony.com/contributors"
}
],
"description": "Symfony Console Component",
"homepage": "http://symfony.com"
}
],
"packages-dev": null,
"aliases": [
],
"minimum-stability": "stable",
"stability-flags": {
"pimple/pimple": 20,
"cilex/cilex": 20,
"silex/silex": 20
}
}

View File

@ -0,0 +1,40 @@
<?php
/*
* This file is part of the Cilex framework.
*
* (c) Mike van Riel <mike.vanriel@naenius.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Cilex\Provider\Console\Adapter\Silex;
use Cilex\Provider\Console\BaseConsoleServiceProvider;
use Silex\Application;
use Silex\ServiceProviderInterface;
/**
* Silex Console Service Provider adapter
*
* @author Beau Simensen <beau@dflydev.com>
*/
class ConsoleServiceProvider implements ServiceProviderInterface
{
/**
* {@inheritdoc}
*/
public function boot(Application $app)
{
}
/**
* {@inheritdoc}
*/
public function register(Application $app)
{
$serviceProvider = new BaseConsoleServiceProvider;
$serviceProvider->register($app);
}
}

View File

@ -0,0 +1,54 @@
<?php
/*
* This file is part of the Cilex framework.
*
* (c) Mike van Riel <mike.vanriel@naenius.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Cilex\Provider\Console;
/**
* Pimple Console Service Provider
*
* @author Beau Simensen <beau@dflydev.com>
*/
class BaseConsoleServiceProvider
{
/**
* {@inheritdoc}
*/
public function register(\Pimple $container)
{
foreach ($this->getDefaults() as $key => $value) {
if (!isset($container[$key])) {
$container[$key] = $value;
}
}
$container['console'] = $container->share(function() use ($container) {
$class = $container['console.class'];
$instance = new $class(
isset($container['console.name']) ? $container['console.name'] : '',
isset($container['console.version']) ? $container['console.version'] : null
);
if ($instance instanceof ContainerAwareApplication) {
$instance->setContainer($container);
}
return $instance;
});
}
protected function getDefaults()
{
return array(
'console.name' => 'Cilex Application',
'console.class' => 'Cilex\Provider\Console\ContainerAwareApplication',
);
}
}

View File

@ -0,0 +1,32 @@
<?php
/*
* This file is part of the Cilex framework.
*
* (c) Mike van Riel <mike.vanriel@naenius.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Cilex\Provider\Console;
use Cilex\Application;
use Cilex\ServiceProviderInterface;
/**
* Cilex Console Service Provider
*
* @author Beau Simensen <beau@dflydev.com>
*/
class ConsoleServiceProvider implements ServiceProviderInterface
{
/**
* {@inheritdoc}
*/
public function register(Application $app)
{
$serviceProvider = new BaseConsoleServiceProvider();
$serviceProvider->register($app);
}
}

View File

@ -0,0 +1,77 @@
<?php
/*
* This file is part of the Cilex framework.
*
* (c) Mike van Riel <mike.vanriel@naenius.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Cilex\Provider\Console;
use Symfony\Component\Console\Application;
/**
* Cilex Pimple Console Application
*
* @author Beau Simensen <beau@dflydev.com>
*/
class ContainerAwareApplication extends Application
{
/** @var \Pimple */
private $container;
/**
* Constructor
*
* @param string $name The name of the application
* @param string $version The version of the application
*/
public function __construct($name = 'UNKNOWN', $version = 'UNKNOWN')
{
parent::__construct($name, $version);
}
/**
* Sets a container instance onto this application.
*
* @param \Pimple $container
*
* @return void
*/
public function setContainer(\Pimple $container)
{
$this->container = $container;
}
/**
* Get the Container.
*
* @return \Pimple
*/
public function getContainer()
{
return $this->container;
}
/**
* Returns a service contained in the application container or null if none is found with that name.
*
* This is a convenience method used to retrieve an element from the Application container without having to assign
* the results of the getContainer() method in every call.
*
* @param string $name Name of the service.
*
* @see self::getContainer()
*
* @api
*
* @return mixed|null
*/
public function getService($name)
{
return isset($this->container[$name]) ? $this->container[$name] : null;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -6,11 +6,21 @@ $vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'320cde22f66dd4f5d3fd621d3e88b98f' => $vendorDir . '/symfony/polyfill-ctype/bootstrap.php',
'10a6847eba8a430a134fad89f4a30a26' => $vendorDir . '/zendframework/zend-cache/autoload/patternPluginManagerPolyfill.php',
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php',
'e88992873b7765f9b5710cab95ba5dd7' => $vendorDir . '/hoa/consistency/Prelude.php',
'fe1bcd0336136e435eaf197895daf81a' => $vendorDir . '/nikic/php-parser/lib/bootstrap.php',
'd9d39f82a605ebe5918f683dd402334c' => $vendorDir . '/padraic/humbug_get_contents/src/function.php',
'3a50d90d85c7fe889a94ae1114b921ce' => $vendorDir . '/padraic/humbug_get_contents/src/functions.php',
'5255c38a0faeba867671b61dfda6d864' => $vendorDir . '/paragonie/random_compat/lib/random.php',
'7b11c4dc42b3b3023073cb14e519683c' => $vendorDir . '/ralouphie/getallheaders/src/getallheaders.php',
'25072dd6e2470089de65ae7bf11d3109' => $vendorDir . '/symfony/polyfill-php72/bootstrap.php',
'a0edc8309cc5e1d60e3047b5df6b7052' => $vendorDir . '/guzzlehttp/psr7/src/functions_include.php',
'6124b4c8570aa390c21fafd04a26c69f' => $vendorDir . '/myclabs/deep-copy/src/DeepCopy/deep_copy.php',
'667aeda72477189d0494fecd327c3641' => $vendorDir . '/symfony/var-dumper/Resources/functions/dump.php',
'2cffec82183ee1cea088009cef9a6fc3' => $vendorDir . '/ezyang/htmlpurifier/library/HTMLPurifier.composer.php',
'757772e28a0943a9afe83def8db95bdf' => $vendorDir . '/mf2/mf2/Mf2/Parser.php',
'decc78cc4436b1292c6c0d151b19445c' => $vendorDir . '/phpseclib/phpseclib/phpseclib/bootstrap.php',
'801c31d8ed748cfa537fa45402288c95' => $vendorDir . '/psy/psysh/src/functions.php',
);

View File

@ -6,6 +6,21 @@ $vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'phpDocumentor' => array($vendorDir . '/phpdocumentor/fileset/src', $vendorDir . '/phpdocumentor/fileset/tests/unit', $vendorDir . '/phpdocumentor/graphviz/src', $vendorDir . '/phpdocumentor/graphviz/tests/unit', $vendorDir . '/phpdocumentor/phpdocumentor/src', $vendorDir . '/phpdocumentor/phpdocumentor/tests/unit', $vendorDir . '/phpdocumentor/reflection/src', $vendorDir . '/phpdocumentor/reflection/tests/unit', $vendorDir . '/phpdocumentor/reflection/tests/mocks', $vendorDir . '/phpdocumentor/reflection-docblock/src'),
'Twig_' => array($vendorDir . '/twig/twig/lib'),
'Pimple' => array($vendorDir . '/pimple/pimple/lib'),
'PhpCollection' => array($vendorDir . '/phpcollection/phpcollection/src'),
'Parsedown' => array($vendorDir . '/erusev/parsedown'),
'Neutron' => array($vendorDir . '/neutron/temporary-filesystem/src'),
'Metadata\\' => array($vendorDir . '/jms/metadata/src'),
'JMS\\Serializer' => array($vendorDir . '/jms/serializer/src'),
'JMS\\' => array($vendorDir . '/jms/parser-lib/src'),
'HTMLPurifier' => array($vendorDir . '/ezyang/htmlpurifier/library'),
'FFMpeg' => array($vendorDir . '/php-ffmpeg/php-ffmpeg/src'),
'Evenement' => array($vendorDir . '/evenement/evenement/src'),
'Console' => array($vendorDir . '/pear/console_getopt'),
'Cilex\\Provider\\Console' => array($vendorDir . '/cilex/console-service-provider/src'),
'Cilex\\Provider' => array($vendorDir . '/phpdocumentor/phpdocumentor/src'),
'Cilex' => array($vendorDir . '/cilex/cilex/src'),
'Alchemy' => array($vendorDir . '/alchemy/binary-driver/src'),
);

View File

@ -7,18 +7,63 @@ $baseDir = dirname($vendorDir);
return array(
'phpseclib\\' => array($vendorDir . '/phpseclib/phpseclib/phpseclib'),
'Zend\\Stdlib\\' => array($vendorDir . '/zendframework/zend-stdlib/src'),
'Zend\\ServiceManager\\' => array($vendorDir . '/zendframework/zend-servicemanager/src'),
'Zend\\Serializer\\' => array($vendorDir . '/zendframework/zend-serializer/src'),
'Zend\\Json\\' => array($vendorDir . '/zendframework/zend-json/src'),
'Zend\\I18n\\' => array($vendorDir . '/zendframework/zend-i18n/src'),
'Zend\\Hydrator\\' => array($vendorDir . '/zendframework/zend-hydrator/src'),
'Zend\\Filter\\' => array($vendorDir . '/zendframework/zend-filter/src'),
'Zend\\EventManager\\' => array($vendorDir . '/zendframework/zend-eventmanager/src'),
'Zend\\Config\\' => array($vendorDir . '/zendframework/zend-config/src'),
'Zend\\Cache\\' => array($vendorDir . '/zendframework/zend-cache/src'),
'XdgBaseDir\\' => array($vendorDir . '/dnoegel/php-xdg-base-dir/src'),
'XMPPHP\\' => array($vendorDir . '/diogocomposer/xmpphp/XMPPHP'),
'Webmozart\\Assert\\' => array($vendorDir . '/webmozart/assert/src'),
'Twig\\' => array($vendorDir . '/twig/twig/src'),
'Symfony\\Polyfill\\Php72\\' => array($vendorDir . '/symfony/polyfill-php72'),
'Symfony\\Polyfill\\Mbstring\\' => array($vendorDir . '/symfony/polyfill-mbstring'),
'Symfony\\Polyfill\\Ctype\\' => array($vendorDir . '/symfony/polyfill-ctype'),
'Symfony\\Component\\VarDumper\\' => array($vendorDir . '/symfony/var-dumper'),
'Symfony\\Component\\Validator\\' => array($vendorDir . '/symfony/validator'),
'Symfony\\Component\\Translation\\' => array($vendorDir . '/symfony/translation'),
'Symfony\\Component\\Stopwatch\\' => array($vendorDir . '/symfony/stopwatch'),
'Symfony\\Component\\Process\\' => array($vendorDir . '/symfony/process'),
'Symfony\\Component\\Finder\\' => array($vendorDir . '/symfony/finder'),
'Symfony\\Component\\Filesystem\\' => array($vendorDir . '/symfony/filesystem'),
'Symfony\\Component\\EventDispatcher\\' => array($vendorDir . '/symfony/event-dispatcher'),
'Symfony\\Component\\Debug\\' => array($vendorDir . '/symfony/debug'),
'Symfony\\Component\\Console\\' => array($vendorDir . '/symfony/console'),
'Symfony\\Component\\Config\\' => array($vendorDir . '/symfony/config'),
'Stomp\\' => array($vendorDir . '/stomp-php/stomp-php/src'),
'Psy\\' => array($vendorDir . '/psy/psysh/src'),
'Psr\\SimpleCache\\' => array($vendorDir . '/psr/simple-cache/src'),
'Psr\\Log\\' => array($vendorDir . '/psr/log/Psr/Log'),
'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-message/src'),
'Psr\\Container\\' => array($vendorDir . '/psr/container/src'),
'Psr\\Cache\\' => array($vendorDir . '/psr/cache/src'),
'Prophecy\\' => array($vendorDir . '/phpspec/prophecy/src/Prophecy'),
'Predis\\' => array($vendorDir . '/predis/predis/src'),
'PhpOption\\' => array($vendorDir . '/phpoption/phpoption/src/PhpOption'),
'ParagonIE\\ConstantTime\\' => array($vendorDir . '/paragonie/constant_time_encoding/src'),
'Monolog\\' => array($vendorDir . '/monolog/monolog/src/Monolog'),
'Michelf\\' => array($vendorDir . '/michelf/php-markdown/Michelf'),
'Masterminds\\' => array($vendorDir . '/masterminds/html5/src'),
'JakubOnderka\\PhpConsoleHighlighter\\' => array($vendorDir . '/jakub-onderka/php-console-highlighter/src'),
'JakubOnderka\\PhpConsoleColor\\' => array($vendorDir . '/jakub-onderka/php-console-color/src'),
'Intervention\\Image\\' => array($vendorDir . '/intervention/image/src/Intervention/Image'),
'Interop\\Container\\' => array($vendorDir . '/container-interop/container-interop/src/Interop/Container'),
'Humbug\\SelfUpdate\\' => array($vendorDir . '/padraic/phar-updater/src'),
'Humbug\\' => array($vendorDir . '/padraic/humbug_get_contents/src'),
'Hoa\\Exception\\' => array($vendorDir . '/hoa/exception'),
'Hoa\\Event\\' => array($vendorDir . '/hoa/event'),
'Hoa\\Consistency\\' => array($vendorDir . '/hoa/consistency'),
'GuzzleHttp\\Psr7\\' => array($vendorDir . '/guzzlehttp/psr7/src'),
'Embed\\' => array($vendorDir . '/embed/embed/src'),
'Doctrine\\Instantiator\\' => array($vendorDir . '/doctrine/instantiator/src/Doctrine/Instantiator'),
'Doctrine\\Common\\Lexer\\' => array($vendorDir . '/doctrine/lexer/lib/Doctrine/Common/Lexer'),
'Doctrine\\Common\\Cache\\' => array($vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache'),
'Doctrine\\Common\\Annotations\\' => array($vendorDir . '/doctrine/annotations/lib/Doctrine/Common/Annotations'),
'DeepCopy\\' => array($vendorDir . '/myclabs/deep-copy/src/DeepCopy'),
'Composer\\CaBundle\\' => array($vendorDir . '/composer/ca-bundle/src'),
);

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,3 @@
composer.lock
composer.phar
/vendor/

View File

@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2013 container-interop
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.

View File

@ -0,0 +1,148 @@
# Container Interoperability
[![Latest Stable Version](https://poser.pugx.org/container-interop/container-interop/v/stable.png)](https://packagist.org/packages/container-interop/container-interop)
[![Total Downloads](https://poser.pugx.org/container-interop/container-interop/downloads.svg)](https://packagist.org/packages/container-interop/container-interop)
## Deprecation warning!
Starting Feb. 13th 2017, container-interop is officially deprecated in favor of [PSR-11](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-11-container.md).
Container-interop has been the test-bed of PSR-11. From v1.2, container-interop directly extends PSR-11 interfaces.
Therefore, all containers implementing container-interop are now *de-facto* compatible with PSR-11.
- Projects implementing container-interop interfaces are encouraged to directly implement PSR-11 interfaces instead.
- Projects consuming container-interop interfaces are very strongly encouraged to directly type-hint on PSR-11 interfaces, in order to be compatible with PSR-11 containers that are not compatible with container-interop.
Regarding the delegate lookup feature, that is present in container-interop and not in PSR-11, the feature is actually a design pattern. It is therefore not deprecated. Documentation regarding this design pattern will be migrated from this repository into a separate website in the future.
## About
*container-interop* tries to identify and standardize features in *container* objects (service locators,
dependency injection containers, etc.) to achieve interoperability.
Through discussions and trials, we try to create a standard, made of common interfaces but also recommendations.
If PHP projects that provide container implementations begin to adopt these common standards, then PHP
applications and projects that use containers can depend on the common interfaces instead of specific
implementations. This facilitates a high-level of interoperability and flexibility that allows users to consume
*any* container implementation that can be adapted to these interfaces.
The work done in this project is not officially endorsed by the [PHP-FIG](http://www.php-fig.org/), but it is being
worked on by members of PHP-FIG and other good developers. We adhere to the spirit and ideals of PHP-FIG, and hope
this project will pave the way for one or more future PSRs.
## Installation
You can install this package through Composer:
```json
composer require container-interop/container-interop
```
The packages adheres to the [SemVer](http://semver.org/) specification, and there will be full backward compatibility
between minor versions.
## Standards
### Available
- [`ContainerInterface`](src/Interop/Container/ContainerInterface.php).
[Description](docs/ContainerInterface.md) [Meta Document](docs/ContainerInterface-meta.md).
Describes the interface of a container that exposes methods to read its entries.
- [*Delegate lookup feature*](docs/Delegate-lookup.md).
[Meta Document](docs/Delegate-lookup-meta.md).
Describes the ability for a container to delegate the lookup of its dependencies to a third-party container. This
feature lets several containers work together in a single application.
### Proposed
View open [request for comments](https://github.com/container-interop/container-interop/labels/RFC)
## Compatible projects
### Projects implementing `ContainerInterface`
- [Acclimate](https://github.com/jeremeamia/acclimate-container): Adapters for
Aura.Di, Laravel, Nette DI, Pimple, Symfony DI, ZF2 Service manager, ZF2
Dependency injection and any container using `ArrayAccess`
- [Aura.Di](https://github.com/auraphp/Aura.Di)
- [auryn-container-interop](https://github.com/elazar/auryn-container-interop)
- [Burlap](https://github.com/codeeverything/burlap)
- [Chernozem](https://github.com/pyrsmk/Chernozem)
- [Data Manager](https://github.com/chrismichaels84/data-manager)
- [Disco](https://github.com/bitexpert/disco)
- [InDI](https://github.com/idealogica/indi)
- [League/Container](http://container.thephpleague.com/)
- [Mouf](http://mouf-php.com)
- [Njasm Container](https://github.com/njasm/container)
- [PHP-DI](http://php-di.org)
- [Picotainer](https://github.com/thecodingmachine/picotainer)
- [PimpleInterop](https://github.com/moufmouf/pimple-interop)
- [Pimple3-ContainerInterop](https://github.com/Sam-Burns/pimple3-containerinterop) (using Pimple v3)
- [SitePoint Container](https://github.com/sitepoint/Container)
- [Thruster Container](https://github.com/ThrusterIO/container) (PHP7 only)
- [Ultra-Lite Container](https://github.com/ultra-lite/container)
- [Unbox](https://github.com/mindplay-dk/unbox)
- [XStatic](https://github.com/jeremeamia/xstatic)
- [Zend\ServiceManager](https://github.com/zendframework/zend-servicemanager)
- [Zit](https://github.com/inxilpro/Zit)
### Projects implementing the *delegate lookup* feature
- [Aura.Di](https://github.com/auraphp/Aura.Di)
- [Burlap](https://github.com/codeeverything/burlap)
- [Chernozem](https://github.com/pyrsmk/Chernozem)
- [InDI](https://github.com/idealogica/indi)
- [League/Container](http://container.thephpleague.com/)
- [Mouf](http://mouf-php.com)
- [Picotainer](https://github.com/thecodingmachine/picotainer)
- [PHP-DI](http://php-di.org)
- [PimpleInterop](https://github.com/moufmouf/pimple-interop)
- [Ultra-Lite Container](https://github.com/ultra-lite/container)
### Middlewares implementing `ContainerInterface`
- [Alias-Container](https://github.com/thecodingmachine/alias-container): add
aliases support to any container
- [Prefixer-Container](https://github.com/thecodingmachine/prefixer-container):
dynamically prefix identifiers
- [Lazy-Container](https://github.com/snapshotpl/lazy-container): lazy services
### Projects using `ContainerInterface`
The list below contains only a sample of all the projects consuming `ContainerInterface`. For a more complete list have a look [here](http://packanalyst.com/class?q=Interop%5CContainer%5CContainerInterface).
| | Downloads |
| --- | --- |
| [Adroit](https://github.com/bitexpert/adroit) | ![](https://img.shields.io/packagist/dt/bitexpert/adroit.svg) |
| [Behat](https://github.com/Behat/Behat/pull/974) | ![](https://img.shields.io/packagist/dt/behat/behat.svg) |
| [blast-facades](https://github.com/phpthinktank/blast-facades): Minimize complexity and represent dependencies as facades. | ![](https://img.shields.io/packagist/dt/blast/facades.svg) |
| [interop.silex.di](https://github.com/thecodingmachine/interop.silex.di): an extension to [Silex](http://silex.sensiolabs.org/) that adds support for any *container-interop* compatible container | ![](https://img.shields.io/packagist/dt/mouf/interop.silex.di.svg) |
| [mindplay/walkway](https://github.com/mindplay-dk/walkway): a modular request router | ![](https://img.shields.io/packagist/dt/mindplay/walkway.svg) |
| [mindplay/middleman](https://github.com/mindplay-dk/middleman): minimalist PSR-7 middleware dispatcher | ![](https://img.shields.io/packagist/dt/mindplay/middleman.svg) |
| [PHP-DI/Invoker](https://github.com/PHP-DI/Invoker): extensible and configurable invoker/dispatcher | ![](https://img.shields.io/packagist/dt/php-di/invoker.svg) |
| [Prophiler](https://github.com/fabfuel/prophiler) | ![](https://img.shields.io/packagist/dt/fabfuel/prophiler.svg) |
| [Silly](https://github.com/mnapoli/silly): CLI micro-framework | ![](https://img.shields.io/packagist/dt/mnapoli/silly.svg) |
| [Slim v3](https://github.com/slimphp/Slim) | ![](https://img.shields.io/packagist/dt/slim/slim.svg) |
| [Splash](http://mouf-php.com/packages/mouf/mvc.splash-common/version/8.0-dev/README.md) | ![](https://img.shields.io/packagist/dt/mouf/mvc.splash-common.svg) |
| [Woohoo Labs. Harmony](https://github.com/woohoolabs/harmony): a flexible micro-framework | ![](https://img.shields.io/packagist/dt/woohoolabs/harmony.svg) |
| [zend-expressive](https://github.com/zendframework/zend-expressive) | ![](https://img.shields.io/packagist/dt/zendframework/zend-expressive.svg) |
## Workflow
Everyone is welcome to join and contribute.
The general workflow looks like this:
1. Someone opens a discussion (GitHub issue) to suggest an interface
1. Feedback is gathered
1. The interface is added to a development branch
1. We release alpha versions so that the interface can be experimented with
1. Discussions and edits ensue until the interface is deemed stable by a general consensus
1. A new minor version of the package is released
We try to not break BC by creating new interfaces instead of editing existing ones.
While we currently work on interfaces, we are open to anything that might help towards interoperability, may that
be code, best practices, etc.

View File

@ -0,0 +1,15 @@
{
"name": "container-interop/container-interop",
"type": "library",
"description": "Promoting the interoperability of container objects (DIC, SL, etc.)",
"homepage": "https://github.com/container-interop/container-interop",
"license": "MIT",
"autoload": {
"psr-4": {
"Interop\\Container\\": "src/Interop/Container/"
}
},
"require": {
"psr/container": "^1.0"
}
}

View File

@ -0,0 +1,114 @@
# ContainerInterface Meta Document
## Introduction
This document describes the process and discussions that lead to the `ContainerInterface`.
Its goal is to explain the reasons behind each decision.
## Goal
The goal set by `ContainerInterface` is to standardize how frameworks and libraries make use of a
container to obtain objects and parameters.
By standardizing such a behavior, frameworks and libraries using the `ContainerInterface`
could work with any compatible container.
That would allow end users to choose their own container based on their own preferences.
It is important to distinguish the two usages of a container:
- configuring entries
- fetching entries
Most of the time, those two sides are not used by the same party.
While it is often end users who tend to configure entries, it is generally the framework that fetch
entries to build the application.
This is why this interface focuses only on how entries can be fetched from a container.
## Interface name
The interface name has been thoroughly discussed and was decided by a vote.
The list of options considered with their respective votes are:
- `ContainerInterface`: +8
- `ProviderInterface`: +2
- `LocatorInterface`: 0
- `ReadableContainerInterface`: -5
- `ServiceLocatorInterface`: -6
- `ObjectFactory`: -6
- `ObjectStore`: -8
- `ConsumerInterface`: -9
[Full results of the vote](https://github.com/container-interop/container-interop/wiki/%231-interface-name:-Vote)
The complete discussion can be read in [the issue #1](https://github.com/container-interop/container-interop/issues/1).
## Interface methods
The choice of which methods the interface would contain was made after a statistical analysis of existing containers.
The results of this analysis are available [in this document](https://gist.github.com/mnapoli/6159681).
The summary of the analysis showed that:
- all containers offer a method to get an entry by its id
- a large majority name such method `get()`
- for all containers, the `get()` method has 1 mandatory parameter of type string
- some containers have an optional additional argument for `get()`, but it doesn't have the same purpose between containers
- a large majority of the containers offer a method to test if it can return an entry by its id
- a majority name such method `has()`
- for all containers offering `has()`, the method has exactly 1 parameter of type string
- a large majority of the containers throw an exception rather than returning null when an entry is not found in `get()`
- a large majority of the containers don't implement `ArrayAccess`
The question of whether to include methods to define entries has been discussed in
[issue #1](https://github.com/container-interop/container-interop/issues/1).
It has been judged that such methods do not belong in the interface described here because it is out of its scope
(see the "Goal" section).
As a result, the `ContainerInterface` contains two methods:
- `get()`, returning anything, with one mandatory string parameter. Should throw an exception if the entry is not found.
- `has()`, returning a boolean, with one mandatory string parameter.
### Number of parameters in `get()` method
While `ContainerInterface` only defines one mandatory parameter in `get()`, it is not incompatible with
existing containers that have additional optional parameters. PHP allows an implementation to offer more parameters
as long as they are optional, because the implementation *does* satisfy the interface.
This issue has been discussed in [issue #6](https://github.com/container-interop/container-interop/issues/6).
### Type of the `$id` parameter
The type of the `$id` parameter in `get()` and `has()` has been discussed in
[issue #6](https://github.com/container-interop/container-interop/issues/6).
While `string` is used in all the containers that were analyzed, it was suggested that allowing
anything (such as objects) could allow containers to offer a more advanced query API.
An example given was to use the container as an object builder. The `$id` parameter would then be an
object that would describe how to create an instance.
The conclusion of the discussion was that this was beyond the scope of getting entries from a container without
knowing how the container provided them, and it was more fit for a factory.
## Contributors
Are listed here all people that contributed in the discussions or votes, by alphabetical order:
- [Amy Stephen](https://github.com/AmyStephen)
- [David Négrier](https://github.com/moufmouf)
- [Don Gilbert](https://github.com/dongilbert)
- [Jason Judge](https://github.com/judgej)
- [Jeremy Lindblom](https://github.com/jeremeamia)
- [Marco Pivetta](https://github.com/Ocramius)
- [Matthieu Napoli](https://github.com/mnapoli)
- [Paul M. Jones](https://github.com/pmjones)
- [Stephan Hochdörfer](https://github.com/shochdoerfer)
- [Taylor Otwell](https://github.com/taylorotwell)
## Relevant links
- [`ContainerInterface.php`](https://github.com/container-interop/container-interop/blob/master/src/Interop/Container/ContainerInterface.php)
- [List of all issues](https://github.com/container-interop/container-interop/issues?labels=ContainerInterface&milestone=&page=1&state=closed)
- [Vote for the interface name](https://github.com/container-interop/container-interop/wiki/%231-interface-name:-Vote)

View File

@ -0,0 +1,158 @@
Container interface
===================
This document describes a common interface for dependency injection containers.
The goal set by `ContainerInterface` is to standardize how frameworks and libraries make use of a
container to obtain objects and parameters (called *entries* in the rest of this document).
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD",
"SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be
interpreted as described in [RFC 2119][].
The word `implementor` in this document is to be interpreted as someone
implementing the `ContainerInterface` in a dependency injection-related library or framework.
Users of dependency injections containers (DIC) are referred to as `user`.
[RFC 2119]: http://tools.ietf.org/html/rfc2119
1. Specification
-----------------
### 1.1 Basics
- The `Interop\Container\ContainerInterface` exposes two methods : `get` and `has`.
- `get` takes one mandatory parameter: an entry identifier. It MUST be a string.
A call to `get` can return anything (a *mixed* value), or throws an exception if the identifier
is not known to the container. Two successive calls to `get` with the same
identifier SHOULD return the same value. However, depending on the `implementor`
design and/or `user` configuration, different values might be returned, so
`user` SHOULD NOT rely on getting the same value on 2 successive calls.
While `ContainerInterface` only defines one mandatory parameter in `get()`, implementations
MAY accept additional optional parameters.
- `has` takes one unique parameter: an entry identifier. It MUST return `true`
if an entry identifier is known to the container and `false` if it is not.
`has($id)` returning true does not mean that `get($id)` will not throw an exception.
It does however mean that `get($id)` will not throw a `NotFoundException`.
### 1.2 Exceptions
Exceptions directly thrown by the container MUST implement the
[`Interop\Container\Exception\ContainerException`](../src/Interop/Container/Exception/ContainerException.php).
A call to the `get` method with a non-existing id SHOULD throw a
[`Interop\Container\Exception\NotFoundException`](../src/Interop/Container/Exception/NotFoundException.php).
### 1.3 Additional features
This section describes additional features that MAY be added to a container. Containers are not
required to implement these features to respect the ContainerInterface.
#### 1.3.1 Delegate lookup feature
The goal of the *delegate lookup* feature is to allow several containers to share entries.
Containers implementing this feature can perform dependency lookups in other containers.
Containers implementing this feature will offer a greater lever of interoperability
with other containers. Implementation of this feature is therefore RECOMMENDED.
A container implementing this feature:
- MUST implement the `ContainerInterface`
- MUST provide a way to register a delegate container (using a constructor parameter, or a setter,
or any possible way). The delegate container MUST implement the `ContainerInterface`.
When a container is configured to use a delegate container for dependencies:
- Calls to the `get` method should only return an entry if the entry is part of the container.
If the entry is not part of the container, an exception should be thrown
(as requested by the `ContainerInterface`).
- Calls to the `has` method should only return `true` if the entry is part of the container.
If the entry is not part of the container, `false` should be returned.
- If the fetched entry has dependencies, **instead** of performing
the dependency lookup in the container, the lookup is performed on the *delegate container*.
Important! By default, the lookup SHOULD be performed on the delegate container **only**, not on the container itself.
It is however allowed for containers to provide exception cases for special entries, and a way to lookup
into the same container (or another container) instead of the delegate container.
2. Package
----------
The interfaces and classes described as well as relevant exception are provided as part of the
[container-interop/container-interop](https://packagist.org/packages/container-interop/container-interop) package.
3. `Interop\Container\ContainerInterface`
-----------------------------------------
```php
<?php
namespace Interop\Container;
use Interop\Container\Exception\ContainerException;
use Interop\Container\Exception\NotFoundException;
/**
* Describes the interface of a container that exposes methods to read its entries.
*/
interface ContainerInterface
{
/**
* Finds an entry of the container by its identifier and returns it.
*
* @param string $id Identifier of the entry to look for.
*
* @throws NotFoundException No entry was found for this identifier.
* @throws ContainerException Error while retrieving the entry.
*
* @return mixed Entry.
*/
public function get($id);
/**
* Returns true if the container can return an entry for the given identifier.
* Returns false otherwise.
*
* `has($id)` returning true does not mean that `get($id)` will not throw an exception.
* It does however mean that `get($id)` will not throw a `NotFoundException`.
*
* @param string $id Identifier of the entry to look for.
*
* @return boolean
*/
public function has($id);
}
```
4. `Interop\Container\Exception\ContainerException`
---------------------------------------------------
```php
<?php
namespace Interop\Container\Exception;
/**
* Base interface representing a generic exception in a container.
*/
interface ContainerException
{
}
```
5. `Interop\Container\Exception\NotFoundException`
---------------------------------------------------
```php
<?php
namespace Interop\Container\Exception;
/**
* No entry was found in the container.
*/
interface NotFoundException extends ContainerException
{
}
```

View File

@ -0,0 +1,259 @@
Delegate lookup feature Meta Document
=====================================
1. Summary
----------
This document describes the *delegate lookup feature*.
Containers are not required to implement this feature to respect the `ContainerInterface`.
However, containers implementing this feature will offer a greater lever of interoperability
with other containers, allowing multiple containers to share entries in the same application.
Implementation of this feature is therefore recommanded.
2. Why Bother?
--------------
The [`ContainerInterface`](../src/Interop/Container/ContainerInterface.php) ([meta doc](ContainerInterface.md))
standardizes how frameworks and libraries make use of a container to obtain objects and parameters.
By standardizing such a behavior, frameworks and libraries relying on the `ContainerInterface`
could work with any compatible container.
That would allow end users to choose their own container based on their own preferences.
The `ContainerInterface` is also enough if we want to have several containers side-by-side in the same
application. For instance, this is what the [CompositeContainer](https://github.com/jeremeamia/acclimate-container/blob/master/src/CompositeContainer.php)
class of [Acclimate](https://github.com/jeremeamia/acclimate-container) is designed for:
![Side by side containers](images/side_by_side_containers.png)
However, an instance in container 1 cannot reference an instance in container 2.
It would be better if an instance of container 1 could reference an instance in container 2,
and the opposite should be true.
![Interoperating containers](images/interoperating_containers.png)
In the sample above, entry 1 in container 1 is referencing entry 3 in container 2.
3. Scope
--------
### 3.1 Goals
The goal of the *delegate lookup* feature is to allow several containers to share entries.
4. Approaches
-------------
### 4.1 Chosen Approach
Containers implementing this feature can perform dependency lookups in other containers.
A container implementing this feature:
- must implement the `ContainerInterface`
- must provide a way to register a *delegate container* (using a constructor parameter, or a setter, or any
possible way). The *delegate container* must implement the `ContainerInterface`.
When a *delegate container* is configured on a container:
- Calls to the `get` method should only return an entry if the entry is part of the container.
If the entry is not part of the container, an exception should be thrown (as required in the `ContainerInterface`).
- Calls to the `has` method should only return *true* if the entry is part of the container.
If the entry is not part of the container, *false* should be returned.
- Finally, the important part: if the entry we are fetching has dependencies,
**instead** of perfoming the dependency lookup in the container, the lookup is performed on the *delegate container*.
Important! By default, the lookup should be performed on the delegate container **only**, not on the container itself.
It is however allowed for containers to provide exception cases for special entries, and a way to lookup into
the same container (or another container) instead of the delegate container.
### 4.2 Typical usage
The *delegate container* will usually be a composite container. A composite container is a container that
contains several other containers. When performing a lookup on a composite container, the inner containers are
queried until one container returns an entry.
An inner container implementing the *delegate lookup feature* will return entries it contains, but if these
entries have dependencies, the dependencies lookup calls will be performed on the composite container, giving
a chance to all containers to answer.
Interestingly enough, the order in which containers are added in the composite container matters. Indeed,
the first containers to be added in the composite container can "override" the entries of containers with
lower priority.
![Containers priority](images/priority.png)
In the example above, "container 2" contains a controller "myController" and the controller is referencing an
"entityManager" entry. "Container 1" contains also an entry named "entityManager".
Without the *delegate lookup* feature, when requesting the "myController" instance to container 2, it would take
in charge the instanciation of both entries.
However, using the *delegate lookup* feature, here is what happens when we ask the composite container for the
"myController" instance:
- The composite container asks container 1 if if contains the "myController" instance. The answer is no.
- The composite container asks container 2 if if contains the "myController" instance. The answer is yes.
- The composite container performs a `get` call on container 2 for the "myController" instance.
- Container 2 sees that "myController" has a dependency on "entityManager".
- Container 2 delegates the lookup of "entityManager" to the composite container.
- The composite container asks container 1 if if contains the "entityManager" instance. The answer is yes.
- The composite container performs a `get` call on container 1 for the "entityManager" instance.
In the end, we get a controller instanciated by container 2 that references an entityManager instanciated
by container 1.
### 4.3 Alternative: the fallback strategy
The first proposed approach we tried was to perform all the lookups in the "local" container,
and if a lookup fails in the container, to use the delegate container. In this scenario, the
delegate container is used in "fallback" mode.
This strategy has been described in @moufmouf blog post: http://mouf-php.com/container-interop-whats-next (solution 1).
It was also discussed [here](https://github.com/container-interop/container-interop/pull/8#issuecomment-33570697) and
[here](https://github.com/container-interop/container-interop/pull/20#issuecomment-56599631).
Problems with this strategy:
- Heavy problem regarding infinite loops
- Unable to overload a container entry with the delegate container entry
### 4.4 Alternative: force implementing an interface
The first proposed approach was to develop a `ParentAwareContainerInterface` interface.
It was proposed here: https://github.com/container-interop/container-interop/pull/8
The interface would have had the behaviour of the delegate lookup feature but would have forced the addition of
a `setParentContainter` method:
```php
interface ParentAwareContainerInterface extends ReadableContainerInterface {
/**
* Sets the parent container associated to that container. This container will call
* the parent container to fetch dependencies.
*
* @param ContainerInterface $container
*/
public function setParentContainer(ContainerInterface $container);
}
```
The interface idea was first questioned by @Ocramius [here](https://github.com/container-interop/container-interop/pull/8#issuecomment-51721777).
@Ocramius expressed the idea that an interface should not contain setters, otherwise, it is forcing implementation
details on the class implementing the interface.
Then @mnapoli made a proposal for a "convention" [here](https://github.com/container-interop/container-interop/pull/8#issuecomment-51841079),
this idea was further discussed until all participants in the discussion agreed to remove the interface idea
and replace it with a "standard" feature.
**Pros:**
If we had had an interface, we could have delegated the registration of the delegate/composite container to the
the delegate/composite container itself.
For instance:
```php
$containerA = new ContainerA();
$containerB = new ContainerB();
$compositeContainer = new CompositeContainer([$containerA, $containerB]);
// The call to 'setParentContainer' is delegated to the CompositeContainer
// It is not the responsibility of the user anymore.
class CompositeContainer {
...
public function __construct($containers) {
foreach ($containers as $container) {
if ($container instanceof ParentAwareContainerInterface) {
$container->setParentContainer($this);
}
}
...
}
}
```
**Cons:**
Cons have been extensively discussed [here](https://github.com/container-interop/container-interop/pull/8#issuecomment-51721777).
Basically, forcing a setter into an interface is a bad idea. Setters are similar to constructor arguments,
and it's a bad idea to standardize a constructor: how the delegate container is configured into a container is an implementation detail. This outweights the benefits of the interface.
### 4.4 Alternative: no exception case for delegate lookups
Originally, the proposed wording for delegate lookup calls was:
> Important! The lookup MUST be performed on the delegate container **only**, not on the container itself.
This was later replaced by:
> Important! By default, the lookup SHOULD be performed on the delegate container **only**, not on the container itself.
>
> It is however allowed for containers to provide exception cases for special entries, and a way to lookup
> into the same container (or another container) instead of the delegate container.
Exception cases have been allowed to avoid breaking dependencies with some services that must be provided
by the container (on @njasm proposal). This was proposed here: https://github.com/container-interop/container-interop/pull/20#issuecomment-56597235
### 4.5 Alternative: having one of the containers act as the composite container
In real-life scenarios, we usually have a big framework (Symfony 2, Zend Framework 2, etc...) and we want to
add another DI container to this container. Most of the time, the "big" framework will be responsible for
creating the controller's instances, using it's own DI container. Until *container-interop* is fully adopted,
the "big" framework will not be aware of the existence of a composite container that it should use instead
of its own container.
For this real-life use cases, @mnapoli and @moufmouf proposed to extend the "big" framework's DI container
to make it act as a composite container.
This has been discussed [here](https://github.com/container-interop/container-interop/pull/8#issuecomment-40367194)
and [here](http://mouf-php.com/container-interop-whats-next#solution4).
This was implemented in Symfony 2 using:
- [interop.symfony.di](https://github.com/thecodingmachine/interop.symfony.di/tree/v0.1.0)
- [framework interop](https://github.com/mnapoli/framework-interop/)
This was implemented in Silex using:
- [interop.silex.di](https://github.com/thecodingmachine/interop.silex.di)
Having a container act as the composite container is not part of the delegate lookup standard because it is
simply a temporary design pattern used to make existing frameworks that do not support yet ContainerInterop
play nice with other DI containers.
5. Implementations
------------------
The following projects already implement the delegate lookup feature:
- [Mouf](http://mouf-php.com), through the [`setDelegateLookupContainer` method](https://github.com/thecodingmachine/mouf/blob/2.0/src/Mouf/MoufManager.php#L2120)
- [PHP-DI](http://php-di.org/), through the [`$wrapperContainer` parameter of the constructor](https://github.com/mnapoli/PHP-DI/blob/master/src/DI/Container.php#L72)
- [pimple-interop](https://github.com/moufmouf/pimple-interop), through the [`$container` parameter of the constructor](https://github.com/moufmouf/pimple-interop/blob/master/src/Interop/Container/Pimple/PimpleInterop.php#L62)
6. People
---------
Are listed here all people that contributed in the discussions, by alphabetical order:
- [Alexandru Pătrănescu](https://github.com/drealecs)
- [Ben Peachey](https://github.com/potherca)
- [David Négrier](https://github.com/moufmouf)
- [Jeremy Lindblom](https://github.com/jeremeamia)
- [Marco Pivetta](https://github.com/Ocramius)
- [Matthieu Napoli](https://github.com/mnapoli)
- [Nelson J Morais](https://github.com/njasm)
- [Phil Sturgeon](https://github.com/philsturgeon)
- [Stephan Hochdörfer](https://github.com/shochdoerfer)
7. Relevant Links
-----------------
_**Note:** Order descending chronologically._
- [Pull request on the delegate lookup feature](https://github.com/container-interop/container-interop/pull/20)
- [Pull request on the interface idea](https://github.com/container-interop/container-interop/pull/8)
- [Original article exposing the delegate lookup idea along many others](http://mouf-php.com/container-interop-whats-next)

View File

@ -0,0 +1,60 @@
Delegate lookup feature
=======================
This document describes a standard for dependency injection containers.
The goal set by the *delegate lookup* feature is to allow several containers to share entries.
Containers implementing this feature can perform dependency lookups in other containers.
Containers implementing this feature will offer a greater lever of interoperability
with other containers. Implementation of this feature is therefore RECOMMENDED.
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD",
"SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be
interpreted as described in [RFC 2119][].
The word `implementor` in this document is to be interpreted as someone
implementing the delegate lookup feature in a dependency injection-related library or framework.
Users of dependency injections containers (DIC) are referred to as `user`.
[RFC 2119]: http://tools.ietf.org/html/rfc2119
1. Vocabulary
-------------
In a dependency injection container, the container is used to fetch entries.
Entries can have dependencies on other entries. Usually, these other entries are fetched by the container.
The *delegate lookup* feature is the ability for a container to fetch dependencies in
another container. In the rest of the document, the word "container" will reference the container
implemented by the implementor. The word "delegate container" will reference the container we are
fetching the dependencies from.
2. Specification
----------------
A container implementing the *delegate lookup* feature:
- MUST implement the [`ContainerInterface`](ContainerInterface.md)
- MUST provide a way to register a delegate container (using a constructor parameter, or a setter,
or any possible way). The delegate container MUST implement the [`ContainerInterface`](ContainerInterface.md).
When a container is configured to use a delegate container for dependencies:
- Calls to the `get` method should only return an entry if the entry is part of the container.
If the entry is not part of the container, an exception should be thrown
(as requested by the [`ContainerInterface`](ContainerInterface.md)).
- Calls to the `has` method should only return `true` if the entry is part of the container.
If the entry is not part of the container, `false` should be returned.
- If the fetched entry has dependencies, **instead** of performing
the dependency lookup in the container, the lookup is performed on the *delegate container*.
Important: By default, the dependency lookups SHOULD be performed on the delegate container **only**, not on the container itself.
It is however allowed for containers to provide exception cases for special entries, and a way to lookup
into the same container (or another container) instead of the delegate container.
3. Package / Interface
----------------------
This feature is not tied to any code, interface or package.

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Some files were not shown because too many files have changed in this diff Show More