scratch

Subversion Repositories:
Compare Path: Rev
With Path: Rev
?path1? @ 114  →  ?path2? @ 115
/vendor/alchemy/binary-driver/.gitignore
@@ -0,0 +1,2 @@
/vendor/
composer.lock
/vendor/alchemy/binary-driver/.travis.yml
@@ -0,0 +1,16 @@
language: php
 
sudo: false
 
matrix:
fast_finish: true
 
before_script:
- composer self-update
- composer install --prefer-source
 
php:
- 5.5
- 5.6
- 7.0
- hhvm
/vendor/alchemy/binary-driver/CHANGELOG.md
@@ -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.
/vendor/alchemy/binary-driver/LICENSE
@@ -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.
/vendor/alchemy/binary-driver/README.md
@@ -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.
/vendor/alchemy/binary-driver/composer.json
@@ -0,0 +1,38 @@
{
"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/"
}
],
"require": {
"php" : ">=5.5",
"evenement/evenement" : "^2.0|^1.0",
"monolog/monolog" : "^1.3",
"psr/log" : "^1.0",
"symfony/process" : "^2.0|^3.0"
},
"require-dev": {
"phpunit/phpunit" : "^4.0|^5.0"
},
"autoload": {
"psr-0": {
"Alchemy": "src"
}
}
}
/vendor/alchemy/binary-driver/phpunit.xml.dist
@@ -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>
 
/vendor/alchemy/binary-driver/src/Alchemy/BinaryDriver/AbstractBinary.php
@@ -0,0 +1,220 @@
<?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 Monolog\Logger;
use Monolog\Handler\NullHandler;
use Psr\Log\LoggerInterface;
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 Logger(__NAMESPACE__ . ' logger');
$logger->pushHandler(new NullHandler());
}
 
$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;
}
}
/vendor/alchemy/binary-driver/src/Alchemy/BinaryDriver/BinaryDriverTestCase.php
@@ -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');
}
}
/vendor/alchemy/binary-driver/src/Alchemy/BinaryDriver/BinaryInterface.php
@@ -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());
}
/vendor/alchemy/binary-driver/src/Alchemy/BinaryDriver/Configuration.php
@@ -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);
}
}
/vendor/alchemy/binary-driver/src/Alchemy/BinaryDriver/ConfigurationAwareInterface.php
@@ -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);
}
/vendor/alchemy/binary-driver/src/Alchemy/BinaryDriver/ConfigurationInterface.php
@@ -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();
}
/vendor/alchemy/binary-driver/src/Alchemy/BinaryDriver/Exception/ExceptionInterface.php
@@ -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
{
}
/vendor/alchemy/binary-driver/src/Alchemy/BinaryDriver/Exception/ExecutableNotFoundException.php
@@ -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
{
}
/vendor/alchemy/binary-driver/src/Alchemy/BinaryDriver/Exception/ExecutionFailureException.php
@@ -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 ExecutionFailureException extends \RuntimeException implements ExceptionInterface
{
}
/vendor/alchemy/binary-driver/src/Alchemy/BinaryDriver/Exception/InvalidArgumentException.php
@@ -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
{
}
/vendor/alchemy/binary-driver/src/Alchemy/BinaryDriver/Listeners/DebugListener.php
@@ -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));
}
}
}
/vendor/alchemy/binary-driver/src/Alchemy/BinaryDriver/Listeners/ListenerInterface.php
@@ -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();
}
/vendor/alchemy/binary-driver/src/Alchemy/BinaryDriver/Listeners/Listeners.php
@@ -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());
};
}
}
/vendor/alchemy/binary-driver/src/Alchemy/BinaryDriver/ProcessBuilderFactory.php
@@ -0,0 +1,177 @@
<?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\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);
 
return ProcessBuilder::create($arguments)
->setTimeout($this->timeout)
->getProcess();
} 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;
}
}
/vendor/alchemy/binary-driver/src/Alchemy/BinaryDriver/ProcessBuilderFactoryAwareInterface.php
@@ -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);
}
/vendor/alchemy/binary-driver/src/Alchemy/BinaryDriver/ProcessBuilderFactoryInterface.php
@@ -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();
}
/vendor/alchemy/binary-driver/src/Alchemy/BinaryDriver/ProcessRunner.php
@@ -0,0 +1,104 @@
<?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(), $e);
}
}
 
if (!$bypassErrors && !$process->isSuccessful()) {
$this->doExecutionFailure($process->getCommandLine());
} elseif (!$process->isSuccessful()) {
$this->logger->error(sprintf(
'%s failed to execute command %s', $this->name, $process->getCommandLine()
));
 
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, \Exception $e = null)
{
$this->logger->error(sprintf(
'%s failed to execute command %s', $this->name, $command
));
throw new ExecutionFailureException(sprintf(
'%s failed to execute command %s', $this->name, $command
), $e ? $e->getCode() : null, $e ?: null);
}
}
/vendor/alchemy/binary-driver/src/Alchemy/BinaryDriver/ProcessRunnerAwareInterface.php
@@ -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);
}
/vendor/alchemy/binary-driver/src/Alchemy/BinaryDriver/ProcessRunnerInterface.php
@@ -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);
}
/vendor/alchemy/binary-driver/tests/Alchemy/Tests/BinaryDriver/AbstractBinaryTest.php
@@ -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';
}
}
/vendor/alchemy/binary-driver/tests/Alchemy/Tests/BinaryDriver/AbstractProcessBuilderFactoryTest.php
@@ -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());
}
}
/vendor/alchemy/binary-driver/tests/Alchemy/Tests/BinaryDriver/ConfigurationTest.php
@@ -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());
}
 
}
/vendor/alchemy/binary-driver/tests/Alchemy/Tests/BinaryDriver/LTSProcessBuilderFactoryTest.php
@@ -0,0 +1,66 @@
<?php
 
namespace Alchemy\Tests\BinaryDriver;
 
use Symfony\Component\Process\ProcessBuilder;
use Alchemy\BinaryDriver\ProcessBuilderFactory;
use Symfony\Component\Process\Process;
 
class LTSProcessBuilderFactoryTest extends AbstractProcessBuilderFactoryTest
{
protected function getProcessBuilderFactory($binary)
{
$factory = new ProcessBuilderFactory($binary);
$factory->setBuilder(new LTSProcessBuilder());
ProcessBuilderFactory::$emulateSfLTS = false;
$factory->useBinary($binary);
 
return $factory;
}
}
 
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);
}
}
/vendor/alchemy/binary-driver/tests/Alchemy/Tests/BinaryDriver/Listeners/DebugListenerTest.php
@@ -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);
}
}
/vendor/alchemy/binary-driver/tests/Alchemy/Tests/BinaryDriver/Listeners/ListenersTest.php
@@ -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');
}
}
/vendor/alchemy/binary-driver/tests/Alchemy/Tests/BinaryDriver/NONLTSProcessBuilderFactoryTest.php
@@ -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);
}
}
/vendor/alchemy/binary-driver/tests/Alchemy/Tests/BinaryDriver/ProcessRunnerTest.php
@@ -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();
}
}
/vendor/alchemy/binary-driver/tests/bootstrap.php
@@ -0,0 +1,4 @@
<?php
 
$loader = require __DIR__.'/../vendor/autoload.php';
$loader->add('Alchemy\Tests', __DIR__);