parent
68053abcb9
commit
08130773c4
@ -0,0 +1,5 @@ |
||||
{ |
||||
"require": { |
||||
"php-ffmpeg/php-ffmpeg": "0.3.x-dev@dev" |
||||
} |
||||
} |
||||
@ -0,0 +1,11 @@ |
||||
language: php |
||||
|
||||
before_script: |
||||
- composer self-update |
||||
- composer install --dev --prefer-source |
||||
|
||||
php: |
||||
- 5.3.3 |
||||
- 5.3 |
||||
- 5.4 |
||||
- 5.5 |
||||
@ -0,0 +1,61 @@ |
||||
CHANGELOG |
||||
--------- |
||||
|
||||
* 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. |
||||
@ -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. |
||||
@ -0,0 +1,190 @@ |
||||
# Binary Driver |
||||
|
||||
Binary-Driver is a set of PHP tools to build binary drivers. |
||||
|
||||
[](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. |
||||
@ -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.3.3", |
||||
"evenement/evenement" : "~1.0", |
||||
"monolog/monolog" : "~1.3", |
||||
"psr/log" : "~1.0", |
||||
"symfony/process" : "~2.0" |
||||
}, |
||||
"require-dev": { |
||||
"phpunit/phpunit" : "~3.7" |
||||
}, |
||||
"autoload": { |
||||
"psr-0": { |
||||
"Alchemy": "src" |
||||
} |
||||
} |
||||
} |
||||
@ -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> |
||||
|
||||
@ -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; |
||||
} |
||||
} |
||||
@ -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'); |
||||
} |
||||
} |
||||
@ -0,0 +1,66 @@ |
||||
<?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\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()); |
||||
} |
||||
@ -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); |
||||
} |
||||
} |
||||
@ -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); |
||||
} |
||||
@ -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(); |
||||
} |
||||
@ -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 |
||||
{ |
||||
} |
||||
@ -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 |
||||
{ |
||||
} |
||||
@ -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 |
||||
{ |
||||
} |
||||
@ -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 |
||||
{ |
||||
} |
||||
@ -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)); |
||||
} |
||||
} |
||||
} |
||||
@ -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(); |
||||
} |
||||
@ -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()); |
||||
}; |
||||
} |
||||
} |
||||
@ -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; |
||||
} |
||||
} |
||||
@ -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); |
||||
} |
||||
@ -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(); |
||||
} |
||||
@ -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); |
||||
} |
||||
} |
||||
@ -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); |
||||
} |
||||
@ -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); |
||||
} |
||||
@ -0,0 +1,297 @@ |
||||
<?php |
||||
|
||||
namespace Alchemy\Tests\BinaryDriver; |
||||
|
||||
use Alchemy\BinaryDriver\AbstractBinary; |
||||
use Alchemy\BinaryDriver\BinaryDriverTestCase; |
||||
use Alchemy\BinaryDriver\Configuration; |
||||
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()); |
||||
} |
||||
|
||||
/** |
||||
* @expectedException Alchemy\BinaryDriver\Exception\ExecutableNotFoundException |
||||
*/ |
||||
public function testLoadWithMultiplePathExpectingAFailure() |
||||
{ |
||||
Implementation::load(array('bachibouzouk', 'moribon')); |
||||
} |
||||
|
||||
/** |
||||
* @expectedException Alchemy\BinaryDriver\Exception\ExecutableNotFoundException |
||||
*/ |
||||
public function testLoadWithUniquePathExpectingAFailure() |
||||
{ |
||||
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() |
||||
{ |
||||
$listener = $this->getMock('Alchemy\BinaryDriver\Listeners\ListenerInterface'); |
||||
$listener->expects($this->any()) |
||||
->method('forwardedEvents') |
||||
->will($this->returnValue(array())); |
||||
|
||||
$listener2 = $this->getMock('Alchemy\BinaryDriver\Listeners\ListenerInterface'); |
||||
$listener2->expects($this->any()) |
||||
->method('forwardedEvents') |
||||
->will($this->returnValue(array())); |
||||
|
||||
return array( |
||||
array('-a', false, array('-a'), 'loubda', 2, array($listener, $listener2)), |
||||
array('-a', false, array('-a'), 'loubda', 1, array($listener)), |
||||
array('-a', false, array('-a'), 'loubda', 1, $listener), |
||||
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); |
||||
} |
||||
} |
||||
|
||||
class Implementation extends AbstractBinary |
||||
{ |
||||
public function getName() |
||||
{ |
||||
return 'Implementation'; |
||||
} |
||||
} |
||||
@ -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()); |
||||
} |
||||
} |
||||
@ -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()); |
||||
} |
||||
|
||||
} |
||||
@ -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); |
||||
} |
||||
} |
||||
@ -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); |
||||
} |
||||
} |
||||
@ -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 TestListener(); |
||||
|
||||
$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 TestListener(); |
||||
$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 TestListener extends EventEmitter implements ListenerInterface |
||||
{ |
||||
public function handle($type, $data) |
||||
{ |
||||
$this->emit('received', array($type, $data)); |
||||
} |
||||
|
||||
public function forwardedEvents() |
||||
{ |
||||
return array('received'); |
||||
} |
||||
} |
||||
@ -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); |
||||
} |
||||
} |
||||
@ -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(); |
||||
} |
||||
} |
||||
@ -0,0 +1,4 @@ |
||||
<?php |
||||
|
||||
$loader = require __DIR__.'/../vendor/autoload.php'; |
||||
$loader->add('Alchemy\Tests', __DIR__); |
||||
@ -0,0 +1,7 @@ |
||||
<?php |
||||
|
||||
// autoload.php generated by Composer |
||||
|
||||
require_once __DIR__ . '/composer' . '/autoload_real.php'; |
||||
|
||||
return ComposerAutoloaderInit75f7ea26a11f6be21d13acba7a2d0249::getLoader(); |
||||
@ -0,0 +1,246 @@ |
||||
<?php |
||||
|
||||
/* |
||||
* This file is part of Composer. |
||||
* |
||||
* (c) Nils Adermann <naderman@naderman.de> |
||||
* Jordi Boggiano <j.boggiano@seld.be> |
||||
* |
||||
* For the full copyright and license information, please view the LICENSE |
||||
* file that was distributed with this source code. |
||||
*/ |
||||
|
||||
namespace Composer\Autoload; |
||||
|
||||
/** |
||||
* ClassLoader implements a PSR-0 class loader |
||||
* |
||||
* See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md |
||||
* |
||||
* $loader = new \Composer\Autoload\ClassLoader(); |
||||
* |
||||
* // register classes with namespaces |
||||
* $loader->add('Symfony\Component', __DIR__.'/component'); |
||||
* $loader->add('Symfony', __DIR__.'/framework'); |
||||
* |
||||
* // activate the autoloader |
||||
* $loader->register(); |
||||
* |
||||
* // to enable searching the include path (eg. for PEAR packages) |
||||
* $loader->setUseIncludePath(true); |
||||
* |
||||
* In this example, if you try to use a class in the Symfony\Component |
||||
* namespace or one of its children (Symfony\Component\Console for instance), |
||||
* the autoloader will first look for the class under the component/ |
||||
* directory, and it will then fallback to the framework/ directory if not |
||||
* found before giving up. |
||||
* |
||||
* This class is loosely based on the Symfony UniversalClassLoader. |
||||
* |
||||
* @author Fabien Potencier <fabien@symfony.com> |
||||
* @author Jordi Boggiano <j.boggiano@seld.be> |
||||
*/ |
||||
class ClassLoader |
||||
{ |
||||
private $prefixes = array(); |
||||
private $fallbackDirs = array(); |
||||
private $useIncludePath = false; |
||||
private $classMap = array(); |
||||
|
||||
public function getPrefixes() |
||||
{ |
||||
return call_user_func_array('array_merge', $this->prefixes); |
||||
} |
||||
|
||||
public function getFallbackDirs() |
||||
{ |
||||
return $this->fallbackDirs; |
||||
} |
||||
|
||||
public function getClassMap() |
||||
{ |
||||
return $this->classMap; |
||||
} |
||||
|
||||
/** |
||||
* @param array $classMap Class to filename map |
||||
*/ |
||||
public function addClassMap(array $classMap) |
||||
{ |
||||
if ($this->classMap) { |
||||
$this->classMap = array_merge($this->classMap, $classMap); |
||||
} else { |
||||
$this->classMap = $classMap; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Registers a set of classes, merging with any others previously set. |
||||
* |
||||
* @param string $prefix The classes prefix |
||||
* @param array|string $paths The location(s) of the classes |
||||
* @param bool $prepend Prepend the location(s) |
||||
*/ |
||||
public function add($prefix, $paths, $prepend = false) |
||||
{ |
||||
if (!$prefix) { |
||||
if ($prepend) { |
||||
$this->fallbackDirs = array_merge( |
||||
(array) $paths, |
||||
$this->fallbackDirs |
||||
); |
||||
} else { |
||||
$this->fallbackDirs = array_merge( |
||||
$this->fallbackDirs, |
||||
(array) $paths |
||||
); |
||||
} |
||||
|
||||
return; |
||||
} |
||||
|
||||
$first = $prefix[0]; |
||||
if (!isset($this->prefixes[$first][$prefix])) { |
||||
$this->prefixes[$first][$prefix] = (array) $paths; |
||||
|
||||
return; |
||||
} |
||||
if ($prepend) { |
||||
$this->prefixes[$first][$prefix] = array_merge( |
||||
(array) $paths, |
||||
$this->prefixes[$first][$prefix] |
||||
); |
||||
} else { |
||||
$this->prefixes[$first][$prefix] = array_merge( |
||||
$this->prefixes[$first][$prefix], |
||||
(array) $paths |
||||
); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Registers a set of classes, replacing any others previously set. |
||||
* |
||||
* @param string $prefix The classes prefix |
||||
* @param array|string $paths The location(s) of the classes |
||||
*/ |
||||
public function set($prefix, $paths) |
||||
{ |
||||
if (!$prefix) { |
||||
$this->fallbackDirs = (array) $paths; |
||||
|
||||
return; |
||||
} |
||||
$this->prefixes[substr($prefix, 0, 1)][$prefix] = (array) $paths; |
||||
} |
||||
|
||||
/** |
||||
* Turns on searching the include path for class files. |
||||
* |
||||
* @param bool $useIncludePath |
||||
*/ |
||||
public function setUseIncludePath($useIncludePath) |
||||
{ |
||||
$this->useIncludePath = $useIncludePath; |
||||
} |
||||
|
||||
/** |
||||
* Can be used to check if the autoloader uses the include path to check |
||||
* for classes. |
||||
* |
||||
* @return bool |
||||
*/ |
||||
public function getUseIncludePath() |
||||
{ |
||||
return $this->useIncludePath; |
||||
} |
||||
|
||||
/** |
||||
* Registers this instance as an autoloader. |
||||
* |
||||
* @param bool $prepend Whether to prepend the autoloader or not |
||||
*/ |
||||
public function register($prepend = false) |
||||
{ |
||||
spl_autoload_register(array($this, 'loadClass'), true, $prepend); |
||||
} |
||||
|
||||
/** |
||||
* Unregisters this instance as an autoloader. |
||||
*/ |
||||
public function unregister() |
||||
{ |
||||
spl_autoload_unregister(array($this, 'loadClass')); |
||||
} |
||||
|
||||
/** |
||||
* Loads the given class or interface. |
||||
* |
||||
* @param string $class The name of the class |
||||
* @return bool|null True if loaded, null otherwise |
||||
*/ |
||||
public function loadClass($class) |
||||
{ |
||||
if ($file = $this->findFile($class)) { |
||||
include $file; |
||||
|
||||
return true; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Finds the path to the file where the class is defined. |
||||
* |
||||
* @param string $class The name of the class |
||||
* |
||||
* @return string|false The path if found, false otherwise |
||||
*/ |
||||
public function findFile($class) |
||||
{ |
||||
// work around for PHP 5.3.0 - 5.3.2 https://bugs.php.net/50731 |
||||
if ('\\' == $class[0]) { |
||||
$class = substr($class, 1); |
||||
} |
||||
|
||||
if (isset($this->classMap[$class])) { |
||||
return $this->classMap[$class]; |
||||
} |
||||
|
||||
if (false !== $pos = strrpos($class, '\\')) { |
||||
// namespaced class name |
||||
$classPath = strtr(substr($class, 0, $pos), '\\', DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR; |
||||
$className = substr($class, $pos + 1); |
||||
} else { |
||||
// PEAR-like class name |
||||
$classPath = null; |
||||
$className = $class; |
||||
} |
||||
|
||||
$classPath .= strtr($className, '_', DIRECTORY_SEPARATOR) . '.php'; |
||||
|
||||
$first = $class[0]; |
||||
if (isset($this->prefixes[$first])) { |
||||
foreach ($this->prefixes[$first] as $prefix => $dirs) { |
||||
if (0 === strpos($class, $prefix)) { |
||||
foreach ($dirs as $dir) { |
||||
if (file_exists($dir . DIRECTORY_SEPARATOR . $classPath)) { |
||||
return $dir . DIRECTORY_SEPARATOR . $classPath; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
foreach ($this->fallbackDirs as $dir) { |
||||
if (file_exists($dir . DIRECTORY_SEPARATOR . $classPath)) { |
||||
return $dir . DIRECTORY_SEPARATOR . $classPath; |
||||
} |
||||
} |
||||
|
||||
if ($this->useIncludePath && $file = stream_resolve_include_path($classPath)) { |
||||
return $file; |
||||
} |
||||
|
||||
return $this->classMap[$class] = false; |
||||
} |
||||
} |
||||
@ -0,0 +1,9 @@ |
||||
<?php |
||||
|
||||
// autoload_classmap.php generated by Composer |
||||
|
||||
$vendorDir = dirname(dirname(__FILE__)); |
||||
$baseDir = dirname($vendorDir); |
||||
|
||||
return array( |
||||
); |
||||
@ -0,0 +1,16 @@ |
||||
<?php |
||||
|
||||
// autoload_namespaces.php generated by Composer |
||||
|
||||
$vendorDir = dirname(dirname(__FILE__)); |
||||
$baseDir = dirname($vendorDir); |
||||
|
||||
return array( |
||||
'Symfony\\Component\\Process\\' => array($vendorDir . '/symfony/process'), |
||||
'Psr\\Log\\' => array($vendorDir . '/psr/log'), |
||||
'Monolog' => array($vendorDir . '/monolog/monolog/src'), |
||||
'FFMpeg' => array($vendorDir . '/php-ffmpeg/php-ffmpeg/src'), |
||||
'Evenement' => array($vendorDir . '/evenement/evenement/src'), |
||||
'Doctrine\\Common\\Cache\\' => array($vendorDir . '/doctrine/cache/lib'), |
||||
'Alchemy' => array($vendorDir . '/alchemy/binary-driver/src'), |
||||
); |
||||
@ -0,0 +1,43 @@ |
||||
<?php |
||||
|
||||
// autoload_real.php generated by Composer |
||||
|
||||
class ComposerAutoloaderInit75f7ea26a11f6be21d13acba7a2d0249 |
||||
{ |
||||
private static $loader; |
||||
|
||||
public static function loadClassLoader($class) |
||||
{ |
||||
if ('Composer\Autoload\ClassLoader' === $class) { |
||||
require __DIR__ . '/ClassLoader.php'; |
||||
} |
||||
} |
||||
|
||||
public static function getLoader() |
||||
{ |
||||
if (null !== self::$loader) { |
||||
return self::$loader; |
||||
} |
||||
|
||||
spl_autoload_register(array('ComposerAutoloaderInit75f7ea26a11f6be21d13acba7a2d0249', 'loadClassLoader'), true, true); |
||||
self::$loader = $loader = new \Composer\Autoload\ClassLoader(); |
||||
spl_autoload_unregister(array('ComposerAutoloaderInit75f7ea26a11f6be21d13acba7a2d0249', 'loadClassLoader')); |
||||
|
||||
$vendorDir = dirname(__DIR__); |
||||
$baseDir = dirname($vendorDir); |
||||
|
||||
$map = require __DIR__ . '/autoload_namespaces.php'; |
||||
foreach ($map as $namespace => $path) { |
||||
$loader->set($namespace, $path); |
||||
} |
||||
|
||||
$classMap = require __DIR__ . '/autoload_classmap.php'; |
||||
if ($classMap) { |
||||
$loader->addClassMap($classMap); |
||||
} |
||||
|
||||
$loader->register(true); |
||||
|
||||
return $loader; |
||||
} |
||||
} |
||||
@ -0,0 +1,397 @@ |
||||
[ |
||||
{ |
||||
"name": "evenement/evenement", |
||||
"version": "v1.0.0", |
||||
"version_normalized": "1.0.0.0", |
||||
"source": { |
||||
"type": "git", |
||||
"url": "https://github.com/igorw/evenement.git", |
||||
"reference": "fa966683e7df3e5dd5929d984a44abfbd6bafe8d" |
||||
}, |
||||
"dist": { |
||||
"type": "zip", |
||||
"url": "https://api.github.com/repos/igorw/evenement/zipball/fa966683e7df3e5dd5929d984a44abfbd6bafe8d", |
||||
"reference": "fa966683e7df3e5dd5929d984a44abfbd6bafe8d", |
||||
"shasum": "" |
||||
}, |
||||
"require": { |
||||
"php": ">=5.3.0" |
||||
}, |
||||
"time": "2012-05-30 15:01:08", |
||||
"type": "library", |
||||
"installation-source": "dist", |
||||
"autoload": { |
||||
"psr-0": { |
||||
"Evenement": "src" |
||||
} |
||||
}, |
||||
"notification-url": "https://packagist.org/downloads/", |
||||
"license": [ |
||||
"MIT" |
||||
], |
||||
"authors": [ |
||||
{ |
||||
"name": "Igor Wiedler", |
||||
"email": "igor@wiedler.ch", |
||||
"homepage": "http://wiedler.ch/igor/" |
||||
} |
||||
], |
||||
"description": "Événement is a very simple event dispatching library for PHP 5.3", |
||||
"keywords": [ |
||||
"event-dispatcher" |
||||
] |
||||
}, |
||||
{ |
||||
"name": "doctrine/cache", |
||||
"version": "v1.1", |
||||
"version_normalized": "1.1.0.0", |
||||
"source": { |
||||
"type": "git", |
||||
"url": "https://github.com/doctrine/cache.git", |
||||
"reference": "2c9761ff1d13e188d5f7378066c1ce2882d7a336" |
||||
}, |
||||
"dist": { |
||||
"type": "zip", |
||||
"url": "https://api.github.com/repos/doctrine/cache/zipball/2c9761ff1d13e188d5f7378066c1ce2882d7a336", |
||||
"reference": "2c9761ff1d13e188d5f7378066c1ce2882d7a336", |
||||
"shasum": "" |
||||
}, |
||||
"require": { |
||||
"php": ">=5.3.2" |
||||
}, |
||||
"conflict": { |
||||
"doctrine/common": ">2.2,<2.4" |
||||
}, |
||||
"time": "2013-08-07 16:04:25", |
||||
"type": "library", |
||||
"extra": { |
||||
"branch-alias": { |
||||
"dev-master": "1.0.x-dev" |
||||
} |
||||
}, |
||||
"installation-source": "dist", |
||||
"autoload": { |
||||
"psr-0": { |
||||
"Doctrine\\Common\\Cache\\": "lib/" |
||||
} |
||||
}, |
||||
"notification-url": "https://packagist.org/downloads/", |
||||
"license": [ |
||||
"MIT" |
||||
], |
||||
"authors": [ |
||||
{ |
||||
"name": "Jonathan Wage", |
||||
"email": "jonwage@gmail.com", |
||||
"homepage": "http://www.jwage.com/" |
||||
}, |
||||
{ |
||||
"name": "Guilherme Blanco", |
||||
"email": "guilhermeblanco@gmail.com", |
||||
"homepage": "http://www.instaclick.com" |
||||
}, |
||||
{ |
||||
"name": "Roman Borschel", |
||||
"email": "roman@code-factory.org" |
||||
}, |
||||
{ |
||||
"name": "Benjamin Eberlei", |
||||
"email": "kontakt@beberlei.de" |
||||
}, |
||||
{ |
||||
"name": "Johannes Schmitt", |
||||
"email": "schmittjoh@gmail.com", |
||||
"homepage": "http://jmsyst.com", |
||||
"role": "Developer of wrapped JMSSerializerBundle" |
||||
} |
||||
], |
||||
"description": "Caching library offering an object-oriented API for many cache backends", |
||||
"homepage": "http://www.doctrine-project.org", |
||||
"keywords": [ |
||||
"cache", |
||||
"caching" |
||||
] |
||||
}, |
||||
{ |
||||
"name": "psr/log", |
||||
"version": "1.0.0", |
||||
"version_normalized": "1.0.0.0", |
||||
"source": { |
||||
"type": "git", |
||||
"url": "https://github.com/php-fig/log.git", |
||||
"reference": "fe0936ee26643249e916849d48e3a51d5f5e278b" |
||||
}, |
||||
"dist": { |
||||
"type": "zip", |
||||
"url": "https://api.github.com/repos/php-fig/log/zipball/fe0936ee26643249e916849d48e3a51d5f5e278b", |
||||
"reference": "fe0936ee26643249e916849d48e3a51d5f5e278b", |
||||
"shasum": "" |
||||
}, |
||||
"time": "2012-12-21 11:40:51", |
||||
"type": "library", |
||||
"installation-source": "dist", |
||||
"autoload": { |
||||
"psr-0": { |
||||
"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", |
||||
"keywords": [ |
||||
"log", |
||||
"psr", |
||||
"psr-3" |
||||
] |
||||
}, |
||||
{ |
||||
"name": "symfony/process", |
||||
"version": "v2.3.4", |
||||
"version_normalized": "2.3.4.0", |
||||
"target-dir": "Symfony/Component/Process", |
||||
"source": { |
||||
"type": "git", |
||||
"url": "https://github.com/symfony/Process.git", |
||||
"reference": "1e91553e1cedd0b8fb1da6ea4f89b02e21713d5b" |
||||
}, |
||||
"dist": { |
||||
"type": "zip", |
||||
"url": "https://api.github.com/repos/symfony/Process/zipball/1e91553e1cedd0b8fb1da6ea4f89b02e21713d5b", |
||||
"reference": "1e91553e1cedd0b8fb1da6ea4f89b02e21713d5b", |
||||
"shasum": "" |
||||
}, |
||||
"require": { |
||||
"php": ">=5.3.3" |
||||
}, |
||||
"time": "2013-08-22 06:42:25", |
||||
"type": "library", |
||||
"extra": { |
||||
"branch-alias": { |
||||
"dev-master": "2.3-dev" |
||||
} |
||||
}, |
||||
"installation-source": "dist", |
||||
"autoload": { |
||||
"psr-0": { |
||||
"Symfony\\Component\\Process\\": "" |
||||
} |
||||
}, |
||||
"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 Process Component", |
||||
"homepage": "http://symfony.com" |
||||
}, |
||||
{ |
||||
"name": "monolog/monolog", |
||||
"version": "1.6.0", |
||||
"version_normalized": "1.6.0.0", |
||||
"source": { |
||||
"type": "git", |
||||
"url": "https://github.com/Seldaek/monolog.git", |
||||
"reference": "f72392d0e6eb855118f5a84e89ac2d257c704abd" |
||||
}, |
||||
"dist": { |
||||
"type": "zip", |
||||
"url": "https://api.github.com/repos/Seldaek/monolog/zipball/f72392d0e6eb855118f5a84e89ac2d257c704abd", |
||||
"reference": "f72392d0e6eb855118f5a84e89ac2d257c704abd", |
||||
"shasum": "" |
||||
}, |
||||
"require": { |
||||
"php": ">=5.3.0", |
||||
"psr/log": "~1.0" |
||||
}, |
||||
"require-dev": { |
||||
"doctrine/couchdb": "dev-master", |
||||
"mlehner/gelf-php": "1.0.*", |
||||
"raven/raven": "0.5.*" |
||||
}, |
||||
"suggest": { |
||||
"doctrine/couchdb": "Allow sending log messages to a CouchDB server", |
||||
"ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", |
||||
"ext-mongo": "Allow sending log messages to a MongoDB server", |
||||
"mlehner/gelf-php": "Allow sending log messages to a GrayLog2 server", |
||||
"raven/raven": "Allow sending log messages to a Sentry server" |
||||
}, |
||||
"time": "2013-07-28 22:38:30", |
||||
"type": "library", |
||||
"extra": { |
||||
"branch-alias": { |
||||
"dev-master": "1.6.x-dev" |
||||
} |
||||
}, |
||||
"installation-source": "dist", |
||||
"autoload": { |
||||
"psr-0": { |
||||
"Monolog": "src/" |
||||
} |
||||
}, |
||||
"notification-url": "https://packagist.org/downloads/", |
||||
"license": [ |
||||
"MIT" |
||||
], |
||||
"authors": [ |
||||
{ |
||||
"name": "Jordi Boggiano", |
||||
"email": "j.boggiano@seld.be", |
||||
"homepage": "http://seld.be", |
||||
"role": "Developer" |
||||
} |
||||
], |
||||
"description": "Sends your logs to files, sockets, inboxes, databases and various web services", |
||||
"homepage": "http://github.com/Seldaek/monolog", |
||||
"keywords": [ |
||||
"log", |
||||
"logging", |
||||
"psr-3" |
||||
] |
||||
}, |
||||
{ |
||||
"name": "alchemy/binary-driver", |
||||
"version": "1.5.0", |
||||
"version_normalized": "1.5.0.0", |
||||
"source": { |
||||
"type": "git", |
||||
"url": "https://github.com/alchemy-fr/BinaryDriver.git", |
||||
"reference": "b32c03d4b56ce29f783051eac55887adae654b41" |
||||
}, |
||||
"dist": { |
||||
"type": "zip", |
||||
"url": "https://api.github.com/repos/alchemy-fr/BinaryDriver/zipball/b32c03d4b56ce29f783051eac55887adae654b41", |
||||
"reference": "b32c03d4b56ce29f783051eac55887adae654b41", |
||||
"shasum": "" |
||||
}, |
||||
"require": { |
||||
"evenement/evenement": "~1.0", |
||||
"monolog/monolog": "~1.3", |
||||
"php": ">=5.3.3", |
||||
"psr/log": "~1.0", |
||||
"symfony/process": "~2.0" |
||||
}, |
||||
"require-dev": { |
||||
"phpunit/phpunit": "~3.7" |
||||
}, |
||||
"time": "2013-06-21 15:51:20", |
||||
"type": "library", |
||||
"installation-source": "dist", |
||||
"autoload": { |
||||
"psr-0": { |
||||
"Alchemy": "src" |
||||
} |
||||
}, |
||||
"notification-url": "https://packagist.org/downloads/", |
||||
"license": [ |
||||
"MIT" |
||||
], |
||||
"authors": [ |
||||
{ |
||||
"name": "Romain Neutron", |
||||
"email": "imprec@gmail.com", |
||||
"homepage": "http://www.lickmychip.com/" |
||||
}, |
||||
{ |
||||
"name": "Nicolas Le Goff", |
||||
"email": "legoff.n@gmail.com" |
||||
}, |
||||
{ |
||||
"name": "Phraseanet Team", |
||||
"email": "info@alchemy.fr", |
||||
"homepage": "http://www.phraseanet.com/" |
||||
} |
||||
], |
||||
"description": "A set of tools to build binary drivers", |
||||
"keywords": [ |
||||
"binary", |
||||
"driver" |
||||
] |
||||
}, |
||||
{ |
||||
"name": "php-ffmpeg/php-ffmpeg", |
||||
"version": "0.3.x-dev", |
||||
"version_normalized": "0.3.9999999.9999999-dev", |
||||
"source": { |
||||
"type": "git", |
||||
"url": "https://github.com/alchemy-fr/PHP-FFmpeg.git", |
||||
"reference": "9fcb485d497872e674cb14eb3df1386dbda9169b" |
||||
}, |
||||
"dist": { |
||||
"type": "zip", |
||||
"url": "https://api.github.com/repos/alchemy-fr/PHP-FFmpeg/zipball/9fcb485d497872e674cb14eb3df1386dbda9169b", |
||||
"reference": "9fcb485d497872e674cb14eb3df1386dbda9169b", |
||||
"shasum": "" |
||||
}, |
||||
"require": { |
||||
"alchemy/binary-driver": "~1.5", |
||||
"doctrine/cache": "~1.0", |
||||
"evenement/evenement": "~1.0", |
||||
"php": ">=5.3.3" |
||||
}, |
||||
"require-dev": { |
||||
"phpunit/phpunit": "~3.7", |
||||
"sami/sami": "~1.0", |
||||
"silex/silex": "~1.0" |
||||
}, |
||||
"suggest": { |
||||
"php-ffmpeg/extras": "A compilation of common audio & video drivers for PHP-FFMpeg" |
||||
}, |
||||
"time": "2013-08-08 10:15:15", |
||||
"type": "library", |
||||
"extra": { |
||||
"branch-alias": { |
||||
"dev-master": "0.4-dev" |
||||
} |
||||
}, |
||||
"installation-source": "source", |
||||
"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/" |
||||
} |
||||
], |
||||
"description": "FFMpeg PHP, an Object Oriented library to communicate with AVconv / ffmpeg", |
||||
"keywords": [ |
||||
"audio", |
||||
"audio processing", |
||||
"avconv", |
||||
"avprobe", |
||||
"ffmpeg", |
||||
"ffprobe", |
||||
"video", |
||||
"video processing" |
||||
] |
||||
} |
||||
] |
||||
@ -0,0 +1,9 @@ |
||||
language: php |
||||
|
||||
php: |
||||
- 5.3 |
||||
- 5.4 |
||||
- 5.5 |
||||
|
||||
before_script: |
||||
- composer --prefer-source --dev install |
||||
@ -0,0 +1,19 @@ |
||||
Copyright (c) 2006-2012 Doctrine Project |
||||
|
||||
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. |
||||
@ -0,0 +1,9 @@ |
||||
# Doctrine Cache |
||||
|
||||
Cache component extracted from the Doctrine Common project. |
||||
|
||||
## Changelog |
||||
|
||||
### v1.1 |
||||
|
||||
* Added support for MongoDB as Cache Provider |
||||
@ -0,0 +1,29 @@ |
||||
{ |
||||
"name": "doctrine/cache", |
||||
"type": "library", |
||||
"description": "Caching library offering an object-oriented API for many cache backends", |
||||
"keywords": ["cache", "caching"], |
||||
"homepage": "http://www.doctrine-project.org", |
||||
"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"} |
||||
], |
||||
"require": { |
||||
"php": ">=5.3.2" |
||||
}, |
||||
"conflict": { |
||||
"doctrine/common": ">2.2,<2.4" |
||||
}, |
||||
"autoload": { |
||||
"psr-0": { "Doctrine\\Common\\Cache\\": "lib/" } |
||||
}, |
||||
"extra": { |
||||
"branch-alias": { |
||||
"dev-master": "1.0.x-dev" |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,91 @@ |
||||
<?php |
||||
/* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
* This software consists of voluntary contributions made by many individuals |
||||
* and is licensed under the MIT license. For more information, see |
||||
* <http://www.doctrine-project.org>. |
||||
*/ |
||||
|
||||
namespace Doctrine\Common\Cache; |
||||
|
||||
/** |
||||
* APC cache provider. |
||||
* |
||||
* @link www.doctrine-project.org |
||||
* @since 2.0 |
||||
* @author Benjamin Eberlei <kontakt@beberlei.de> |
||||
* @author Guilherme Blanco <guilhermeblanco@hotmail.com> |
||||
* @author Jonathan Wage <jonwage@gmail.com> |
||||
* @author Roman Borschel <roman@code-factory.org> |
||||
* @author David Abdemoulaie <dave@hobodave.com> |
||||
*/ |
||||
class ApcCache extends CacheProvider |
||||
{ |
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
protected function doFetch($id) |
||||
{ |
||||
return apc_fetch($id); |
||||
} |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
protected function doContains($id) |
||||
{ |
||||
return apc_exists($id); |
||||
} |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
protected function doSave($id, $data, $lifeTime = 0) |
||||
{ |
||||
return (bool) apc_store($id, $data, (int) $lifeTime); |
||||
} |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
protected function doDelete($id) |
||||
{ |
||||
return apc_delete($id); |
||||
} |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
protected function doFlush() |
||||
{ |
||||
return apc_clear_cache() && apc_clear_cache('user'); |
||||
} |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
protected function doGetStats() |
||||
{ |
||||
$info = apc_cache_info(); |
||||
$sma = apc_sma_info(); |
||||
|
||||
return array( |
||||
Cache::STATS_HITS => $info['num_hits'], |
||||
Cache::STATS_MISSES => $info['num_misses'], |
||||
Cache::STATS_UPTIME => $info['start_time'], |
||||
Cache::STATS_MEMORY_USAGE => $info['mem_size'], |
||||
Cache::STATS_MEMORY_AVAILABLE => $sma['avail_mem'], |
||||
); |
||||
} |
||||
} |
||||
@ -0,0 +1,93 @@ |
||||
<?php |
||||
/* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
* This software consists of voluntary contributions made by many individuals |
||||
* and is licensed under the MIT license. For more information, see |
||||
* <http://www.doctrine-project.org>. |
||||
*/ |
||||
|
||||
namespace Doctrine\Common\Cache; |
||||
|
||||
/** |
||||
* Array cache driver. |
||||
* |
||||
* @link www.doctrine-project.org |
||||
* @since 2.0 |
||||
* @author Benjamin Eberlei <kontakt@beberlei.de> |
||||
* @author Guilherme Blanco <guilhermeblanco@hotmail.com> |
||||
* @author Jonathan Wage <jonwage@gmail.com> |
||||
* @author Roman Borschel <roman@code-factory.org> |
||||
* @author David Abdemoulaie <dave@hobodave.com> |
||||
*/ |
||||
class ArrayCache extends CacheProvider |
||||
{ |
||||
/** |
||||
* @var array $data |
||||
*/ |
||||
private $data = array(); |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
protected function doFetch($id) |
||||
{ |
||||
return (isset($this->data[$id])) ? $this->data[$id] : false; |
||||
} |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
protected function doContains($id) |
||||
{ |
||||
return isset($this->data[$id]); |
||||
} |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
protected function doSave($id, $data, $lifeTime = 0) |
||||
{ |
||||
$this->data[$id] = $data; |
||||
|
||||
return true; |
||||
} |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
protected function doDelete($id) |
||||
{ |
||||
unset($this->data[$id]); |
||||
|
||||
return true; |
||||
} |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
protected function doFlush() |
||||
{ |
||||
$this->data = array(); |
||||
|
||||
return true; |
||||
} |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
protected function doGetStats() |
||||
{ |
||||
return null; |
||||
} |
||||
} |
||||
@ -0,0 +1,111 @@ |
||||
<?php |
||||
/* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
* This software consists of voluntary contributions made by many individuals |
||||
* and is licensed under the MIT license. For more information, see |
||||
* <http://www.doctrine-project.org>. |
||||
*/ |
||||
|
||||
namespace Doctrine\Common\Cache; |
||||
|
||||
/** |
||||
* Interface for cache drivers. |
||||
* |
||||
* @link www.doctrine-project.org |
||||
* @since 2.0 |
||||
* @author Benjamin Eberlei <kontakt@beberlei.de> |
||||
* @author Guilherme Blanco <guilhermeblanco@hotmail.com> |
||||
* @author Jonathan Wage <jonwage@gmail.com> |
||||
* @author Roman Borschel <roman@code-factory.org> |
||||
* @author Fabio B. Silva <fabio.bat.silva@gmail.com> |
||||
*/ |
||||
interface Cache |
||||
{ |
||||
const STATS_HITS = 'hits'; |
||||
const STATS_MISSES = 'misses'; |
||||
const STATS_UPTIME = 'uptime'; |
||||
const STATS_MEMORY_USAGE = 'memory_usage'; |
||||
const STATS_MEMORY_AVAILABLE = 'memory_available'; |
||||
/** |
||||
* Only for backward compatibility (may be removed in next major release) |
||||
* |
||||
* @deprecated |
||||
*/ |
||||
const STATS_MEMORY_AVAILIABLE = 'memory_available'; |
||||
|
||||
/** |
||||
* Fetches an entry from the cache. |
||||
* |
||||
* @param string $id The id of the cache entry to fetch. |
||||
* |
||||
* @return mixed The cached data or FALSE, if no cache entry exists for the given id. |
||||
*/ |
||||
function fetch($id); |
||||
|
||||
/** |
||||
* Tests if an entry exists in the cache. |
||||
* |
||||
* @param string $id The cache id of the entry to check for. |
||||
* |
||||
* @return boolean TRUE if a cache entry exists for the given cache id, FALSE otherwise. |
||||
*/ |
||||
function contains($id); |
||||
|
||||
/** |
||||
* Puts data into the cache. |
||||
* |
||||
* @param string $id The cache id. |
||||
* @param mixed $data The cache entry/data. |
||||
* @param int $lifeTime The cache lifetime. |
||||
* If != 0, sets a specific lifetime for this cache entry (0 => infinite lifeTime). |
||||
* |
||||
* @return boolean TRUE if the entry was successfully stored in the cache, FALSE otherwise. |
||||
*/ |
||||
function save($id, $data, $lifeTime = 0); |
||||
|
||||
/** |
||||
* Deletes a cache entry. |
||||
* |
||||
* @param string $id The cache id. |
||||
* |
||||
* @return boolean TRUE if the cache entry was successfully deleted, FALSE otherwise. |
||||
*/ |
||||
function delete($id); |
||||
|
||||
/** |
||||
* Retrieves cached information from the data store. |
||||
* |
||||
* The server's statistics array has the following values: |
||||
* |
||||
* - <b>hits</b> |
||||
* Number of keys that have been requested and found present. |
||||
* |
||||
* - <b>misses</b> |
||||
* Number of items that have been requested and not found. |
||||
* |
||||
* - <b>uptime</b> |
||||
* Time that the server is running. |
||||
* |
||||
* - <b>memory_usage</b> |
||||
* Memory used by this server to store items. |
||||
* |
||||
* - <b>memory_available</b> |
||||
* Memory allowed to use for storage. |
||||
* |
||||
* @since 2.2 |
||||
* |
||||
* @return array|null An associative array with server's statistics if available, NULL otherwise. |
||||
*/ |
||||
function getStats(); |
||||
} |
||||
@ -0,0 +1,240 @@ |
||||
<?php |
||||
/* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
* This software consists of voluntary contributions made by many individuals |
||||
* and is licensed under the MIT license. For more information, see |
||||
* <http://www.doctrine-project.org>. |
||||
*/ |
||||
|
||||
namespace Doctrine\Common\Cache; |
||||
|
||||
/** |
||||
* Base class for cache provider implementations. |
||||
* |
||||
* @since 2.2 |
||||
* @author Benjamin Eberlei <kontakt@beberlei.de> |
||||
* @author Guilherme Blanco <guilhermeblanco@hotmail.com> |
||||
* @author Jonathan Wage <jonwage@gmail.com> |
||||
* @author Roman Borschel <roman@code-factory.org> |
||||
* @author Fabio B. Silva <fabio.bat.silva@gmail.com> |
||||
*/ |
||||
abstract class CacheProvider implements Cache |
||||
{ |
||||
const DOCTRINE_NAMESPACE_CACHEKEY = 'DoctrineNamespaceCacheKey[%s]'; |
||||
|
||||
/** |
||||
* The namespace to prefix all cache ids with. |
||||
* |
||||
* @var string |
||||
*/ |
||||
private $namespace = ''; |
||||
|
||||
/** |
||||
* The namespace version. |
||||
* |
||||
* @var string |
||||
*/ |
||||
private $namespaceVersion; |
||||
|
||||
/** |
||||
* Sets the namespace to prefix all cache ids with. |
||||
* |
||||
* @param string $namespace |
||||
* |
||||
* @return void |
||||
*/ |
||||
public function setNamespace($namespace) |
||||
{ |
||||
$this->namespace = (string) $namespace; |
||||
} |
||||
|
||||
/** |
||||
* Retrieves the namespace that prefixes all cache ids. |
||||
* |
||||
* @return string |
||||
*/ |
||||
public function getNamespace() |
||||
{ |
||||
return $this->namespace; |
||||
} |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
public function fetch($id) |
||||
{ |
||||
return $this->doFetch($this->getNamespacedId($id)); |
||||
} |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
public function contains($id) |
||||
{ |
||||
return $this->doContains($this->getNamespacedId($id)); |
||||
} |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
public function save($id, $data, $lifeTime = 0) |
||||
{ |
||||
return $this->doSave($this->getNamespacedId($id), $data, $lifeTime); |
||||
} |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
public function delete($id) |
||||
{ |
||||
return $this->doDelete($this->getNamespacedId($id)); |
||||
} |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
public function getStats() |
||||
{ |
||||
return $this->doGetStats(); |
||||
} |
||||
|
||||
/** |
||||
* Flushes all cache entries. |
||||
* |
||||
* @return boolean TRUE if the cache entries were successfully flushed, FALSE otherwise. |
||||
*/ |
||||
public function flushAll() |
||||
{ |
||||
return $this->doFlush(); |
||||
} |
||||
|
||||
/** |
||||
* Deletes all cache entries. |
||||
* |
||||
* @return boolean TRUE if the cache entries were successfully deleted, FALSE otherwise. |
||||
*/ |
||||
public function deleteAll() |
||||
{ |
||||
$namespaceCacheKey = $this->getNamespaceCacheKey(); |
||||
$namespaceVersion = $this->getNamespaceVersion() + 1; |
||||
|
||||
$this->namespaceVersion = $namespaceVersion; |
||||
|
||||
return $this->doSave($namespaceCacheKey, $namespaceVersion); |
||||
} |
||||
|
||||
/** |
||||
* Prefixes the passed id with the configured namespace value. |
||||
* |
||||
* @param string $id The id to namespace. |
||||
* |
||||
* @return string The namespaced id. |
||||
*/ |
||||
private function getNamespacedId($id) |
||||
{ |
||||
$namespaceVersion = $this->getNamespaceVersion(); |
||||
|
||||
return sprintf('%s[%s][%s]', $this->namespace, $id, $namespaceVersion); |
||||
} |
||||
|
||||
/** |
||||
* Returns the namespace cache key. |
||||
* |
||||
* @return string |
||||
*/ |
||||
private function getNamespaceCacheKey() |
||||
{ |
||||
return sprintf(self::DOCTRINE_NAMESPACE_CACHEKEY, $this->namespace); |
||||
} |
||||
|
||||
/** |
||||
* Returns the namespace version. |
||||
* |
||||
* @return string |
||||
*/ |
||||
private function getNamespaceVersion() |
||||
{ |
||||
if (null !== $this->namespaceVersion) { |
||||
return $this->namespaceVersion; |
||||
} |
||||
|
||||
$namespaceCacheKey = $this->getNamespaceCacheKey(); |
||||
$namespaceVersion = $this->doFetch($namespaceCacheKey); |
||||
|
||||
if (false === $namespaceVersion) { |
||||
$namespaceVersion = 1; |
||||
|
||||
$this->doSave($namespaceCacheKey, $namespaceVersion); |
||||
} |
||||
|
||||
$this->namespaceVersion = $namespaceVersion; |
||||
|
||||
return $this->namespaceVersion; |
||||
} |
||||
|
||||
/** |
||||
* Fetches an entry from the cache. |
||||
* |
||||
* @param string $id The id of the cache entry to fetch. |
||||
* |
||||
* @return string|bool The cached data or FALSE, if no cache entry exists for the given id. |
||||
*/ |
||||
abstract protected function doFetch($id); |
||||
|
||||
/** |
||||
* Tests if an entry exists in the cache. |
||||
* |
||||
* @param string $id The cache id of the entry to check for. |
||||
* |
||||
* @return boolean TRUE if a cache entry exists for the given cache id, FALSE otherwise. |
||||
*/ |
||||
abstract protected function doContains($id); |
||||
|
||||
/** |
||||
* Puts data into the cache. |
||||
* |
||||
* @param string $id The cache id. |
||||
* @param string $data The cache entry/data. |
||||
* @param int $lifeTime The lifetime. If != 0, sets a specific lifetime for this |
||||
* cache entry (0 => infinite lifeTime). |
||||
* |
||||
* @return boolean TRUE if the entry was successfully stored in the cache, FALSE otherwise. |
||||
*/ |
||||
abstract protected function doSave($id, $data, $lifeTime = 0); |
||||
|
||||
/** |
||||
* Deletes a cache entry. |
||||
* |
||||
* @param string $id The cache id. |
||||
* |
||||
* @return boolean TRUE if the cache entry was successfully deleted, FALSE otherwise. |
||||
*/ |
||||
abstract protected function doDelete($id); |
||||
|
||||
/** |
||||
* Flushes all cache entries. |
||||
* |
||||
* @return boolean TRUE if the cache entry was successfully deleted, FALSE otherwise. |
||||
*/ |
||||
abstract protected function doFlush(); |
||||
|
||||
/** |
||||
* Retrieves cached information from the data store. |
||||
* |
||||
* @since 2.2 |
||||
* |
||||
* @return array|null An associative array with server's statistics if available, NULL otherwise. |
||||
*/ |
||||
abstract protected function doGetStats(); |
||||
} |
||||
@ -0,0 +1,121 @@ |
||||
<?php |
||||
/* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
* This software consists of voluntary contributions made by many individuals |
||||
* and is licensed under the MIT license. For more information, see |
||||
* <http://www.doctrine-project.org>. |
||||
*/ |
||||
|
||||
namespace Doctrine\Common\Cache; |
||||
|
||||
use \Couchbase; |
||||
|
||||
/** |
||||
* Couchbase cache provider. |
||||
* |
||||
* @link www.doctrine-project.org |
||||
* @since 2.4 |
||||
* @author Michael Nitschinger <michael@nitschinger.at> |
||||
*/ |
||||
class CouchbaseCache extends CacheProvider |
||||
{ |
||||
/** |
||||
* @var Couchbase|null |
||||
*/ |
||||
private $couchbase; |
||||
|
||||
/** |
||||
* Sets the Couchbase instance to use. |
||||
* |
||||
* @param Couchbase $couchbase |
||||
* |
||||
* @return void |
||||
*/ |
||||
public function setCouchbase(Couchbase $couchbase) |
||||
{ |
||||
$this->couchbase = $couchbase; |
||||
} |
||||
|
||||
/** |
||||
* Gets the Couchbase instance used by the cache. |
||||
* |
||||
* @return Couchbase|null |
||||
*/ |
||||
public function getCouchbase() |
||||
{ |
||||
return $this->couchbase; |
||||
} |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
protected function doFetch($id) |
||||
{ |
||||
return $this->couchbase->get($id) ?: false; |
||||
} |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
protected function doContains($id) |
||||
{ |
||||
return (null !== $this->couchbase->get($id)); |
||||
} |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
protected function doSave($id, $data, $lifeTime = 0) |
||||
{ |
||||
if ($lifeTime > 30 * 24 * 3600) { |
||||
$lifeTime = time() + $lifeTime; |
||||
} |
||||
return $this->couchbase->set($id, $data, (int) $lifeTime); |
||||
} |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
protected function doDelete($id) |
||||
{ |
||||
return $this->couchbase->delete($id); |
||||
} |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
protected function doFlush() |
||||
{ |
||||
return $this->couchbase->flush(); |
||||
} |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
protected function doGetStats() |
||||
{ |
||||
$stats = $this->couchbase->getStats(); |
||||
$servers = $this->couchbase->getServers(); |
||||
$server = explode(":", $servers[0]); |
||||
$key = $server[0] . ":" . "11210"; |
||||
$stats = $stats[$key]; |
||||
return array( |
||||
Cache::STATS_HITS => $stats['get_hits'], |
||||
Cache::STATS_MISSES => $stats['get_misses'], |
||||
Cache::STATS_UPTIME => $stats['uptime'], |
||||
Cache::STATS_MEMORY_USAGE => $stats['bytes'], |
||||
Cache::STATS_MEMORY_AVAILABLE => $stats['limit_maxbytes'], |
||||
); |
||||
} |
||||
} |
||||
@ -0,0 +1,158 @@ |
||||
<?php |
||||
/* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
* This software consists of voluntary contributions made by many individuals |
||||
* and is licensed under the MIT license. For more information, see |
||||
* <http://www.doctrine-project.org>. |
||||
*/ |
||||
|
||||
namespace Doctrine\Common\Cache; |
||||
|
||||
/** |
||||
* Base file cache driver. |
||||
* |
||||
* @since 2.3 |
||||
* @author Fabio B. Silva <fabio.bat.silva@gmail.com> |
||||
*/ |
||||
abstract class FileCache extends CacheProvider |
||||
{ |
||||
/** |
||||
* The cache directory. |
||||
* |
||||
* @var string |
||||
*/ |
||||
protected $directory; |
||||
|
||||
/** |
||||
* The cache file extension. |
||||
* |
||||
* @var string|null |
||||
*/ |
||||
protected $extension; |
||||
|
||||
/** |
||||
* Constructor. |
||||
* |
||||
* @param string $directory The cache directory. |
||||
* @param string|null $extension The cache file extension. |
||||
* |
||||
* @throws \InvalidArgumentException |
||||
*/ |
||||
public function __construct($directory, $extension = null) |
||||
{ |
||||
if ( ! is_dir($directory) && ! @mkdir($directory, 0777, true)) { |
||||
throw new \InvalidArgumentException(sprintf( |
||||
'The directory "%s" does not exist and could not be created.', |
||||
$directory |
||||
)); |
||||
} |
||||
|
||||
if ( ! is_writable($directory)) { |
||||
throw new \InvalidArgumentException(sprintf( |
||||
'The directory "%s" is not writable.', |
||||
$directory |
||||
)); |
||||
} |
||||
|
||||
$this->directory = realpath($directory); |
||||
$this->extension = $extension ?: $this->extension; |
||||
} |
||||
|
||||
/** |
||||
* Gets the cache directory. |
||||
* |
||||
* @return string |
||||
*/ |
||||
public function getDirectory() |
||||
{ |
||||
return $this->directory; |
||||
} |
||||
|
||||
/** |
||||
* Gets the cache file extension. |
||||
* |
||||
* @return string|null |
||||
*/ |
||||
public function getExtension() |
||||
{ |
||||
return $this->extension; |
||||
} |
||||
|
||||
/** |
||||
* @param string $id |
||||
* |
||||
* @return string |
||||
*/ |
||||
protected function getFilename($id) |
||||
{ |
||||
$hash = hash('sha256', $id); |
||||
$path = implode(str_split($hash, 16), DIRECTORY_SEPARATOR); |
||||
$path = $this->directory . DIRECTORY_SEPARATOR . $path; |
||||
$id = preg_replace('@[\\\/:"*?<>|]+@', '', $id); |
||||
|
||||
return $path . DIRECTORY_SEPARATOR . $id . $this->extension; |
||||
} |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
protected function doDelete($id) |
||||
{ |
||||
return @unlink($this->getFilename($id)); |
||||
} |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
protected function doFlush() |
||||
{ |
||||
foreach ($this->getIterator() as $name => $file) { |
||||
@unlink($name); |
||||
} |
||||
|
||||
return true; |
||||
} |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
protected function doGetStats() |
||||
{ |
||||
$usage = 0; |
||||
foreach ($this->getIterator() as $name => $file) { |
||||
$usage += $file->getSize(); |
||||
} |
||||
|
||||
$free = disk_free_space($this->directory); |
||||
|
||||
return array( |
||||
Cache::STATS_HITS => null, |
||||
Cache::STATS_MISSES => null, |
||||
Cache::STATS_UPTIME => null, |
||||
Cache::STATS_MEMORY_USAGE => $usage, |
||||
Cache::STATS_MEMORY_AVAILABLE => $free, |
||||
); |
||||
} |
||||
|
||||
/** |
||||
* @return \Iterator |
||||
*/ |
||||
private function getIterator() |
||||
{ |
||||
$pattern = '/^.+\\' . $this->extension . '$/i'; |
||||
$iterator = new \RecursiveDirectoryIterator($this->directory); |
||||
$iterator = new \RecursiveIteratorIterator($iterator); |
||||
return new \RegexIterator($iterator, $pattern); |
||||
} |
||||
} |
||||
@ -0,0 +1,113 @@ |
||||
<?php |
||||
/* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
* This software consists of voluntary contributions made by many individuals |
||||
* and is licensed under the MIT license. For more information, see |
||||
* <http://www.doctrine-project.org>. |
||||
*/ |
||||
|
||||
namespace Doctrine\Common\Cache; |
||||
|
||||
/** |
||||
* Filesystem cache driver. |
||||
* |
||||
* @since 2.3 |
||||
* @author Fabio B. Silva <fabio.bat.silva@gmail.com> |
||||
*/ |
||||
class FilesystemCache extends FileCache |
||||
{ |
||||
const EXTENSION = '.doctrinecache.data'; |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
protected $extension = self::EXTENSION; |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
protected function doFetch($id) |
||||
{ |
||||
$data = ''; |
||||
$lifetime = -1; |
||||
$filename = $this->getFilename($id); |
||||
|
||||
if ( ! is_file($filename)) { |
||||
return false; |
||||
} |
||||
|
||||
$resource = fopen($filename, "r"); |
||||
|
||||
if (false !== ($line = fgets($resource))) { |
||||
$lifetime = (integer) $line; |
||||
} |
||||
|
||||
if ($lifetime !== 0 && $lifetime < time()) { |
||||
fclose($resource); |
||||
|
||||
return false; |
||||
} |
||||
|
||||
while (false !== ($line = fgets($resource))) { |
||||
$data .= $line; |
||||
} |
||||
|
||||
fclose($resource); |
||||
|
||||
return unserialize($data); |
||||
} |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
protected function doContains($id) |
||||
{ |
||||
$lifetime = -1; |
||||
$filename = $this->getFilename($id); |
||||
|
||||
if ( ! is_file($filename)) { |
||||
return false; |
||||
} |
||||
|
||||
$resource = fopen($filename, "r"); |
||||
|
||||
if (false !== ($line = fgets($resource))) { |
||||
$lifetime = (integer) $line; |
||||
} |
||||
|
||||
fclose($resource); |
||||
|
||||
return $lifetime === 0 || $lifetime > time(); |
||||
} |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
protected function doSave($id, $data, $lifeTime = 0) |
||||
{ |
||||
if ($lifeTime > 0) { |
||||
$lifeTime = time() + $lifeTime; |
||||
} |
||||
|
||||
$data = serialize($data); |
||||
$filename = $this->getFilename($id); |
||||
$filepath = pathinfo($filename, PATHINFO_DIRNAME); |
||||
|
||||
if ( ! is_dir($filepath)) { |
||||
mkdir($filepath, 0777, true); |
||||
} |
||||
|
||||
return file_put_contents($filename, $lifeTime . PHP_EOL . $data); |
||||
} |
||||
} |
||||
@ -0,0 +1,121 @@ |
||||
<?php |
||||
/* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
* This software consists of voluntary contributions made by many individuals |
||||
* and is licensed under the MIT license. For more information, see |
||||
* <http://www.doctrine-project.org>. |
||||
*/ |
||||
|
||||
namespace Doctrine\Common\Cache; |
||||
|
||||
use \Memcache; |
||||
|
||||
/** |
||||
* Memcache cache provider. |
||||
* |
||||
* @link www.doctrine-project.org |
||||
* @since 2.0 |
||||
* @author Benjamin Eberlei <kontakt@beberlei.de> |
||||
* @author Guilherme Blanco <guilhermeblanco@hotmail.com> |
||||
* @author Jonathan Wage <jonwage@gmail.com> |
||||
* @author Roman Borschel <roman@code-factory.org> |
||||
* @author David Abdemoulaie <dave@hobodave.com> |
||||
*/ |
||||
class MemcacheCache extends CacheProvider |
||||
{ |
||||
/** |
||||
* @var Memcache|null |
||||
*/ |
||||
private $memcache; |
||||
|
||||
/** |
||||
* Sets the memcache instance to use. |
||||
* |
||||
* @param Memcache $memcache |
||||
* |
||||
* @return void |
||||
*/ |
||||
public function setMemcache(Memcache $memcache) |
||||
{ |
||||
$this->memcache = $memcache; |
||||
} |
||||
|
||||
/** |
||||
* Gets the memcache instance used by the cache. |
||||
* |
||||
* @return Memcache|null |
||||
*/ |
||||
public function getMemcache() |
||||
{ |
||||
return $this->memcache; |
||||
} |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
protected function doFetch($id) |
||||
{ |
||||
return $this->memcache->get($id); |
||||
} |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
protected function doContains($id) |
||||
{ |
||||
return (bool) $this->memcache->get($id); |
||||
} |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
protected function doSave($id, $data, $lifeTime = 0) |
||||
{ |
||||
if ($lifeTime > 30 * 24 * 3600) { |
||||
$lifeTime = time() + $lifeTime; |
||||
} |
||||
return $this->memcache->set($id, $data, 0, (int) $lifeTime); |
||||
} |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
protected function doDelete($id) |
||||
{ |
||||
return $this->memcache->delete($id); |
||||
} |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
protected function doFlush() |
||||
{ |
||||
return $this->memcache->flush(); |
||||
} |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
protected function doGetStats() |
||||
{ |
||||
$stats = $this->memcache->getStats(); |
||||
return array( |
||||
Cache::STATS_HITS => $stats['get_hits'], |
||||
Cache::STATS_MISSES => $stats['get_misses'], |
||||
Cache::STATS_UPTIME => $stats['uptime'], |
||||
Cache::STATS_MEMORY_USAGE => $stats['bytes'], |
||||
Cache::STATS_MEMORY_AVAILABLE => $stats['limit_maxbytes'], |
||||
); |
||||
} |
||||
} |
||||
@ -0,0 +1,124 @@ |
||||
<?php |
||||
/* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
* This software consists of voluntary contributions made by many individuals |
||||
* and is licensed under the MIT license. For more information, see |
||||
* <http://www.doctrine-project.org>. |
||||
*/ |
||||
|
||||
namespace Doctrine\Common\Cache; |
||||
|
||||
use \Memcached; |
||||
|
||||
/** |
||||
* Memcached cache provider. |
||||
* |
||||
* @link www.doctrine-project.org |
||||
* @since 2.2 |
||||
* @author Benjamin Eberlei <kontakt@beberlei.de> |
||||
* @author Guilherme Blanco <guilhermeblanco@hotmail.com> |
||||
* @author Jonathan Wage <jonwage@gmail.com> |
||||
* @author Roman Borschel <roman@code-factory.org> |
||||
* @author David Abdemoulaie <dave@hobodave.com> |
||||
*/ |
||||
class MemcachedCache extends CacheProvider |
||||
{ |
||||
/** |
||||
* @var Memcached|null |
||||
*/ |
||||
private $memcached; |
||||
|
||||
/** |
||||
* Sets the memcache instance to use. |
||||
* |
||||
* @param Memcached $memcached |
||||
* |
||||
* @return void |
||||
*/ |
||||
public function setMemcached(Memcached $memcached) |
||||
{ |
||||
$this->memcached = $memcached; |
||||
} |
||||
|
||||
/** |
||||
* Gets the memcached instance used by the cache. |
||||
* |
||||
* @return Memcached|null |
||||
*/ |
||||
public function getMemcached() |
||||
{ |
||||
return $this->memcached; |
||||
} |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
protected function doFetch($id) |
||||
{ |
||||
return $this->memcached->get($id); |
||||
} |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
protected function doContains($id) |
||||
{ |
||||
return (false !== $this->memcached->get($id)); |
||||
} |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
protected function doSave($id, $data, $lifeTime = 0) |
||||
{ |
||||
if ($lifeTime > 30 * 24 * 3600) { |
||||
$lifeTime = time() + $lifeTime; |
||||
} |
||||
return $this->memcached->set($id, $data, (int) $lifeTime); |
||||
} |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
protected function doDelete($id) |
||||
{ |
||||
return $this->memcached->delete($id); |
||||
} |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
protected function doFlush() |
||||
{ |
||||
return $this->memcached->flush(); |
||||
} |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
protected function doGetStats() |
||||
{ |
||||
$stats = $this->memcached->getStats(); |
||||
$servers = $this->memcached->getServerList(); |
||||
$key = $servers[0]['host'] . ':' . $servers[0]['port']; |
||||
$stats = $stats[$key]; |
||||
return array( |
||||
Cache::STATS_HITS => $stats['get_hits'], |
||||
Cache::STATS_MISSES => $stats['get_misses'], |
||||
Cache::STATS_UPTIME => $stats['uptime'], |
||||
Cache::STATS_MEMORY_USAGE => $stats['bytes'], |
||||
Cache::STATS_MEMORY_AVAILABLE => $stats['limit_maxbytes'], |
||||
); |
||||
} |
||||
} |
||||
@ -0,0 +1,107 @@ |
||||
<?php |
||||
/* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
* This software consists of voluntary contributions made by many individuals |
||||
* and is licensed under the MIT license. For more information, see |
||||
* <http://www.doctrine-project.org>. |
||||
*/ |
||||
|
||||
namespace Doctrine\Common\Cache; |
||||
|
||||
/** |
||||
* Php file cache driver. |
||||
* |
||||
* @since 2.3 |
||||
* @author Fabio B. Silva <fabio.bat.silva@gmail.com> |
||||
*/ |
||||
class PhpFileCache extends FileCache |
||||
{ |
||||
const EXTENSION = '.doctrinecache.php'; |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
protected $extension = self::EXTENSION; |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
protected function doFetch($id) |
||||
{ |
||||
$filename = $this->getFilename($id); |
||||
|
||||
if ( ! is_file($filename)) { |
||||
return false; |
||||
} |
||||
|
||||
$value = include $filename; |
||||
|
||||
if ($value['lifetime'] !== 0 && $value['lifetime'] < time()) { |
||||
return false; |
||||
} |
||||
|
||||
return $value['data']; |
||||
} |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
protected function doContains($id) |
||||
{ |
||||
$filename = $this->getFilename($id); |
||||
|
||||
if ( ! is_file($filename)) { |
||||
return false; |
||||
} |
||||
|
||||
$value = include $filename; |
||||
|
||||
return $value['lifetime'] === 0 || $value['lifetime'] > time(); |
||||
} |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
protected function doSave($id, $data, $lifeTime = 0) |
||||
{ |
||||
if ($lifeTime > 0) { |
||||
$lifeTime = time() + $lifeTime; |
||||
} |
||||
|
||||
if (is_object($data) && ! method_exists($data, '__set_state')) { |
||||
throw new \InvalidArgumentException( |
||||
"Invalid argument given, PhpFileCache only allows objects that implement __set_state() " . |
||||
"and fully support var_export(). You can use the FilesystemCache to save arbitrary object " . |
||||
"graphs using serialize()/deserialize()." |
||||
); |
||||
} |
||||
|
||||
$filename = $this->getFilename($id); |
||||
$filepath = pathinfo($filename, PATHINFO_DIRNAME); |
||||
|
||||
if ( ! is_dir($filepath)) { |
||||
mkdir($filepath, 0777, true); |
||||
} |
||||
|
||||
$value = array( |
||||
'lifetime' => $lifeTime, |
||||
'data' => $data |
||||
); |
||||
|
||||
$value = var_export($value, true); |
||||
$code = sprintf('<?php return %s;', $value); |
||||
|
||||
return file_put_contents($filename, $code); |
||||
} |
||||
} |
||||
@ -0,0 +1,130 @@ |
||||
<?php |
||||
/* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
* This software consists of voluntary contributions made by many individuals |
||||
* and is licensed under the MIT license. For more information, see |
||||
* <http://www.doctrine-project.org>. |
||||
*/ |
||||
|
||||
namespace Doctrine\Common\Cache; |
||||
|
||||
use Redis; |
||||
|
||||
/** |
||||
* Redis cache provider. |
||||
* |
||||
* @link www.doctrine-project.org |
||||
* @since 2.2 |
||||
* @author Osman Ungur <osmanungur@gmail.com> |
||||
*/ |
||||
class RedisCache extends CacheProvider |
||||
{ |
||||
/** |
||||
* @var Redis|null |
||||
*/ |
||||
private $redis; |
||||
|
||||
/** |
||||
* Sets the redis instance to use. |
||||
* |
||||
* @param Redis $redis |
||||
* |
||||
* @return void |
||||
*/ |
||||
public function setRedis(Redis $redis) |
||||
{ |
||||
$redis->setOption(Redis::OPT_SERIALIZER, $this->getSerializerValue()); |
||||
$this->redis = $redis; |
||||
} |
||||
|
||||
/** |
||||
* Gets the redis instance used by the cache. |
||||
* |
||||
* @return Redis|null |
||||
*/ |
||||
public function getRedis() |
||||
{ |
||||
return $this->redis; |
||||
} |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
protected function doFetch($id) |
||||
{ |
||||
return $this->redis->get($id); |
||||
} |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
protected function doContains($id) |
||||
{ |
||||
return $this->redis->exists($id); |
||||
} |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
protected function doSave($id, $data, $lifeTime = 0) |
||||
{ |
||||
if ($lifeTime > 0) { |
||||
return $this->redis->setex($id, $lifeTime, $data); |
||||
} |
||||
return $this->redis->set($id, $data); |
||||
} |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
protected function doDelete($id) |
||||
{ |
||||
return $this->redis->delete($id); |
||||
} |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
protected function doFlush() |
||||
{ |
||||
return $this->redis->flushDB(); |
||||
} |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
protected function doGetStats() |
||||
{ |
||||
$info = $this->redis->info(); |
||||
return array( |
||||
Cache::STATS_HITS => false, |
||||
Cache::STATS_MISSES => false, |
||||
Cache::STATS_UPTIME => $info['uptime_in_seconds'], |
||||
Cache::STATS_MEMORY_USAGE => $info['used_memory'], |
||||
Cache::STATS_MEMORY_AVAILABLE => false |
||||
); |
||||
} |
||||
|
||||
/** |
||||
* Returns the serializer constant to use. If Redis is compiled with |
||||
* igbinary support, that is used. Otherwise the default PHP serializer is |
||||
* used. |
||||
* |
||||
* @return integer One of the Redis::SERIALIZER_* constants |
||||
*/ |
||||
protected function getSerializerValue() |
||||
{ |
||||
return defined('Redis::SERIALIZER_IGBINARY') ? Redis::SERIALIZER_IGBINARY : Redis::SERIALIZER_PHP; |
||||
} |
||||
} |
||||
@ -0,0 +1,250 @@ |
||||
<?php |
||||
/* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
* This software consists of voluntary contributions made by many individuals |
||||
* and is licensed under the MIT license. For more information, see |
||||
* <http://www.doctrine-project.org>. |
||||
*/ |
||||
|
||||
namespace Doctrine\Common\Cache; |
||||
|
||||
use Riak\Bucket; |
||||
use Riak\Connection; |
||||
use Riak\Input; |
||||
use Riak\Exception; |
||||
use Riak\Object; |
||||
|
||||
/** |
||||
* Riak cache provider. |
||||
* |
||||
* @link www.doctrine-project.org |
||||
* @since 1.1 |
||||
* @author Guilherme Blanco <guilhermeblanco@hotmail.com> |
||||
*/ |
||||
class RiakCache extends CacheProvider |
||||
{ |
||||
const EXPIRES_HEADER = 'X-Riak-Meta-Expires'; |
||||
|
||||
/** |
||||
* @var \Riak\Bucket |
||||
*/ |
||||
private $bucket; |
||||
|
||||
/** |
||||
* Sets the riak bucket instance to use. |
||||
* |
||||
* @param \Riak\Bucket $bucket |
||||
*/ |
||||
public function __construct(Bucket $bucket) |
||||
{ |
||||
$this->bucket = $bucket; |
||||
} |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
protected function doFetch($id) |
||||
{ |
||||
try { |
||||
$response = $this->bucket->get(urlencode($id)); |
||||
|
||||
// No objects found |
||||
if ( ! $response->hasObject()) { |
||||
return false; |
||||
} |
||||
|
||||
// Check for attempted siblings |
||||
$object = ($response->hasSiblings()) |
||||
? $this->resolveConflict($id, $response->getVClock(), $response->getObjectList()) |
||||
: $response->getFirstObject(); |
||||
|
||||
// Check for expired object |
||||
if ($this->isExpired($object)) { |
||||
$this->bucket->delete($object); |
||||
|
||||
return false; |
||||
} |
||||
|
||||
return unserialize($object->getContent()); |
||||
} catch (Exception\RiakException $e) { |
||||
// Covers: |
||||
// - Riak\ConnectionException |
||||
// - Riak\CommunicationException |
||||
// - Riak\UnexpectedResponseException |
||||
// - Riak\NotFoundException |
||||
} |
||||
|
||||
return false; |
||||
} |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
protected function doContains($id) |
||||
{ |
||||
try { |
||||
// We only need the HEAD, not the entire object |
||||
$input = new Input\GetInput(); |
||||
|
||||
$input->setReturnHead(true); |
||||
|
||||
$response = $this->bucket->get(urlencode($id), $input); |
||||
|
||||
// No objects found |
||||
if ( ! $response->hasObject()) { |
||||
return false; |
||||
} |
||||
|
||||
$object = $response->getFirstObject(); |
||||
|
||||
// Check for expired object |
||||
if ($this->isExpired($object)) { |
||||
$this->bucket->delete($object); |
||||
|
||||
return false; |
||||
} |
||||
|
||||
return true; |
||||
} catch (Exception\RiakException $e) { |
||||
// Do nothing |
||||
} |
||||
|
||||
return false; |
||||
} |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
protected function doSave($id, $data, $lifeTime = 0) |
||||
{ |
||||
try { |
||||
$object = new Object(urlencode($id)); |
||||
|
||||
$object->setContent(serialize($data)); |
||||
|
||||
if ($lifeTime > 0) { |
||||
$object->addMetadata(self::EXPIRES_HEADER, (string) (time() + $lifeTime)); |
||||
} |
||||
|
||||
$this->bucket->put($object); |
||||
|
||||
return true; |
||||
} catch (Exception\RiakException $e) { |
||||
// Do nothing |
||||
} |
||||
|
||||
return false; |
||||
} |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
protected function doDelete($id) |
||||
{ |
||||
try { |
||||
$this->bucket->delete(urlencode($id)); |
||||
|
||||
return true; |
||||
} catch (Exception\BadArgumentsException $e) { |
||||
// Key did not exist on cluster already |
||||
} catch (Exception\RiakException $e) { |
||||
// Covers: |
||||
// - Riak\Exception\ConnectionException |
||||
// - Riak\Exception\CommunicationException |
||||
// - Riak\Exception\UnexpectedResponseException |
||||
} |
||||
|
||||
return false; |
||||
} |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
protected function doFlush() |
||||
{ |
||||
try { |
||||
$keyList = $this->bucket->getKeyList(); |
||||
|
||||
foreach ($keyList as $key) { |
||||
$this->bucket->delete($key); |
||||
} |
||||
|
||||
return true; |
||||
} catch (Exception\RiakException $e) { |
||||
// Do nothing |
||||
} |
||||
|
||||
return false; |
||||
} |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
protected function doGetStats() |
||||
{ |
||||
// Only exposed through HTTP stats API, not Protocol Buffers API |
||||
return null; |
||||
} |
||||
|
||||
/** |
||||
* Check if a given Riak Object have expired. |
||||
* |
||||
* @param \Riak\Object $object |
||||
* |
||||
* @return boolean |
||||
*/ |
||||
private function isExpired(Object $object) |
||||
{ |
||||
$metadataMap = $object->getMetadataMap(); |
||||
|
||||
return isset($metadataMap[self::EXPIRES_HEADER]) |
||||
&& $metadataMap[self::EXPIRES_HEADER] < time(); |
||||
} |
||||
|
||||
/** |
||||
* On-read conflict resolution. Applied approach here is last write wins. |
||||
* Specific needs may override this method to apply alternate conflict resolutions. |
||||
* |
||||
* {@internal Riak does not attempt to resolve a write conflict, and store |
||||
* it as sibling of conflicted one. By following this approach, it is up to |
||||
* the next read to resolve the conflict. When this happens, your fetched |
||||
* object will have a list of siblings (read as a list of objects). |
||||
* In our specific case, we do not care about the intermediate ones since |
||||
* they are all the same read from storage, and we do apply a last sibling |
||||
* (last write) wins logic. |
||||
* If by any means our resolution generates another conflict, it'll up to |
||||
* next read to properly solve it.} |
||||
* |
||||
* @param string $id |
||||
* @param string $vClock |
||||
* @param array $objectList |
||||
* |
||||
* @return \Riak\Object |
||||
*/ |
||||
protected function resolveConflict($id, $vClock, array $objectList) |
||||
{ |
||||
// Our approach here is last-write wins |
||||
$winner = $objectList[count($objectList)]; |
||||
|
||||
$putInput = new Input\PutInput(); |
||||
$putInput->setVClock($vClock); |
||||
|
||||
$mergedObject = new Object(urlencode($id)); |
||||
$mergedObject->setContent($winner->getContent()); |
||||
|
||||
$this->bucket->put($mergedObject, $putInput); |
||||
|
||||
return $mergedObject; |
||||
} |
||||
} |
||||
@ -0,0 +1,91 @@ |
||||
<?php |
||||
/* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
* This software consists of voluntary contributions made by many individuals |
||||
* and is licensed under the MIT license. For more information, see |
||||
* <http://www.doctrine-project.org>. |
||||
*/ |
||||
|
||||
namespace Doctrine\Common\Cache; |
||||
|
||||
/** |
||||
* WinCache cache provider. |
||||
* |
||||
* @link www.doctrine-project.org |
||||
* @since 2.2 |
||||
* @author Benjamin Eberlei <kontakt@beberlei.de> |
||||
* @author Guilherme Blanco <guilhermeblanco@hotmail.com> |
||||
* @author Jonathan Wage <jonwage@gmail.com> |
||||
* @author Roman Borschel <roman@code-factory.org> |
||||
* @author David Abdemoulaie <dave@hobodave.com> |
||||
*/ |
||||
class WinCacheCache extends CacheProvider |
||||
{ |
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
protected function doFetch($id) |
||||
{ |
||||
return wincache_ucache_get($id); |
||||
} |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
protected function doContains($id) |
||||
{ |
||||
return wincache_ucache_exists($id); |
||||
} |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
protected function doSave($id, $data, $lifeTime = 0) |
||||
{ |
||||
return (bool) wincache_ucache_set($id, $data, (int) $lifeTime); |
||||
} |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
protected function doDelete($id) |
||||
{ |
||||
return wincache_ucache_delete($id); |
||||
} |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
protected function doFlush() |
||||
{ |
||||
return wincache_ucache_clear(); |
||||
} |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
protected function doGetStats() |
||||
{ |
||||
$info = wincache_ucache_info(); |
||||
$meminfo = wincache_ucache_meminfo(); |
||||
|
||||
return array( |
||||
Cache::STATS_HITS => $info['total_hit_count'], |
||||
Cache::STATS_MISSES => $info['total_miss_count'], |
||||
Cache::STATS_UPTIME => $info['total_cache_uptime'], |
||||
Cache::STATS_MEMORY_USAGE => $meminfo['memory_total'], |
||||
Cache::STATS_MEMORY_AVAILABLE => $meminfo['memory_free'], |
||||
); |
||||
} |
||||
} |
||||
@ -0,0 +1,109 @@ |
||||
<?php |
||||
/* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
* This software consists of voluntary contributions made by many individuals |
||||
* and is licensed under the MIT license. For more information, see |
||||
* <http://www.doctrine-project.org>. |
||||
*/ |
||||
|
||||
namespace Doctrine\Common\Cache; |
||||
|
||||
/** |
||||
* Xcache cache driver. |
||||
* |
||||
* @link www.doctrine-project.org |
||||
* @since 2.0 |
||||
* @author Benjamin Eberlei <kontakt@beberlei.de> |
||||
* @author Guilherme Blanco <guilhermeblanco@hotmail.com> |
||||
* @author Jonathan Wage <jonwage@gmail.com> |
||||
* @author Roman Borschel <roman@code-factory.org> |
||||
* @author David Abdemoulaie <dave@hobodave.com> |
||||
*/ |
||||
class XcacheCache extends CacheProvider |
||||
{ |
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
protected function doFetch($id) |
||||
{ |
||||
return $this->doContains($id) ? unserialize(xcache_get($id)) : false; |
||||
} |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
protected function doContains($id) |
||||
{ |
||||
return xcache_isset($id); |
||||
} |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
protected function doSave($id, $data, $lifeTime = 0) |
||||
{ |
||||
return xcache_set($id, serialize($data), (int) $lifeTime); |
||||
} |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
protected function doDelete($id) |
||||
{ |
||||
return xcache_unset($id); |
||||
} |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
protected function doFlush() |
||||
{ |
||||
$this->checkAuthorization(); |
||||
|
||||
xcache_clear_cache(XC_TYPE_VAR, 0); |
||||
|
||||
return true; |
||||
} |
||||
|
||||
/** |
||||
* Checks that xcache.admin.enable_auth is Off. |
||||
* |
||||
* @return void |
||||
* |
||||
* @throws \BadMethodCallException When xcache.admin.enable_auth is On. |
||||
*/ |
||||
protected function checkAuthorization() |
||||
{ |
||||
if (ini_get('xcache.admin.enable_auth')) { |
||||
throw new \BadMethodCallException('To use all features of \Doctrine\Common\Cache\XcacheCache, you must set "xcache.admin.enable_auth" to "Off" in your php.ini.'); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
protected function doGetStats() |
||||
{ |
||||
$this->checkAuthorization(); |
||||
|
||||
$info = xcache_info(XC_TYPE_VAR, 0); |
||||
return array( |
||||
Cache::STATS_HITS => $info['hits'], |
||||
Cache::STATS_MISSES => $info['misses'], |
||||
Cache::STATS_UPTIME => null, |
||||
Cache::STATS_MEMORY_USAGE => $info['size'], |
||||
Cache::STATS_MEMORY_AVAILABLE => $info['avail'], |
||||
); |
||||
} |
||||
} |
||||
@ -0,0 +1,83 @@ |
||||
<?php |
||||
/* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
* This software consists of voluntary contributions made by many individuals |
||||
* and is licensed under the MIT license. For more information, see |
||||
* <http://www.doctrine-project.org>. |
||||
*/ |
||||
|
||||
namespace Doctrine\Common\Cache; |
||||
|
||||
/** |
||||
* Zend Data Cache cache driver. |
||||
* |
||||
* @link www.doctrine-project.org |
||||
* @since 2.0 |
||||
* @author Ralph Schindler <ralph.schindler@zend.com> |
||||
* @author Guilherme Blanco <guilhermeblanco@hotmail.com> |
||||
*/ |
||||
class ZendDataCache extends CacheProvider |
||||
{ |
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
protected function doFetch($id) |
||||
{ |
||||
return zend_shm_cache_fetch($id); |
||||
} |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
protected function doContains($id) |
||||
{ |
||||
return (false !== zend_shm_cache_fetch($id)); |
||||
} |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
protected function doSave($id, $data, $lifeTime = 0) |
||||
{ |
||||
return zend_shm_cache_store($id, $data, $lifeTime); |
||||
} |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
protected function doDelete($id) |
||||
{ |
||||
return zend_shm_cache_delete($id); |
||||
} |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
protected function doFlush() |
||||
{ |
||||
$namespace = $this->getNamespace(); |
||||
if (empty($namespace)) { |
||||
return zend_shm_cache_clear(); |
||||
} |
||||
return zend_shm_cache_clear($namespace); |
||||
} |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
protected function doGetStats() |
||||
{ |
||||
return null; |
||||
} |
||||
} |
||||
@ -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="false" |
||||
bootstrap="./tests/Doctrine/Tests/TestInit.php" |
||||
> |
||||
<testsuites> |
||||
<testsuite name="Doctrine Cache Test Suite"> |
||||
<directory>./tests/Doctrine/</directory> |
||||
</testsuite> |
||||
</testsuites> |
||||
|
||||
<filter> |
||||
<whitelist> |
||||
<directory>./lib/Doctrine/</directory> |
||||
</whitelist> |
||||
</filter> |
||||
|
||||
<groups> |
||||
<exclude> |
||||
<group>performance</group> |
||||
</exclude> |
||||
</groups> |
||||
</phpunit> |
||||
@ -0,0 +1,20 @@ |
||||
<?php |
||||
|
||||
namespace Doctrine\Tests\Common\Cache; |
||||
|
||||
use Doctrine\Common\Cache\ApcCache; |
||||
|
||||
class ApcCacheTest extends CacheTest |
||||
{ |
||||
public function setUp() |
||||
{ |
||||
if ( ! extension_loaded('apc') || false === @apc_cache_info()) { |
||||
$this->markTestSkipped('The ' . __CLASS__ .' requires the use of APC'); |
||||
} |
||||
} |
||||
|
||||
protected function _getCacheDriver() |
||||
{ |
||||
return new ApcCache(); |
||||
} |
||||
} |
||||
@ -0,0 +1,21 @@ |
||||
<?php |
||||
|
||||
namespace Doctrine\Tests\Common\Cache; |
||||
|
||||
use Doctrine\Common\Cache\ArrayCache; |
||||
|
||||
class ArrayCacheTest extends CacheTest |
||||
{ |
||||
protected function _getCacheDriver() |
||||
{ |
||||
return new ArrayCache(); |
||||
} |
||||
|
||||
public function testGetStats() |
||||
{ |
||||
$cache = $this->_getCacheDriver(); |
||||
$stats = $cache->getStats(); |
||||
|
||||
$this->assertNull($stats); |
||||
} |
||||
} |
||||
@ -0,0 +1,103 @@ |
||||
<?php |
||||
|
||||
namespace Doctrine\Tests\Common\Cache; |
||||
|
||||
use Doctrine\Common\Cache\Cache; |
||||
|
||||
abstract class CacheTest extends \Doctrine\Tests\DoctrineTestCase |
||||
{ |
||||
public function testBasics() |
||||
{ |
||||
$cache = $this->_getCacheDriver(); |
||||
|
||||
// Test save |
||||
$cache->save('test_key', 'testing this out'); |
||||
|
||||
// Test contains to test that save() worked |
||||
$this->assertTrue($cache->contains('test_key')); |
||||
|
||||
// Test fetch |
||||
$this->assertEquals('testing this out', $cache->fetch('test_key')); |
||||
|
||||
// Test delete |
||||
$cache->save('test_key2', 'test2'); |
||||
$cache->delete('test_key2'); |
||||
$this->assertFalse($cache->contains('test_key2')); |
||||
} |
||||
|
||||
public function testObjects() |
||||
{ |
||||
$cache = $this->_getCacheDriver(); |
||||
|
||||
// Fetch/save test with objects (Is cache driver serializes/unserializes objects correctly ?) |
||||
$cache->save('test_object_key', new \ArrayObject()); |
||||
$this->assertTrue($cache->fetch('test_object_key') instanceof \ArrayObject); |
||||
} |
||||
|
||||
public function testDeleteAll() |
||||
{ |
||||
$cache = $this->_getCacheDriver(); |
||||
$cache->save('test_key1', '1'); |
||||
$cache->save('test_key2', '2'); |
||||
$cache->deleteAll(); |
||||
|
||||
$this->assertFalse($cache->contains('test_key1')); |
||||
$this->assertFalse($cache->contains('test_key2')); |
||||
} |
||||
|
||||
public function testFlushAll() |
||||
{ |
||||
$cache = $this->_getCacheDriver(); |
||||
$cache->save('test_key1', '1'); |
||||
$cache->save('test_key2', '2'); |
||||
$cache->flushAll(); |
||||
|
||||
$this->assertFalse($cache->contains('test_key1')); |
||||
$this->assertFalse($cache->contains('test_key2')); |
||||
} |
||||
|
||||
public function testNamespace() |
||||
{ |
||||
$cache = $this->_getCacheDriver(); |
||||
$cache->setNamespace('test_'); |
||||
$cache->save('key1', 'test'); |
||||
|
||||
$this->assertTrue($cache->contains('key1')); |
||||
|
||||
$cache->setNamespace('test2_'); |
||||
|
||||
$this->assertFalse($cache->contains('key1')); |
||||
} |
||||
|
||||
/** |
||||
* @group DCOM-43 |
||||
*/ |
||||
public function testGetStats() |
||||
{ |
||||
$cache = $this->_getCacheDriver(); |
||||
$stats = $cache->getStats(); |
||||
|
||||
$this->assertArrayHasKey(Cache::STATS_HITS, $stats); |
||||
$this->assertArrayHasKey(Cache::STATS_MISSES, $stats); |
||||
$this->assertArrayHasKey(Cache::STATS_UPTIME, $stats); |
||||
$this->assertArrayHasKey(Cache::STATS_MEMORY_USAGE, $stats); |
||||
$this->assertArrayHasKey(Cache::STATS_MEMORY_AVAILABLE, $stats); |
||||
} |
||||
|
||||
/** |
||||
* Make sure that all supported caches return "false" instead of "null" to be compatible |
||||
* with ORM integration. |
||||
*/ |
||||
public function testFalseOnFailedFetch() |
||||
{ |
||||
$cache = $this->_getCacheDriver(); |
||||
$result = $cache->fetch('nonexistent_key'); |
||||
$this->assertFalse($result); |
||||
$this->assertNotNull($result); |
||||
} |
||||
|
||||
/** |
||||
* @return \Doctrine\Common\Cache\CacheProvider |
||||
*/ |
||||
abstract protected function _getCacheDriver(); |
||||
} |
||||
@ -0,0 +1,47 @@ |
||||
<?php |
||||
|
||||
namespace Doctrine\Tests\Common\Cache; |
||||
|
||||
use Couchbase; |
||||
use Doctrine\Common\Cache\CouchbaseCache; |
||||
|
||||
class CouchbaseCacheTest extends CacheTest |
||||
{ |
||||
private $couchbase; |
||||
|
||||
public function setUp() |
||||
{ |
||||
if (extension_loaded('couchbase')) { |
||||
try { |
||||
$this->couchbase = new Couchbase('127.0.0.1', 'Administrator', 'password', 'default'); |
||||
} catch(Exception $ex) { |
||||
$this->markTestSkipped('Could not instantiate the Couchbase cache because of: ' . $ex); |
||||
} |
||||
} else { |
||||
$this->markTestSkipped('The ' . __CLASS__ .' requires the use of the couchbase extension'); |
||||
} |
||||
} |
||||
|
||||
public function testNoExpire() |
||||
{ |
||||
$cache = $this->_getCacheDriver(); |
||||
$cache->save('noexpire', 'value', 0); |
||||
sleep(1); |
||||
$this->assertTrue($cache->contains('noexpire'), 'Couchbase provider should support no-expire'); |
||||
} |
||||
|
||||
public function testLongLifetime() |
||||
{ |
||||
$cache = $this->_getCacheDriver(); |
||||
$cache->save('key', 'value', 30 * 24 * 3600 + 1); |
||||
|
||||
$this->assertTrue($cache->contains('key'), 'Couchbase provider should support TTL > 30 days'); |
||||
} |
||||
|
||||
protected function _getCacheDriver() |
||||
{ |
||||
$driver = new CouchbaseCache(); |
||||
$driver->setCouchbase($this->couchbase); |
||||
return $driver; |
||||
} |
||||
} |
||||
@ -0,0 +1,107 @@ |
||||
<?php |
||||
|
||||
namespace Doctrine\Tests\Common\Cache; |
||||
|
||||
/** |
||||
* @group DCOM-101 |
||||
*/ |
||||
class FileCacheTest extends \Doctrine\Tests\DoctrineTestCase |
||||
{ |
||||
/** |
||||
* @var \Doctrine\Common\Cache\FileCache |
||||
*/ |
||||
private $driver; |
||||
|
||||
protected function setUp() |
||||
{ |
||||
$this->driver = $this->getMock( |
||||
'Doctrine\Common\Cache\FileCache', |
||||
array('doFetch', 'doContains', 'doSave'), |
||||
array(), '', false |
||||
); |
||||
} |
||||
|
||||
public function getProviderFileName() |
||||
{ |
||||
return array( |
||||
//The characters :\/<>"*?| are not valid in Windows filenames. |
||||
array('key:1', 'key1'), |
||||
array('key\2', 'key2'), |
||||
array('key/3', 'key3'), |
||||
array('key<4', 'key4'), |
||||
array('key>5', 'key5'), |
||||
array('key"6', 'key6'), |
||||
array('key*7', 'key7'), |
||||
array('key?8', 'key8'), |
||||
array('key|9', 'key9'), |
||||
array('key[0]','key[0]'), |
||||
); |
||||
} |
||||
|
||||
/** |
||||
* @dataProvider getProviderFileName |
||||
*/ |
||||
public function testInvalidFilename($key, $expected) |
||||
{ |
||||
$cache = $this->driver; |
||||
$method = new \ReflectionMethod($cache, 'getFilename'); |
||||
|
||||
$method->setAccessible(true); |
||||
|
||||
$value = $method->invoke($cache, $key); |
||||
$actual = pathinfo($value, PATHINFO_FILENAME); |
||||
|
||||
$this->assertEquals($expected, $actual); |
||||
} |
||||
|
||||
public function testFilenameCollision() |
||||
{ |
||||
$data['key:0'] = 'key0'; |
||||
$data['key\0'] = 'key0'; |
||||
$data['key/0'] = 'key0'; |
||||
$data['key<0'] = 'key0'; |
||||
$data['key>0'] = 'key0'; |
||||
$data['key"0'] = 'key0'; |
||||
$data['key*0'] = 'key0'; |
||||
$data['key?0'] = 'key0'; |
||||
$data['key|0'] = 'key0'; |
||||
|
||||
$paths = array(); |
||||
$cache = $this->driver; |
||||
$method = new \ReflectionMethod($cache, 'getFilename'); |
||||
|
||||
$method->setAccessible(true); |
||||
|
||||
foreach ($data as $key => $expected) { |
||||
$path = $method->invoke($cache, $key); |
||||
$actual = pathinfo($path, PATHINFO_FILENAME); |
||||
|
||||
$this->assertNotContains($path, $paths); |
||||
$this->assertEquals($expected, $actual); |
||||
|
||||
$paths[] = $path; |
||||
} |
||||
} |
||||
|
||||
public function testFilenameShouldCreateThePathWithFourSubDirectories() |
||||
{ |
||||
$cache = $this->driver; |
||||
$method = new \ReflectionMethod($cache, 'getFilename'); |
||||
$key = 'item-key'; |
||||
$expectedDir[] = '84e0e2e893febb73'; |
||||
$expectedDir[] = '7a0fee0c89d53f4b'; |
||||
$expectedDir[] = 'b7fcb44c57cdf3d3'; |
||||
$expectedDir[] = '2ce7363f5d597760'; |
||||
$expectedDir = implode(DIRECTORY_SEPARATOR, $expectedDir); |
||||
|
||||
$method->setAccessible(true); |
||||
|
||||
$path = $method->invoke($cache, $key); |
||||
$filename = pathinfo($path, PATHINFO_FILENAME); |
||||
$dirname = pathinfo($path, PATHINFO_DIRNAME); |
||||
|
||||
$this->assertEquals('item-key', $filename); |
||||
$this->assertEquals(DIRECTORY_SEPARATOR . $expectedDir, $dirname); |
||||
$this->assertEquals(DIRECTORY_SEPARATOR . $expectedDir . DIRECTORY_SEPARATOR . $key, $path); |
||||
} |
||||
} |
||||
@ -0,0 +1,102 @@ |
||||
<?php |
||||
|
||||
namespace Doctrine\Tests\Common\Cache; |
||||
|
||||
use Doctrine\Common\Cache\Cache; |
||||
use Doctrine\Common\Cache\FilesystemCache; |
||||
|
||||
/** |
||||
* @group DCOM-101 |
||||
*/ |
||||
class FilesystemCacheTest extends CacheTest |
||||
{ |
||||
/** |
||||
* @var \Doctrine\Common\Cache\FilesystemCache |
||||
*/ |
||||
private $driver; |
||||
|
||||
protected function _getCacheDriver() |
||||
{ |
||||
$dir = sys_get_temp_dir() . "/doctrine_cache_". uniqid(); |
||||
$this->assertFalse(is_dir($dir)); |
||||
|
||||
$this->driver = new FilesystemCache($dir); |
||||
$this->assertTrue(is_dir($dir)); |
||||
|
||||
return $this->driver; |
||||
} |
||||
|
||||
public function testLifetime() |
||||
{ |
||||
$cache = $this->_getCacheDriver(); |
||||
|
||||
// Test save |
||||
$cache->save('test_key', 'testing this out', 10); |
||||
|
||||
// Test contains to test that save() worked |
||||
$this->assertTrue($cache->contains('test_key')); |
||||
|
||||
// Test fetch |
||||
$this->assertEquals('testing this out', $cache->fetch('test_key')); |
||||
|
||||
// access private methods |
||||
$getFilename = new \ReflectionMethod($cache, 'getFilename'); |
||||
$getNamespacedId = new \ReflectionMethod($cache, 'getNamespacedId'); |
||||
|
||||
$getFilename->setAccessible(true); |
||||
$getNamespacedId->setAccessible(true); |
||||
|
||||
$id = $getNamespacedId->invoke($cache, 'test_key'); |
||||
$filename = $getFilename->invoke($cache, $id); |
||||
|
||||
$data = ''; |
||||
$lifetime = 0; |
||||
$resource = fopen($filename, "r"); |
||||
|
||||
if (false !== ($line = fgets($resource))) { |
||||
$lifetime = (integer) $line; |
||||
} |
||||
|
||||
while (false !== ($line = fgets($resource))) { |
||||
$data .= $line; |
||||
} |
||||
|
||||
$this->assertNotEquals(0, $lifetime, "previous lifetime could not be loaded"); |
||||
|
||||
// update lifetime |
||||
$lifetime = $lifetime - 20; |
||||
file_put_contents($filename, $lifetime . PHP_EOL . $data); |
||||
|
||||
// test expired data |
||||
$this->assertFalse($cache->contains('test_key')); |
||||
$this->assertFalse($cache->fetch('test_key')); |
||||
} |
||||
|
||||
public function testGetStats() |
||||
{ |
||||
$cache = $this->_getCacheDriver(); |
||||
$stats = $cache->getStats(); |
||||
|
||||
$this->assertNull($stats[Cache::STATS_HITS]); |
||||
$this->assertNull($stats[Cache::STATS_MISSES]); |
||||
$this->assertNull($stats[Cache::STATS_UPTIME]); |
||||
$this->assertEquals(0, $stats[Cache::STATS_MEMORY_USAGE]); |
||||
$this->assertGreaterThan(0, $stats[Cache::STATS_MEMORY_AVAILABLE]); |
||||
} |
||||
|
||||
public function tearDown() |
||||
{ |
||||
$dir = $this->driver->getDirectory(); |
||||
$ext = $this->driver->getExtension(); |
||||
$iterator = new \RecursiveDirectoryIterator($dir); |
||||
|
||||
foreach (new \RecursiveIteratorIterator($iterator, \RecursiveIteratorIterator::CHILD_FIRST) as $file) { |
||||
if ($file->isFile()) { |
||||
@unlink($file->getRealPath()); |
||||
} else { |
||||
@rmdir($file->getRealPath()); |
||||
} |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,45 @@ |
||||
<?php |
||||
|
||||
namespace Doctrine\Tests\Common\Cache; |
||||
|
||||
use Doctrine\Common\Cache\MemcacheCache; |
||||
|
||||
class MemcacheCacheTest extends CacheTest |
||||
{ |
||||
private $_memcache; |
||||
|
||||
public function setUp() |
||||
{ |
||||
if (extension_loaded('memcache')) { |
||||
$this->_memcache = new \Memcache; |
||||
$ok = @$this->_memcache->connect('localhost', 11211); |
||||
if (!$ok) { |
||||
$this->markTestSkipped('The ' . __CLASS__ .' requires the use of memcache'); |
||||
} |
||||
} else { |
||||
$this->markTestSkipped('The ' . __CLASS__ .' requires the use of memcache'); |
||||
} |
||||
} |
||||
|
||||
public function testNoExpire() { |
||||
$cache = $this->_getCacheDriver(); |
||||
$cache->save('noexpire', 'value', 0); |
||||
sleep(1); |
||||
$this->assertTrue($cache->contains('noexpire'), 'Memcache provider should support no-expire'); |
||||
} |
||||
|
||||
public function testLongLifetime() |
||||
{ |
||||
$cache = $this->_getCacheDriver(); |
||||
$cache->save('key', 'value', 30 * 24 * 3600 + 1); |
||||
$this->assertTrue($cache->contains('key'), 'Memcache provider should support TTL > 30 days'); |
||||
} |
||||
|
||||
protected function _getCacheDriver() |
||||
{ |
||||
$driver = new MemcacheCache(); |
||||
$driver->setMemcache($this->_memcache); |
||||
return $driver; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,48 @@ |
||||
<?php |
||||
|
||||
namespace Doctrine\Tests\Common\Cache; |
||||
|
||||
use Doctrine\Common\Cache\MemcachedCache; |
||||
|
||||
class MemcachedCacheTest extends CacheTest |
||||
{ |
||||
private $memcached; |
||||
|
||||
public function setUp() |
||||
{ |
||||
if (extension_loaded('memcached')) { |
||||
$this->memcached = new \Memcached(); |
||||
$this->memcached->setOption(\Memcached::OPT_COMPRESSION, false); |
||||
$this->memcached->addServer('127.0.0.1', 11211); |
||||
|
||||
$fh = @fsockopen('127.0.0.1', 11211); |
||||
if (!$fh) { |
||||
$this->markTestSkipped('The ' . __CLASS__ .' requires the use of memcache'); |
||||
} |
||||
} else { |
||||
$this->markTestSkipped('The ' . __CLASS__ .' requires the use of memcache'); |
||||
} |
||||
} |
||||
|
||||
public function testNoExpire() { |
||||
$cache = $this->_getCacheDriver(); |
||||
$cache->save('noexpire', 'value', 0); |
||||
sleep(1); |
||||
$this->assertTrue($cache->contains('noexpire'), 'Memcache provider should support no-expire'); |
||||
} |
||||
|
||||
public function testLongLifetime() |
||||
{ |
||||
$cache = $this->_getCacheDriver(); |
||||
$cache->save('key', 'value', 30 * 24 * 3600 + 1); |
||||
|
||||
$this->assertTrue($cache->contains('key'), 'Memcached provider should support TTL > 30 days'); |
||||
} |
||||
|
||||
protected function _getCacheDriver() |
||||
{ |
||||
$driver = new MemcachedCache(); |
||||
$driver->setMemcached($this->memcached); |
||||
return $driver; |
||||
} |
||||
} |
||||
@ -0,0 +1,154 @@ |
||||
<?php |
||||
|
||||
namespace Doctrine\Tests\Common\Cache; |
||||
|
||||
use Doctrine\Common\Cache\Cache; |
||||
use Doctrine\Common\Cache\PhpFileCache; |
||||
|
||||
/** |
||||
* @group DCOM-101 |
||||
*/ |
||||
class PhpFileCacheTest extends CacheTest |
||||
{ |
||||
/** |
||||
* @var \Doctrine\Common\Cache\PhpFileCache |
||||
*/ |
||||
private $driver; |
||||
|
||||
protected function _getCacheDriver() |
||||
{ |
||||
$dir = sys_get_temp_dir() . "/doctrine_cache_". uniqid(); |
||||
$this->assertFalse(is_dir($dir)); |
||||
|
||||
$this->driver = new PhpFileCache($dir); |
||||
$this->assertTrue(is_dir($dir)); |
||||
|
||||
return $this->driver; |
||||
} |
||||
|
||||
public function testObjects() |
||||
{ |
||||
$this->markTestSkipped('PhpFileCache does not support saving objects that dont implement __set_state()'); |
||||
} |
||||
|
||||
public function testLifetime() |
||||
{ |
||||
$cache = $this->_getCacheDriver(); |
||||
|
||||
// Test save |
||||
$cache->save('test_key', 'testing this out', 10); |
||||
|
||||
// Test contains to test that save() worked |
||||
$this->assertTrue($cache->contains('test_key')); |
||||
|
||||
// Test fetch |
||||
$this->assertEquals('testing this out', $cache->fetch('test_key')); |
||||
|
||||
// access private methods |
||||
$getFilename = new \ReflectionMethod($cache, 'getFilename'); |
||||
$getNamespacedId = new \ReflectionMethod($cache, 'getNamespacedId'); |
||||
|
||||
$getFilename->setAccessible(true); |
||||
$getNamespacedId->setAccessible(true); |
||||
|
||||
$id = $getNamespacedId->invoke($cache, 'test_key'); |
||||
$path = $getFilename->invoke($cache, $id); |
||||
$value = include $path; |
||||
|
||||
// update lifetime |
||||
$value['lifetime'] = $value['lifetime'] - 20; |
||||
file_put_contents($path, '<?php return unserialize(' . var_export(serialize($value), true) . ');'); |
||||
|
||||
// test expired data |
||||
$this->assertFalse($cache->contains('test_key')); |
||||
$this->assertFalse($cache->fetch('test_key')); |
||||
} |
||||
|
||||
public function testImplementsSetState() |
||||
{ |
||||
$cache = $this->_getCacheDriver(); |
||||
|
||||
// Test save |
||||
$cache->save('test_set_state', new SetStateClass(array(1,2,3))); |
||||
|
||||
//Test __set_state call |
||||
$this->assertCount(0, SetStateClass::$values); |
||||
|
||||
// Test fetch |
||||
$value = $cache->fetch('test_set_state'); |
||||
$this->assertInstanceOf('Doctrine\Tests\Common\Cache\SetStateClass', $value); |
||||
$this->assertEquals(array(1,2,3), $value->getValue()); |
||||
|
||||
//Test __set_state call |
||||
$this->assertCount(1, SetStateClass::$values); |
||||
|
||||
// Test contains |
||||
$this->assertTrue($cache->contains('test_set_state')); |
||||
} |
||||
|
||||
public function testNotImplementsSetState() |
||||
{ |
||||
$cache = $this->_getCacheDriver(); |
||||
|
||||
$this->setExpectedException('InvalidArgumentException'); |
||||
$cache->save('test_not_set_state', new NotSetStateClass(array(1,2,3))); |
||||
} |
||||
|
||||
public function testGetStats() |
||||
{ |
||||
$cache = $this->_getCacheDriver(); |
||||
$stats = $cache->getStats(); |
||||
|
||||
$this->assertNull($stats[Cache::STATS_HITS]); |
||||
$this->assertNull($stats[Cache::STATS_MISSES]); |
||||
$this->assertNull($stats[Cache::STATS_UPTIME]); |
||||
$this->assertEquals(0, $stats[Cache::STATS_MEMORY_USAGE]); |
||||
$this->assertGreaterThan(0, $stats[Cache::STATS_MEMORY_AVAILABLE]); |
||||
} |
||||
|
||||
public function tearDown() |
||||
{ |
||||
if (!$this->driver) { |
||||
return; |
||||
} |
||||
|
||||
$dir = $this->driver->getDirectory(); |
||||
$ext = $this->driver->getExtension(); |
||||
$iterator = new \RecursiveDirectoryIterator($dir); |
||||
|
||||
foreach (new \RecursiveIteratorIterator($iterator, \RecursiveIteratorIterator::CHILD_FIRST) as $file) { |
||||
if ($file->isFile()) { |
||||
@unlink($file->getRealPath()); |
||||
} else { |
||||
@rmdir($file->getRealPath()); |
||||
} |
||||
} |
||||
} |
||||
|
||||
} |
||||
|
||||
class NotSetStateClass |
||||
{ |
||||
private $value; |
||||
|
||||
public function __construct($value) |
||||
{ |
||||
$this->value = $value; |
||||
} |
||||
|
||||
public function getValue() |
||||
{ |
||||
return $this->value; |
||||
} |
||||
} |
||||
|
||||
class SetStateClass extends NotSetStateClass |
||||
{ |
||||
public static $values = array(); |
||||
|
||||
public static function __set_state($data) |
||||
{ |
||||
self::$values = $data; |
||||
return new self($data['value']); |
||||
} |
||||
} |
||||
@ -0,0 +1,30 @@ |
||||
<?php |
||||
|
||||
namespace Doctrine\Tests\Common\Cache; |
||||
|
||||
use Doctrine\Common\Cache\RedisCache; |
||||
|
||||
class RedisCacheTest extends CacheTest |
||||
{ |
||||
private $_redis; |
||||
|
||||
public function setUp() |
||||
{ |
||||
if (extension_loaded('redis')) { |
||||
$this->_redis = new \Redis(); |
||||
$ok = @$this->_redis->connect('127.0.0.1'); |
||||
if (!$ok) { |
||||
$this->markTestSkipped('The ' . __CLASS__ .' requires the use of redis'); |
||||
} |
||||
} else { |
||||
$this->markTestSkipped('The ' . __CLASS__ .' requires the use of redis'); |
||||
} |
||||
} |
||||
|
||||
protected function _getCacheDriver() |
||||
{ |
||||
$driver = new RedisCache(); |
||||
$driver->setRedis($this->_redis); |
||||
return $driver; |
||||
} |
||||
} |
||||
@ -0,0 +1,64 @@ |
||||
<?php |
||||
|
||||
namespace Doctrine\Tests\Common\Cache; |
||||
|
||||
use Riak\Bucket; |
||||
use Riak\Connection; |
||||
use Riak\Exception; |
||||
use Doctrine\Common\Cache\RiakCache; |
||||
|
||||
/** |
||||
* RiakCache test |
||||
* |
||||
* @group Riak |
||||
*/ |
||||
class RiakCacheTest extends CacheTest |
||||
{ |
||||
/** |
||||
* @var \Riak\Connection |
||||
*/ |
||||
private $connection; |
||||
|
||||
/** |
||||
* @var \Riak\Bucket |
||||
*/ |
||||
private $bucket; |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
public function setUp() |
||||
{ |
||||
if ( ! extension_loaded('riak')) { |
||||
$this->markTestSkipped('The ' . __CLASS__ .' requires the use of Riak'); |
||||
} |
||||
|
||||
try { |
||||
$this->connection = new Connection('127.0.0.1', 8087); |
||||
$this->bucket = new Bucket($this->connection, 'test'); |
||||
} catch (Exception\RiakException $e) { |
||||
$this->markTestSkipped('The ' . __CLASS__ .' requires the use of Riak'); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
public function testGetStats() |
||||
{ |
||||
$cache = $this->_getCacheDriver(); |
||||
$stats = $cache->getStats(); |
||||
|
||||
$this->assertNull($stats); |
||||
} |
||||
|
||||
/** |
||||
* Retrieve RiakCache instance. |
||||
* |
||||
* @return \Doctrine\Common\Cache\RiakCache |
||||
*/ |
||||
protected function _getCacheDriver() |
||||
{ |
||||
return new RiakCache($this->bucket); |
||||
} |
||||
} |
||||
@ -0,0 +1,20 @@ |
||||
<?php |
||||
|
||||
namespace Doctrine\Tests\Common\Cache; |
||||
|
||||
use Doctrine\Common\Cache\WincacheCache; |
||||
|
||||
class WincacheCacheTest extends CacheTest |
||||
{ |
||||
public function setUp() |
||||
{ |
||||
if ( ! extension_loaded('wincache') || ! function_exists('wincache_ucache_info')) { |
||||
$this->markTestSkipped('The ' . __CLASS__ .' requires the use of Wincache'); |
||||
} |
||||
} |
||||
|
||||
protected function _getCacheDriver() |
||||
{ |
||||
return new WincacheCache(); |
||||
} |
||||
} |
||||
@ -0,0 +1,20 @@ |
||||
<?php |
||||
|
||||
namespace Doctrine\Tests\Common\Cache; |
||||
|
||||
use Doctrine\Common\Cache\XcacheCache; |
||||
|
||||
class XcacheCacheTest extends CacheTest |
||||
{ |
||||
public function setUp() |
||||
{ |
||||
if ( ! extension_loaded('xcache')) { |
||||
$this->markTestSkipped('The ' . __CLASS__ .' requires the use of xcache'); |
||||
} |
||||
} |
||||
|
||||
protected function _getCacheDriver() |
||||
{ |
||||
return new XcacheCache(); |
||||
} |
||||
} |
||||
@ -0,0 +1,28 @@ |
||||
<?php |
||||
|
||||
namespace Doctrine\Tests\Common\Cache; |
||||
|
||||
use Doctrine\Common\Cache\ZendDataCache; |
||||
|
||||
class ZendDataCacheTest extends CacheTest |
||||
{ |
||||
public function setUp() |
||||
{ |
||||
if (!function_exists('zend_shm_cache_fetch') || (php_sapi_name() != 'apache2handler')) { |
||||
$this->markTestSkipped('The ' . __CLASS__ .' requires the use of Zend Data Cache which only works in apache2handler SAPI'); |
||||
} |
||||
} |
||||
|
||||
public function testGetStats() |
||||
{ |
||||
$cache = $this->_getCacheDriver(); |
||||
$stats = $cache->getStats(); |
||||
|
||||
$this->assertNull($stats); |
||||
} |
||||
|
||||
protected function _getCacheDriver() |
||||
{ |
||||
return new ZendDataCache(); |
||||
} |
||||
} |
||||
@ -0,0 +1,10 @@ |
||||
<?php |
||||
|
||||
namespace Doctrine\Tests; |
||||
|
||||
/** |
||||
* Base testcase class for all Doctrine testcases. |
||||
*/ |
||||
abstract class DoctrineTestCase extends \PHPUnit_Framework_TestCase |
||||
{ |
||||
} |
||||
@ -0,0 +1,23 @@ |
||||
<?php |
||||
/* |
||||
* This file bootstraps the test environment. |
||||
*/ |
||||
namespace Doctrine\Tests; |
||||
|
||||
error_reporting(E_ALL | E_STRICT); |
||||
|
||||
// register silently failing autoloader |
||||
spl_autoload_register(function($class) |
||||
{ |
||||
if (0 === strpos($class, 'Doctrine\Tests\\')) { |
||||
$path = __DIR__.'/../../'.strtr($class, '\\', '/').'.php'; |
||||
if (is_file($path) && is_readable($path)) { |
||||
require_once $path; |
||||
|
||||
return true; |
||||
} |
||||
} |
||||
}); |
||||
|
||||
require_once __DIR__ . "/../../../vendor/autoload.php"; |
||||
|
||||
@ -0,0 +1,11 @@ |
||||
language: php |
||||
|
||||
php: |
||||
- 5.3 |
||||
- 5.4 |
||||
|
||||
before_script: |
||||
- wget http://getcomposer.org/composer.phar |
||||
- php composer.phar install |
||||
|
||||
script: phpunit --coverage-text |
||||
@ -0,0 +1,19 @@ |
||||
Copyright (c) 2011 Igor Wiedler |
||||
|
||||
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. |
||||
@ -0,0 +1,74 @@ |
||||
# Événement |
||||
|
||||
Événement is a very simple event dispatching library for PHP 5.3. |
||||
|
||||
It has the same design goals as [Silex](http://silex-project.org) and |
||||
[Pimple](http://pimple-project.org), to empower the user while staying concise |
||||
and simple. |
||||
|
||||
It is very strongly inspired by the EventEmitter API found in |
||||
[node.js](http://nodejs.org). It includes an implementation of |
||||
[EventEmitter2](https://github.com/hij1nx/EventEmitter2), that extends |
||||
the original EventEmitter. |
||||
|
||||
[](http://travis-ci.org/igorw/evenement) |
||||
|
||||
## Fetch |
||||
|
||||
The recommended way to install Événement is [through composer](http://getcomposer.org). |
||||
|
||||
Just create a composer.json file for your project: |
||||
|
||||
```JSON |
||||
{ |
||||
"require": { |
||||
"evenement/evenement": "dev-master" |
||||
} |
||||
} |
||||
``` |
||||
|
||||
And run these two commands to install it: |
||||
|
||||
$ curl -s http://getcomposer.org/installer | php |
||||
$ php composer.phar install |
||||
|
||||
Now you can add the autoloader, and you will have access to the library: |
||||
|
||||
```php |
||||
<?php |
||||
require 'vendor/autoload.php'; |
||||
``` |
||||
|
||||
## Usage |
||||
|
||||
### Creating an Emitter |
||||
|
||||
```php |
||||
<?php |
||||
$emitter = new Evenement\EventEmitter(); |
||||
``` |
||||
|
||||
### Adding Listeners |
||||
|
||||
```php |
||||
<?php |
||||
$emitter->on('user.create', function (User $user) use ($logger) { |
||||
$logger->log(sprintf("User '%s' was created.", $user->getLogin())); |
||||
}); |
||||
``` |
||||
|
||||
### Emitting Events |
||||
|
||||
```php |
||||
<?php |
||||
$emitter->emit('user.create', array($user)); |
||||
``` |
||||
|
||||
Tests |
||||
----- |
||||
|
||||
$ phpunit |
||||
|
||||
License |
||||
------- |
||||
MIT, see LICENSE. |
||||
@ -0,0 +1,20 @@ |
||||
{ |
||||
"name": "evenement/evenement", |
||||
"description": "Événement is a very simple event dispatching library for PHP 5.3", |
||||
"keywords": ["event-dispatcher"], |
||||
"license": "MIT", |
||||
"authors": [ |
||||
{ |
||||
"name": "Igor Wiedler", |
||||
"email": "igor@wiedler.ch" |
||||
} |
||||
], |
||||
"require": { |
||||
"php": ">=5.3.0" |
||||
}, |
||||
"autoload": { |
||||
"psr-0": { |
||||
"Evenement": "src" |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,25 @@ |
||||
<?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="tests/bootstrap.php" |
||||
> |
||||
<testsuites> |
||||
<testsuite name="Evenement Test Suite"> |
||||
<directory>./tests/Evenement/</directory> |
||||
</testsuite> |
||||
</testsuites> |
||||
|
||||
<filter> |
||||
<whitelist> |
||||
<directory>./src/</directory> |
||||
</whitelist> |
||||
</filter> |
||||
</phpunit> |
||||
@ -0,0 +1,73 @@ |
||||
<?php |
||||
|
||||
/* |
||||
* This file is part of Evenement. |
||||
* |
||||
* (c) Igor Wiedler <igor@wiedler.ch> |
||||
* |
||||
* For the full copyright and license information, please view the LICENSE |
||||
* file that was distributed with this source code. |
||||
*/ |
||||
|
||||
namespace Evenement; |
||||
|
||||
class EventEmitter implements EventEmitterInterface |
||||
{ |
||||
protected $listeners = array(); |
||||
|
||||
public function on($event, $listener) |
||||
{ |
||||
if (!is_callable($listener)) { |
||||
throw new \InvalidArgumentException('The provided listener was not a valid callable.'); |
||||
} |
||||
|
||||
if (!isset($this->listeners[$event])) { |
||||
$this->listeners[$event] = array(); |
||||
} |
||||
|
||||
$this->listeners[$event][] = $listener; |
||||
} |
||||
|
||||
public function once($event, $listener) |
||||
{ |
||||
$that = $this; |
||||
|
||||
$onceListener = function () use ($that, &$onceListener, $event, $listener) { |
||||
$that->removeListener($event, $onceListener); |
||||
|
||||
call_user_func_array($listener, func_get_args()); |
||||
}; |
||||
|
||||
$this->on($event, $onceListener); |
||||
} |
||||
|
||||
public function removeListener($event, $listener) |
||||
{ |
||||
if (isset($this->listeners[$event])) { |
||||
if (false !== $index = array_search($listener, $this->listeners[$event], true)) { |
||||
unset($this->listeners[$event][$index]); |
||||
} |
||||
} |
||||
} |
||||
|
||||
public function removeAllListeners($event = null) |
||||
{ |
||||
if ($event !== null) { |
||||
unset($this->listeners[$event]); |
||||
} else { |
||||
$this->listeners = array(); |
||||
} |
||||
} |
||||
|
||||
public function listeners($event) |
||||
{ |
||||
return isset($this->listeners[$event]) ? $this->listeners[$event] : array(); |
||||
} |
||||
|
||||
public function emit($event, array $arguments = array()) |
||||
{ |
||||
foreach ($this->listeners($event) as $listener) { |
||||
call_user_func_array($listener, $arguments); |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,114 @@ |
||||
<?php |
||||
|
||||
/* |
||||
* This file is part of Evenement. |
||||
* |
||||
* (c) Igor Wiedler <igor@wiedler.ch> |
||||
* |
||||
* For the full copyright and license information, please view the LICENSE |
||||
* file that was distributed with this source code. |
||||
*/ |
||||
|
||||
namespace Evenement; |
||||
|
||||
class EventEmitter2 extends EventEmitter |
||||
{ |
||||
protected $options; |
||||
protected $anyListeners = array(); |
||||
|
||||
public function __construct(array $options = array()) |
||||
{ |
||||
$this->options = array_merge(array( |
||||
'delimiter' => '.', |
||||
), $options); |
||||
} |
||||
|
||||
public function onAny($listener) |
||||
{ |
||||
$this->anyListeners[] = $listener; |
||||
} |
||||
|
||||
public function offAny($listener) |
||||
{ |
||||
if (false !== $index = array_search($listener, $this->anyListeners, true)) { |
||||
unset($this->anyListeners[$index]); |
||||
} |
||||
} |
||||
|
||||
public function many($event, $timesToListen, $listener) |
||||
{ |
||||
$that = $this; |
||||
|
||||
$timesListened = 0; |
||||
|
||||
if ($timesToListen == 0) { |
||||
return; |
||||
} |
||||
|
||||
if ($timesToListen < 0) { |
||||
throw new \OutOfRangeException('You cannot listen less than zero times.'); |
||||
} |
||||
|
||||
$manyListener = function () use ($that, &$timesListened, &$manyListener, $event, $timesToListen, $listener) { |
||||
if (++$timesListened == $timesToListen) { |
||||
$that->removeListener($event, $manyListener); |
||||
} |
||||
|
||||
call_user_func_array($listener, func_get_args()); |
||||
}; |
||||
|
||||
$this->on($event, $manyListener); |
||||
} |
||||
|
||||
public function emit($event, array $arguments = array()) |
||||
{ |
||||
foreach ($this->anyListeners as $listener) { |
||||
call_user_func_array($listener, $arguments); |
||||
} |
||||
|
||||
parent::emit($event, $arguments); |
||||
} |
||||
|
||||
public function listeners($event) |
||||
{ |
||||
$matchedListeners = array(); |
||||
|
||||
foreach ($this->listeners as $name => $listeners) { |
||||
foreach ($listeners as $listener) { |
||||
if ($this->matchEventName($event, $name)) { |
||||
$matchedListeners[] = $listener; |
||||
} |
||||
} |
||||
} |
||||
|
||||
return $matchedListeners; |
||||
} |
||||
|
||||
protected function matchEventName($matchPattern, $eventName) |
||||
{ |
||||
$patternParts = explode($this->options['delimiter'], $matchPattern); |
||||
$nameParts = explode($this->options['delimiter'], $eventName); |
||||
|
||||
if (count($patternParts) != count($nameParts)) { |
||||
return false; |
||||
} |
||||
|
||||
$size = min(count($patternParts), count($nameParts)); |
||||
for ($i = 0; $i < $size; $i++) { |
||||
$patternPart = $patternParts[$i]; |
||||
$namePart = $nameParts[$i]; |
||||
|
||||
if ('*' === $patternPart || '*' === $namePart) { |
||||
continue; |
||||
} |
||||
|
||||
if ($namePart === $patternPart) { |
||||
continue; |
||||
} |
||||
|
||||
return false; |
||||
} |
||||
|
||||
return true; |
||||
} |
||||
} |
||||
@ -0,0 +1,22 @@ |
||||
<?php |
||||
|
||||
/* |
||||
* This file is part of Evenement. |
||||
* |
||||
* (c) Igor Wiedler <igor@wiedler.ch> |
||||
* |
||||
* For the full copyright and license information, please view the LICENSE |
||||
* file that was distributed with this source code. |
||||
*/ |
||||
|
||||
namespace Evenement; |
||||
|
||||
interface EventEmitterInterface |
||||
{ |
||||
public function on($event, $listener); |
||||
public function once($event, $listener); |
||||
public function removeListener($event, $listener); |
||||
public function removeAllListeners($event = null); |
||||
public function listeners($event); |
||||
public function emit($event, array $arguments = array()); |
||||
} |
||||
@ -0,0 +1,160 @@ |
||||
<?php |
||||
|
||||
/* |
||||
* This file is part of Evenement. |
||||
* |
||||
* Copyright (c) 2011 Igor Wiedler |
||||
* |
||||
* 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. |
||||
*/ |
||||
|
||||
namespace Evenement\Tests; |
||||
|
||||
use Evenement\EventEmitter2; |
||||
|
||||
class EventEmitter2Test extends \PHPUnit_Framework_TestCase |
||||
{ |
||||
private $emitter; |
||||
|
||||
public function setUp() |
||||
{ |
||||
$this->emitter = new EventEmitter2(); |
||||
} |
||||
|
||||
// matching tests from |
||||
// test/wildcardEvents/addListener.js |
||||
|
||||
public function testWildcardMatching7() |
||||
{ |
||||
$listenerCalled = 0; |
||||
|
||||
$listener = function () use (&$listenerCalled) { |
||||
$listenerCalled++; |
||||
}; |
||||
|
||||
$this->emitter->on('*.test', $listener); |
||||
$this->emitter->on('*.*', $listener); |
||||
$this->emitter->on('*', $listener); |
||||
|
||||
$this->emitter->emit('other.emit'); |
||||
$this->emitter->emit('foo.test'); |
||||
|
||||
$this->assertSame(3, $listenerCalled); |
||||
} |
||||
|
||||
public function testWildcardMatching8() |
||||
{ |
||||
$listenerCalled = 0; |
||||
|
||||
$listener = function () use (&$listenerCalled) { |
||||
$listenerCalled++; |
||||
}; |
||||
|
||||
$this->emitter->on('foo.test', $listener); |
||||
$this->emitter->on('*.*', $listener); |
||||
$this->emitter->on('*', $listener); |
||||
|
||||
$this->emitter->emit('*.*'); |
||||
$this->emitter->emit('foo.test'); |
||||
$this->emitter->emit('*'); |
||||
|
||||
$this->assertSame(5, $listenerCalled); |
||||
} |
||||
|
||||
public function testOnAny() |
||||
{ |
||||
$this->emitter->onAny(function () {}); |
||||
} |
||||
|
||||
public function testOnAnyWithEmit() |
||||
{ |
||||
$listenerCalled = 0; |
||||
|
||||
$this->emitter->onAny(function () use (&$listenerCalled) { |
||||
$listenerCalled++; |
||||
}); |
||||
|
||||
$this->assertSame(0, $listenerCalled); |
||||
|
||||
$this->emitter->emit('foo'); |
||||
|
||||
$this->assertSame(1, $listenerCalled); |
||||
|
||||
$this->emitter->emit('bar'); |
||||
|
||||
$this->assertSame(2, $listenerCalled); |
||||
} |
||||
|
||||
public function testoffAnyWithEmit() |
||||
{ |
||||
$listenerCalled = 0; |
||||
|
||||
$listener = function () use (&$listenerCalled) { |
||||
$listenerCalled++; |
||||
}; |
||||
|
||||
$this->emitter->onAny($listener); |
||||
$this->emitter->offAny($listener); |
||||
|
||||
$this->assertSame(0, $listenerCalled); |
||||
$this->emitter->emit('foo'); |
||||
$this->assertSame(0, $listenerCalled); |
||||
} |
||||
|
||||
/** |
||||
* @dataProvider provideMany |
||||
*/ |
||||
public function testMany($amount) |
||||
{ |
||||
$listenerCalled = 0; |
||||
|
||||
$this->emitter->many('foo', $amount, function () use (&$listenerCalled) { |
||||
$listenerCalled++; |
||||
}); |
||||
|
||||
for ($i = 0; $i < $amount; $i++) { |
||||
$this->assertSame($i, $listenerCalled); |
||||
$this->emitter->emit('foo'); |
||||
} |
||||
|
||||
$this->emitter->emit('foo'); |
||||
$this->assertSame($amount, $listenerCalled); |
||||
} |
||||
|
||||
public function provideMany() |
||||
{ |
||||
return array( |
||||
array(0), |
||||
array(1), |
||||
array(2), |
||||
array(3), |
||||
array(4), |
||||
array(400), |
||||
); |
||||
} |
||||
|
||||
/** |
||||
* @expectedException OutOfRangeException |
||||
*/ |
||||
public function testManyWithLessThanZeroTtl() |
||||
{ |
||||
$this->emitter->many('foo', -1, function () {}); |
||||
$this->emitter->emit('foo'); |
||||
} |
||||
} |
||||
@ -0,0 +1,236 @@ |
||||
<?php |
||||
|
||||
/* |
||||
* This file is part of Evenement. |
||||
* |
||||
* Copyright (c) 2011 Igor Wiedler |
||||
* |
||||
* 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. |
||||
*/ |
||||
|
||||
namespace Evenement\Tests; |
||||
|
||||
use Evenement\EventEmitter; |
||||
|
||||
class EventEmitterTest extends \PHPUnit_Framework_TestCase |
||||
{ |
||||
private $emitter; |
||||
|
||||
public function setUp() |
||||
{ |
||||
$this->emitter = new EventEmitter(); |
||||
} |
||||
|
||||
public function testAddListenerWithLambda() |
||||
{ |
||||
$this->emitter->on('foo', function () {}); |
||||
} |
||||
|
||||
public function testAddListenerWithMethod() |
||||
{ |
||||
$listener = new Listener(); |
||||
$this->emitter->on('foo', array($listener, 'onFoo')); |
||||
} |
||||
|
||||
public function testAddListenerWithStaticMethod() |
||||
{ |
||||
$this->emitter->on('bar', array('Evenement\Tests\Listener', 'onBar')); |
||||
} |
||||
|
||||
/** |
||||
* @expectedException InvalidArgumentException |
||||
*/ |
||||
public function testAddListenerWithInvalidListener() |
||||
{ |
||||
$this->emitter->on('foo', 'not a callable'); |
||||
} |
||||
|
||||
public function testOnce() |
||||
{ |
||||
$listenerCalled = 0; |
||||
|
||||
$this->emitter->once('foo', function () use (&$listenerCalled) { |
||||
$listenerCalled++; |
||||
}); |
||||
|
||||
$this->assertSame(0, $listenerCalled); |
||||
|
||||
$this->emitter->emit('foo'); |
||||
|
||||
$this->assertSame(1, $listenerCalled); |
||||
|
||||
$this->emitter->emit('foo'); |
||||
|
||||
$this->assertSame(1, $listenerCalled); |
||||
} |
||||
|
||||
public function testEmitWithoutArguments() |
||||
{ |
||||
$listenerCalled = false; |
||||
|
||||
$this->emitter->on('foo', function () use (&$listenerCalled) { |
||||
$listenerCalled = true; |
||||
}); |
||||
|
||||
$this->assertSame(false, $listenerCalled); |
||||
$this->emitter->emit('foo'); |
||||
$this->assertSame(true, $listenerCalled); |
||||
} |
||||
|
||||
public function testEmitWithOneArgument() |
||||
{ |
||||
$test = $this; |
||||
|
||||
$listenerCalled = false; |
||||
|
||||
$this->emitter->on('foo', function ($value) use (&$listenerCalled, $test) { |
||||
$listenerCalled = true; |
||||
|
||||
$test->assertSame('bar', $value); |
||||
}); |
||||
|
||||
$this->assertSame(false, $listenerCalled); |
||||
$this->emitter->emit('foo', array('bar')); |
||||
$this->assertSame(true, $listenerCalled); |
||||
} |
||||
|
||||
public function testEmitWithTwoArguments() |
||||
{ |
||||
$test = $this; |
||||
|
||||
$listenerCalled = false; |
||||
|
||||
$this->emitter->on('foo', function ($arg1, $arg2) use (&$listenerCalled, $test) { |
||||
$listenerCalled = true; |
||||
|
||||
$test->assertSame('bar', $arg1); |
||||
$test->assertSame('baz', $arg2); |
||||
}); |
||||
|
||||
$this->assertSame(false, $listenerCalled); |
||||
$this->emitter->emit('foo', array('bar', 'baz')); |
||||
$this->assertSame(true, $listenerCalled); |
||||
} |
||||
|
||||
public function testEmitWithNoListeners() |
||||
{ |
||||
$this->emitter->emit('foo'); |
||||
$this->emitter->emit('foo', array('bar')); |
||||
$this->emitter->emit('foo', array('bar', 'baz')); |
||||
} |
||||
|
||||
public function testEmitWithTwoListeners() |
||||
{ |
||||
$listenersCalled = 0; |
||||
|
||||
$this->emitter->on('foo', function () use (&$listenersCalled) { |
||||
$listenersCalled++; |
||||
}); |
||||
|
||||
$this->emitter->on('foo', function () use (&$listenersCalled) { |
||||
$listenersCalled++; |
||||
}); |
||||
|
||||
$this->assertSame(0, $listenersCalled); |
||||
$this->emitter->emit('foo'); |
||||
$this->assertSame(2, $listenersCalled); |
||||
} |
||||
|
||||
public function testRemoveListenerMatching() |
||||
{ |
||||
$listenersCalled = 0; |
||||
|
||||
$listener = function () use (&$listenersCalled) { |
||||
$listenersCalled++; |
||||
}; |
||||
|
||||
$this->emitter->on('foo', $listener); |
||||
$this->emitter->removeListener('foo', $listener); |
||||
|
||||
$this->assertSame(0, $listenersCalled); |
||||
$this->emitter->emit('foo'); |
||||
$this->assertSame(0, $listenersCalled); |
||||
} |
||||
|
||||
public function testRemoveListenerNotMatching() |
||||
{ |
||||
$listenersCalled = 0; |
||||
|
||||
$listener = function () use (&$listenersCalled) { |
||||
$listenersCalled++; |
||||
}; |
||||
|
||||
$this->emitter->on('foo', $listener); |
||||
$this->emitter->removeListener('bar', $listener); |
||||
|
||||
$this->assertSame(0, $listenersCalled); |
||||
$this->emitter->emit('foo'); |
||||
$this->assertSame(1, $listenersCalled); |
||||
} |
||||
|
||||
public function testRemoveAllListenersMatching() |
||||
{ |
||||
$listenersCalled = 0; |
||||
|
||||
$this->emitter->on('foo', function () use (&$listenersCalled) { |
||||
$listenersCalled++; |
||||
}); |
||||
|
||||
$this->emitter->removeAllListeners('foo'); |
||||
|
||||
$this->assertSame(0, $listenersCalled); |
||||
$this->emitter->emit('foo'); |
||||
$this->assertSame(0, $listenersCalled); |
||||
} |
||||
|
||||
public function testRemoveAllListenersNotMatching() |
||||
{ |
||||
$listenersCalled = 0; |
||||
|
||||
$this->emitter->on('foo', function () use (&$listenersCalled) { |
||||
$listenersCalled++; |
||||
}); |
||||
|
||||
$this->emitter->removeAllListeners('bar'); |
||||
|
||||
$this->assertSame(0, $listenersCalled); |
||||
$this->emitter->emit('foo'); |
||||
$this->assertSame(1, $listenersCalled); |
||||
} |
||||
|
||||
public function testRemoveAllListenersWithoutArguments() |
||||
{ |
||||
$listenersCalled = 0; |
||||
|
||||
$this->emitter->on('foo', function () use (&$listenersCalled) { |
||||
$listenersCalled++; |
||||
}); |
||||
|
||||
$this->emitter->on('bar', function () use (&$listenersCalled) { |
||||
$listenersCalled++; |
||||
}); |
||||
|
||||
$this->emitter->removeAllListeners(); |
||||
|
||||
$this->assertSame(0, $listenersCalled); |
||||
$this->emitter->emit('foo'); |
||||
$this->emitter->emit('bar'); |
||||
$this->assertSame(0, $listenersCalled); |
||||
} |
||||
} |
||||
@ -0,0 +1,38 @@ |
||||
<?php |
||||
|
||||
/* |
||||
* This file is part of Evenement. |
||||
* |
||||
* Copyright (c) 2011 Igor Wiedler |
||||
* |
||||
* 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. |
||||
*/ |
||||
|
||||
namespace Evenement\Tests; |
||||
|
||||
class Listener |
||||
{ |
||||
public function onFoo() |
||||
{ |
||||
} |
||||
|
||||
public static function onBar() |
||||
{ |
||||
} |
||||
} |
||||
@ -0,0 +1,28 @@ |
||||
<?php |
||||
|
||||
/* |
||||
* This file is part of Evenement. |
||||
* |
||||
* Copyright (c) 2011 Igor Wiedler |
||||
* |
||||
* 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. |
||||
*/ |
||||
|
||||
$loader = require __DIR__.'/../vendor/autoload.php'; |
||||
$loader->add('Evenement\Tests', __DIR__); |
||||
@ -0,0 +1,110 @@ |
||||
### 1.6.0 (2013-07-29) |
||||
|
||||
* Added HipChatHandler to send logs to a HipChat chat room |
||||
* Added ErrorLogHandler to send logs to PHP's error_log function |
||||
* Added NewRelicHandler to send logs to NewRelic's service |
||||
* Added Monolog\ErrorHandler helper class to register a Logger as exception/error/fatal handler |
||||
* Added ChannelLevelActivationStrategy for the FingersCrossedHandler to customize levels by channel |
||||
* Added stack traces output when normalizing exceptions (json output & co) |
||||
* Added Monolog\Logger::API constant (currently 1) |
||||
* Added support for ChromePHP's v4.0 extension |
||||
* Added support for message priorities in PushoverHandler, see $highPriorityLevel and $emergencyLevel |
||||
* Added support for sending messages to multiple users at once with the PushoverHandler |
||||
* Fixed RavenHandler's support for batch sending of messages (when behind a Buffer or FingersCrossedHandler) |
||||
* Fixed normalization of Traversables with very large data sets, only the first 1000 items are shown now |
||||
* Fixed issue in RotatingFileHandler when an open_basedir restriction is active |
||||
* Fixed minor issues in RavenHandler and bumped the API to Raven 0.5.0 |
||||
* Fixed SyslogHandler issue when many were used concurrently with different facilities |
||||
|
||||
### 1.5.0 (2013-04-23) |
||||
|
||||
* Added ProcessIdProcessor to inject the PID in log records |
||||
* Added UidProcessor to inject a unique identifier to all log records of one request/run |
||||
* Added support for previous exceptions in the LineFormatter exception serialization |
||||
* Added Monolog\Logger::getLevels() to get all available levels |
||||
* Fixed ChromePHPHandler so it avoids sending headers larger than Chrome can handle |
||||
|
||||
### 1.4.1 (2013-04-01) |
||||
|
||||
* Fixed exception formatting in the LineFormatter to be more minimalistic |
||||
* Fixed RavenHandler's handling of context/extra data, requires Raven client >0.1.0 |
||||
* Fixed log rotation in RotatingFileHandler to work with long running scripts spanning multiple days |
||||
* Fixed WebProcessor array access so it checks for data presence |
||||
* Fixed Buffer, Group and FingersCrossed handlers to make use of their processors |
||||
|
||||
### 1.4.0 (2013-02-13) |
||||
|
||||
* Added RedisHandler to log to Redis via the Predis library or the phpredis extension |
||||
* Added ZendMonitorHandler to log to the Zend Server monitor |
||||
* Added the possibility to pass arrays of handlers and processors directly in the Logger constructor |
||||
* Added `$useSSL` option to the PushoverHandler which is enabled by default |
||||
* Fixed ChromePHPHandler and FirePHPHandler issue when multiple instances are used simultaneously |
||||
* Fixed header injection capability in the NativeMailHandler |
||||
|
||||
### 1.3.1 (2013-01-11) |
||||
|
||||
* Fixed LogstashFormatter to be usable with stream handlers |
||||
* Fixed GelfMessageFormatter levels on Windows |
||||
|
||||
### 1.3.0 (2013-01-08) |
||||
|
||||
* Added PSR-3 compliance, the `Monolog\Logger` class is now an instance of `Psr\Log\LoggerInterface` |
||||
* Added PsrLogMessageProcessor that you can selectively enable for full PSR-3 compliance |
||||
* Added LogstashFormatter (combine with SocketHandler or StreamHandler to send logs to Logstash) |
||||
* Added PushoverHandler to send mobile notifications |
||||
* Added CouchDBHandler and DoctrineCouchDBHandler |
||||
* Added RavenHandler to send data to Sentry servers |
||||
* Added support for the new MongoClient class in MongoDBHandler |
||||
* Added microsecond precision to log records' timestamps |
||||
* Added `$flushOnOverflow` param to BufferHandler to flush by batches instead of losing |
||||
the oldest entries |
||||
* Fixed normalization of objects with cyclic references |
||||
|
||||
### 1.2.1 (2012-08-29) |
||||
|
||||
* Added new $logopts arg to SyslogHandler to provide custom openlog options |
||||
* Fixed fatal error in SyslogHandler |
||||
|
||||
### 1.2.0 (2012-08-18) |
||||
|
||||
* Added AmqpHandler (for use with AMQP servers) |
||||
* Added CubeHandler |
||||
* Added NativeMailerHandler::addHeader() to send custom headers in mails |
||||
* Added the possibility to specify more than one recipient in NativeMailerHandler |
||||
* Added the possibility to specify float timeouts in SocketHandler |
||||
* Added NOTICE and EMERGENCY levels to conform with RFC 5424 |
||||
* Fixed the log records to use the php default timezone instead of UTC |
||||
* Fixed BufferHandler not being flushed properly on PHP fatal errors |
||||
* Fixed normalization of exotic resource types |
||||
* Fixed the default format of the SyslogHandler to avoid duplicating datetimes in syslog |
||||
|
||||
### 1.1.0 (2012-04-23) |
||||
|
||||
* Added Monolog\Logger::isHandling() to check if a handler will |
||||
handle the given log level |
||||
* Added ChromePHPHandler |
||||
* Added MongoDBHandler |
||||
* Added GelfHandler (for use with Graylog2 servers) |
||||
* Added SocketHandler (for use with syslog-ng for example) |
||||
* Added NormalizerFormatter |
||||
* Added the possibility to change the activation strategy of the FingersCrossedHandler |
||||
* Added possibility to show microseconds in logs |
||||
* Added `server` and `referer` to WebProcessor output |
||||
|
||||
### 1.0.2 (2011-10-24) |
||||
|
||||
* Fixed bug in IE with large response headers and FirePHPHandler |
||||
|
||||
### 1.0.1 (2011-08-25) |
||||
|
||||
* Added MemoryPeakUsageProcessor and MemoryUsageProcessor |
||||
* Added Monolog\Logger::getName() to get a logger's channel name |
||||
|
||||
### 1.0.0 (2011-07-06) |
||||
|
||||
* Added IntrospectionProcessor to get info from where the logger was called |
||||
* Fixed WebProcessor in CLI |
||||
|
||||
### 1.0.0-RC1 (2011-07-01) |
||||
|
||||
* Initial release |
||||
@ -0,0 +1,19 @@ |
||||
Copyright (c) Jordi Boggiano |
||||
|
||||
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. |
||||
@ -0,0 +1,248 @@ |
||||
Monolog - Logging for PHP 5.3+ [](http://travis-ci.org/Seldaek/monolog) |
||||
============================== |
||||
|
||||
[](https://packagist.org/packages/monolog/monolog) |
||||
[](https://packagist.org/packages/monolog/monolog) |
||||
|
||||
|
||||
Monolog sends your logs to files, sockets, inboxes, databases and various |
||||
web services. See the complete list of handlers below. Special handlers |
||||
allow you to build advanced logging strategies. |
||||
|
||||
This library implements the [PSR-3](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md) |
||||
interface that you can type-hint against in your own libraries to keep |
||||
a maximum of interoperability. You can also use it in your applications to |
||||
make sure you can always use another compatible logger at a later time. |
||||
|
||||
Usage |
||||
----- |
||||
|
||||
```php |
||||
<?php |
||||
|
||||
use Monolog\Logger; |
||||
use Monolog\Handler\StreamHandler; |
||||
|
||||
// create a log channel |
||||
$log = new Logger('name'); |
||||
$log->pushHandler(new StreamHandler('path/to/your.log', Logger::WARNING)); |
||||
|
||||
// add records to the log |
||||
$log->addWarning('Foo'); |
||||
$log->addError('Bar'); |
||||
``` |
||||
|
||||
Core Concepts |
||||
------------- |
||||
|
||||
Every `Logger` instance has a channel (name) and a stack of handlers. Whenever |
||||
you add a record to the logger, it traverses the handler stack. Each handler |
||||
decides whether it handled fully the record, and if so, the propagation of the |
||||
record ends there. |
||||
|
||||
This allows for flexible logging setups, for example having a `StreamHandler` at |
||||
the bottom of the stack that will log anything to disk, and on top of that add |
||||
a `MailHandler` that will send emails only when an error message is logged. |
||||
Handlers also have a `$bubble` property which defines whether they block the |
||||
record or not if they handled it. In this example, setting the `MailHandler`'s |
||||
`$bubble` argument to true means that all records will propagate to the |
||||
`StreamHandler`, even the errors that are handled by the `MailHandler`. |
||||
|
||||
You can create many `Logger`s, each defining a channel (e.g.: db, request, |
||||
router, ..) and each of them combining various handlers, which can be shared |
||||
or not. The channel is reflected in the logs and allows you to easily see or |
||||
filter records. |
||||
|
||||
Each Handler also has a Formatter, a default one with settings that make sense |
||||
will be created if you don't set one. The formatters normalize and format |
||||
incoming records so that they can be used by the handlers to output useful |
||||
information. |
||||
|
||||
Custom severity levels are not available. Only the eight |
||||
[RFC 5424](http://tools.ietf.org/html/rfc5424) levels (debug, info, notice, |
||||
warning, error, critical, alert, emergency) are present for basic filtering |
||||
purposes, but for sorting and other use cases that would require |
||||
flexibility, you should add Processors to the Logger that can add extra |
||||
information (tags, user ip, ..) to the records before they are handled. |
||||
|
||||
Log Levels |
||||
---------- |
||||
|
||||
Monolog supports all 8 logging levels defined in |
||||
[RFC 5424](http://tools.ietf.org/html/rfc5424), but unless you specifically |
||||
need syslog compatibility, it is advised to only use DEBUG, INFO, WARNING, |
||||
ERROR, CRITICAL, ALERT. |
||||
|
||||
- **DEBUG** (100): Detailed debug information. |
||||
|
||||
- **INFO** (200): Interesting events. Examples: User logs in, SQL logs. |
||||
|
||||
- NOTICE (250): Normal but significant events. |
||||
|
||||
- **WARNING** (300): Exceptional occurrences that are not errors. Examples: |
||||
Use of deprecated APIs, poor use of an API, undesirable things that are not |
||||
necessarily wrong. |
||||
|
||||
- **ERROR** (400): Runtime errors that do not require immediate action but |
||||
should typically be logged and monitored. |
||||
|
||||
- **CRITICAL** (500): Critical conditions. Example: Application component |
||||
unavailable, unexpected exception. |
||||
|
||||
- **ALERT** (550): Action must be taken immediately. Example: Entire website |
||||
down, database unavailable, etc. This should trigger the SMS alerts and wake |
||||
you up. |
||||
|
||||
- EMERGENCY (600): Emergency: system is unusable. |
||||
|
||||
Docs |
||||
==== |
||||
|
||||
**See the `doc` directory for more detailed documentation. |
||||
The following is only a list of all parts that come with Monolog.** |
||||
|
||||
Handlers |
||||
-------- |
||||
|
||||
### Log to files and syslog |
||||
|
||||
- _StreamHandler_: Logs records into any PHP stream, use this for log files. |
||||
- _RotatingFileHandler_: Logs records to a file and creates one logfile per day. |
||||
It will also delete files older than `$maxFiles`. You should use |
||||
[logrotate](http://linuxcommand.org/man_pages/logrotate8.html) for high profile |
||||
setups though, this is just meant as a quick and dirty solution. |
||||
- _SyslogHandler_: Logs records to the syslog. |
||||
- _ErrorLogHandler_: Logs records to PHP's |
||||
[`error_log()`](http://docs.php.net/manual/en/function.error-log.php) function. |
||||
|
||||
### Send alerts and emails |
||||
|
||||
- _NativeMailerHandler_: Sends emails using PHP's |
||||
[`mail()`](http://php.net/manual/en/function.mail.php) function. |
||||
- _SwiftMailerHandler_: Sends emails using a [`Swift_Mailer`](http://swiftmailer.org/) instance. |
||||
- _PushoverHandler_: Sends mobile notifications via the [Pushover](https://www.pushover.net/) API. |
||||
- _HipChatHandler_: Logs records to a [HipChat](http://hipchat.com) chat room using its API. |
||||
|
||||
### Log specific servers and networked logging |
||||
|
||||
- _SocketHandler_: Logs records to [sockets](http://php.net/fsockopen), use this |
||||
for UNIX and TCP sockets. See an [example](https://github.com/Seldaek/monolog/blob/master/doc/sockets.md). |
||||
- _AmqpHandler_: Logs records to an [amqp](http://www.amqp.org/) compatible |
||||
server. Requires the [php-amqp](http://pecl.php.net/package/amqp) extension (1.0+). |
||||
- _GelfHandler_: Logs records to a [Graylog2](http://www.graylog2.org) server. |
||||
- _CubeHandler_: Logs records to a [Cube](http://square.github.com/cube/) server. |
||||
- _RavenHandler_: Logs records to a [Sentry](http://getsentry.com/) server using |
||||
[raven](https://packagist.org/packages/raven/raven). |
||||
- _ZendMonitorHandler_: Logs records to the Zend Monitor present in Zend Server. |
||||
- _NewRelicHandler_: Logs records to a [NewRelic](http://newrelic.com/) application. |
||||
|
||||
### Logging in development |
||||
|
||||
- _FirePHPHandler_: Handler for [FirePHP](http://www.firephp.org/), providing |
||||
inline `console` messages within [FireBug](http://getfirebug.com/). |
||||
- _ChromePHPHandler_: Handler for [ChromePHP](http://www.chromephp.com/), providing |
||||
inline `console` messages within Chrome. |
||||
|
||||
### Log to databases |
||||
|
||||
- _RedisHandler_: Logs records to a [redis](http://redis.io) server. |
||||
- _MongoDBHandler_: Handler to write records in MongoDB via a |
||||
[Mongo](http://pecl.php.net/package/mongo) extension connection. |
||||
- _CouchDBHandler_: Logs records to a CouchDB server. |
||||
- _DoctrineCouchDBHandler_: Logs records to a CouchDB server via the Doctrine CouchDB ODM. |
||||
|
||||
### Wrappers / Special Handlers |
||||
|
||||
- _FingersCrossedHandler_: A very interesting wrapper. It takes a logger as |
||||
parameter and will accumulate log records of all levels until a record |
||||
exceeds the defined severity level. At which point it delivers all records, |
||||
including those of lower severity, to the handler it wraps. This means that |
||||
until an error actually happens you will not see anything in your logs, but |
||||
when it happens you will have the full information, including debug and info |
||||
records. This provides you with all the information you need, but only when |
||||
you need it. |
||||
- _NullHandler_: Any record it can handle will be thrown away. This can be used |
||||
to put on top of an existing handler stack to disable it temporarily. |
||||
- _BufferHandler_: This handler will buffer all the log records it receives |
||||
until `close()` is called at which point it will call `handleBatch()` on the |
||||
handler it wraps with all the log messages at once. This is very useful to |
||||
send an email with all records at once for example instead of having one mail |
||||
for every log record. |
||||
- _GroupHandler_: This handler groups other handlers. Every record received is |
||||
sent to all the handlers it is configured with. |
||||
- _TestHandler_: Used for testing, it records everything that is sent to it and |
||||
has accessors to read out the information. |
||||
|
||||
Formatters |
||||
---------- |
||||
|
||||
- _LineFormatter_: Formats a log record into a one-line string. |
||||
- _NormalizerFormatter_: Normalizes objects/resources down to strings so a record can easily be serialized/encoded. |
||||
- _JsonFormatter_: Encodes a log record into json. |
||||
- _WildfireFormatter_: Used to format log records into the Wildfire/FirePHP protocol, only useful for the FirePHPHandler. |
||||
- _ChromePHPFormatter_: Used to format log records into the ChromePHP format, only useful for the ChromePHPHandler. |
||||
- _GelfFormatter_: Used to format log records into Gelf message instances, only useful for the GelfHandler. |
||||
- _LogstashFormatter_: Used to format log records into [logstash](http://logstash.net/) event json, useful for any handler listed under inputs [here](http://logstash.net/docs/1.1.5/). |
||||
|
||||
Processors |
||||
---------- |
||||
|
||||
- _IntrospectionProcessor_: Adds the line/file/class/method from which the log call originated. |
||||
- _WebProcessor_: Adds the current request URI, request method and client IP to a log record. |
||||
- _MemoryUsageProcessor_: Adds the current memory usage to a log record. |
||||
- _MemoryPeakUsageProcessor_: Adds the peak memory usage to a log record. |
||||
- _ProcessIdProcessor_: Adds the process id to a log record. |
||||
- _UidProcessor_: Adds a unique identifier to a log record. |
||||
|
||||
Utilities |
||||
--------- |
||||
|
||||
- _ErrorHandler_: The `Monolog\ErrorHandler` class allows you to easily register |
||||
a Logger instance as an exception handler, error handler or fatal error handler. |
||||
- _ErrorLevelActivationStrategy_: Activates a FingersCrossedHandler when a certain log |
||||
level is reached. |
||||
- _ChannelLevelActivationStrategy_: Activates a FingersCrossedHandler when a certain |
||||
log level is reached, depending on which channel received the log record. |
||||
|
||||
About |
||||
===== |
||||
|
||||
Requirements |
||||
------------ |
||||
|
||||
- Any flavor of PHP 5.3 or above should do |
||||
- [optional] PHPUnit 3.5+ to execute the test suite (phpunit --version) |
||||
|
||||
Submitting bugs and feature requests |
||||
------------------------------------ |
||||
|
||||
Bugs and feature request are tracked on [GitHub](https://github.com/Seldaek/monolog/issues) |
||||
|
||||
Frameworks Integration |
||||
---------------------- |
||||
|
||||
- Frameworks and libraries using [PSR-3](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md) |
||||
can be used very easily with Monolog since it implements the interface. |
||||
- [Symfony2](http://symfony.com) comes out of the box with Monolog. |
||||
- [Silex](http://silex.sensiolabs.org/) comes out of the box with Monolog. |
||||
- [Laravel4](http://laravel.com/) comes out of the box with Monolog. |
||||
- [PPI](http://www.ppi.io/) comes out of the box with Monolog. |
||||
- [CakePHP](http://cakephp.org/) is usable with Monolog via the [cakephp-monolog](https://github.com/jadb/cakephp-monolog) plugin. |
||||
- [Slim](http://www.slimframework.com/) is usable with Monolog via the [Slim-Monolog](https://github.com/Flynsarmy/Slim-Monolog) log writer. |
||||
|
||||
Author |
||||
------ |
||||
|
||||
Jordi Boggiano - <j.boggiano@seld.be> - <http://twitter.com/seldaek><br /> |
||||
See also the list of [contributors](https://github.com/Seldaek/monolog/contributors) which participated in this project. |
||||
|
||||
License |
||||
------- |
||||
|
||||
Monolog is licensed under the MIT License - see the `LICENSE` file for details |
||||
|
||||
Acknowledgements |
||||
---------------- |
||||
|
||||
This library is heavily inspired by Python's [Logbook](http://packages.python.org/Logbook/) |
||||
library, although most concepts have been adjusted to fit to the PHP world. |
||||
@ -0,0 +1,39 @@ |
||||
{ |
||||
"name": "monolog/monolog", |
||||
"description": "Sends your logs to files, sockets, inboxes, databases and various web services", |
||||
"keywords": ["log", "logging", "psr-3"], |
||||
"homepage": "http://github.com/Seldaek/monolog", |
||||
"type": "library", |
||||
"license": "MIT", |
||||
"authors": [ |
||||
{ |
||||
"name": "Jordi Boggiano", |
||||
"email": "j.boggiano@seld.be", |
||||
"homepage": "http://seld.be" |
||||
} |
||||
], |
||||
"require": { |
||||
"php": ">=5.3.0", |
||||
"psr/log": "~1.0" |
||||
}, |
||||
"require-dev": { |
||||
"mlehner/gelf-php": "1.0.*", |
||||
"raven/raven": "0.5.*", |
||||
"doctrine/couchdb": "dev-master" |
||||
}, |
||||
"suggest": { |
||||
"mlehner/gelf-php": "Allow sending log messages to a GrayLog2 server", |
||||
"raven/raven": "Allow sending log messages to a Sentry server", |
||||
"doctrine/couchdb": "Allow sending log messages to a CouchDB server", |
||||
"ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", |
||||
"ext-mongo": "Allow sending log messages to a MongoDB server" |
||||
}, |
||||
"autoload": { |
||||
"psr-0": {"Monolog": "src/"} |
||||
}, |
||||
"extra": { |
||||
"branch-alias": { |
||||
"dev-master": "1.6.x-dev" |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,76 @@ |
||||
Extending Monolog |
||||
================= |
||||
|
||||
Monolog is fully extensible, allowing you to adapt your logger to your needs. |
||||
|
||||
Writing your own handler |
||||
------------------------ |
||||
|
||||
Monolog provides many built-in handlers. But if the one you need does not |
||||
exist, you can write it and use it in your logger. The only requirement is |
||||
to implement `Monolog\Handler\HandlerInterface`. |
||||
|
||||
Let's write a PDOHandler to log records to a database. We will extend the |
||||
abstract class provided by Monolog to keep things DRY. |
||||
|
||||
```php |
||||
<?php |
||||
|
||||
use Monolog\Logger; |
||||
use Monolog\Handler\AbstractProcessingHandler; |
||||
|
||||
class PDOHandler extends AbstractProcessingHandler |
||||
{ |
||||
private $initialized = false; |
||||
private $pdo; |
||||
private $statement; |
||||
|
||||
public function __construct(PDO $pdo, $level = Logger::DEBUG, $bubble = true) |
||||
{ |
||||
$this->pdo = $pdo; |
||||
parent::__construct($level, $bubble); |
||||
} |
||||
|
||||
protected function write(array $record) |
||||
{ |
||||
if (!$this->initialized) { |
||||
$this->initialize(); |
||||
} |
||||
|
||||
$this->statement->execute(array( |
||||
'channel' => $record['channel'], |
||||
'level' => $record['level'], |
||||
'message' => $record['formatted'], |
||||
'time' => $record['datetime']->format('U'), |
||||
)); |
||||
} |
||||
|
||||
private function initialize() |
||||
{ |
||||
$this->pdo->exec( |
||||
'CREATE TABLE IF NOT EXISTS monolog ' |
||||
.'(channel VARCHAR(255), level INTEGER, message LONGTEXT, time INTEGER UNSIGNED)' |
||||
); |
||||
$this->statement = $this->pdo->prepare( |
||||
'INSERT INTO monolog (channel, level, message, time) VALUES (:channel, :level, :message, :time)' |
||||
); |
||||
|
||||
$this->initialized = true; |
||||
} |
||||
} |
||||
``` |
||||
|
||||
You can now use this handler in your logger: |
||||
|
||||
```php |
||||
<?php |
||||
|
||||
$logger->pushHandler(new PDOHandler(new PDO('sqlite:logs.sqlite')); |
||||
|
||||
// You can now use your logger |
||||
$logger->addInfo('My logger is now ready'); |
||||
``` |
||||
|
||||
The `Monolog\Handler\AbstractProcessingHandler` class provides most of the |
||||
logic needed for the handler, including the use of processors and the formatting |
||||
of the record (which is why we use ``$record['formatted']`` instead of ``$record['message']``). |
||||
@ -0,0 +1,37 @@ |
||||
Sockets Handler |
||||
=============== |
||||
|
||||
This handler allows you to write your logs to sockets using [fsockopen](http://php.net/fsockopen) |
||||
or [pfsockopen](http://php.net/pfsockopen). |
||||
|
||||
Persistent sockets are mainly useful in web environments where you gain some performance not closing/opening |
||||
the connections between requests. |
||||
|
||||
Basic Example |
||||
------------- |
||||
|
||||
```php |
||||
<?php |
||||
|
||||
use Monolog\Logger; |
||||
use Monolog\Handler\SocketHandler; |
||||
|
||||
// Create the logger |
||||
$logger = new Logger('my_logger'); |
||||
|
||||
// Create the handler |
||||
$handler = new SocketHandler('unix:///var/log/httpd_app_log.socket'); |
||||
$handler->setPersistent(true); |
||||
|
||||
// Now add the handler |
||||
$logger->pushHandler($handler, Logger::DEBUG); |
||||
|
||||
// You can now use your logger |
||||
$logger->addInfo('My logger is now ready'); |
||||
|
||||
``` |
||||
|
||||
In this example, using syslog-ng, you should see the log on the log server: |
||||
|
||||
cweb1 [2012-02-26 00:12:03] my_logger.INFO: My logger is now ready [] [] |
||||
|
||||
@ -0,0 +1,158 @@ |
||||
Using Monolog |
||||
============= |
||||
|
||||
Installation |
||||
------------ |
||||
|
||||
Monolog is available on Packagist ([monolog/monolog](http://packagist.org/packages/monolog/monolog)) |
||||
and as such installable via [Composer](http://getcomposer.org/). |
||||
|
||||
If you do not use Composer, you can grab the code from GitHub, and use any |
||||
PSR-0 compatible autoloader (e.g. the [Symfony2 ClassLoader component](https://github.com/symfony/ClassLoader)) |
||||
to load Monolog classes. |
||||
|
||||
Configuring a logger |
||||
-------------------- |
||||
|
||||
Here is a basic setup to log to a file and to firephp on the DEBUG level: |
||||
|
||||
```php |
||||
<?php |
||||
|
||||
use Monolog\Logger; |
||||
use Monolog\Handler\StreamHandler; |
||||
use Monolog\Handler\FirePHPHandler; |
||||
|
||||
// Create the logger |
||||
$logger = new Logger('my_logger'); |
||||
// Now add some handlers |
||||
$logger->pushHandler(new StreamHandler(__DIR__.'/my_app.log', Logger::DEBUG)); |
||||
$logger->pushHandler(new FirePHPHandler()); |
||||
|
||||
// You can now use your logger |
||||
$logger->addInfo('My logger is now ready'); |
||||
``` |
||||
|
||||
Let's explain it. The first step is to create the logger instance which will |
||||
be used in your code. The argument is a channel name, which is useful when |
||||
you use several loggers (see below for more details about it). |
||||
|
||||
The logger itself does not know how to handle a record. It delegates it to |
||||
some handlers. The code above registers two handlers in the stack to allow |
||||
handling records in two different ways. |
||||
|
||||
Note that the FirePHPHandler is called first as it is added on top of the |
||||
stack. This allows you to temporarily add a logger with bubbling disabled if |
||||
you want to override other configured loggers. |
||||
|
||||
Adding extra data in the records |
||||
-------------------------------- |
||||
|
||||
Monolog provides two different ways to add extra informations along the simple |
||||
textual message. |
||||
|
||||
### Using the logging context |
||||
|
||||
The first way is the context, allowing to pass an array of data along the |
||||
record: |
||||
|
||||
```php |
||||
<?php |
||||
|
||||
$logger->addInfo('Adding a new user', array('username' => 'Seldaek')); |
||||
``` |
||||
|
||||
Simple handlers (like the StreamHandler for instance) will simply format |
||||
the array to a string but richer handlers can take advantage of the context |
||||
(FirePHP is able to display arrays in pretty way for instance). |
||||
|
||||
### Using processors |
||||
|
||||
The second way is to add extra data for all records by using a processor. |
||||
Processors can be any callable. They will get the record as parameter and |
||||
must return it after having eventually changed the `extra` part of it. Let's |
||||
write a processor adding some dummy data in the record: |
||||
|
||||
```php |
||||
<?php |
||||
|
||||
$logger->pushProcessor(function ($record) { |
||||
$record['extra']['dummy'] = 'Hello world!'; |
||||
|
||||
return $record; |
||||
}); |
||||
``` |
||||
|
||||
Monolog provides some built-in processors that can be used in your project. |
||||
Look at the README file for the list. |
||||
|
||||
> Tip: processors can also be registered on a specific handler instead of |
||||
the logger to apply only for this handler. |
||||
|
||||
Leveraging channels |
||||
------------------- |
||||
|
||||
Channels are a great way to identify to which part of the application a record |
||||
is related. This is useful in big applications (and is leveraged by |
||||
MonologBundle in Symfony2). |
||||
|
||||
Picture two loggers sharing a handler that writes to a single log file. |
||||
Channels would allow you to identify the logger that issued every record. |
||||
You can easily grep through the log files filtering this or that channel. |
||||
|
||||
```php |
||||
<?php |
||||
|
||||
use Monolog\Logger; |
||||
use Monolog\Handler\StreamHandler; |
||||
use Monolog\Handler\FirePHPHandler; |
||||
|
||||
// Create some handlers |
||||
$stream = new StreamHandler(__DIR__.'/my_app.log', Logger::DEBUG); |
||||
$firephp = new FirePHPHandler(); |
||||
|
||||
// Create the main logger of the app |
||||
$logger = new Logger('my_logger'); |
||||
$logger->pushHandler($stream); |
||||
$logger->pushHandler($firephp); |
||||
|
||||
// Create a logger for the security-related stuff with a different channel |
||||
$securityLogger = new Logger('security'); |
||||
$securityLogger->pushHandler($stream); |
||||
$securityLogger->pushHandler($firephp); |
||||
``` |
||||
|
||||
Customizing log format |
||||
---------------------- |
||||
|
||||
In Monolog it's easy to customize the format of the logs written into files, |
||||
sockets, mails, databases and other handlers. Most of the handlers use the |
||||
|
||||
```php |
||||
$record['formatted'] |
||||
``` |
||||
|
||||
value to be automatically put into the log device. This value depends on the |
||||
formatter settings. You can choose between predefined formatter classes or |
||||
write your own (e.g. a multiline text file for human-readable output). |
||||
|
||||
To configure a predefined formatter class, just set it as the handler's field: |
||||
|
||||
```php |
||||
// the default date format is "Y-m-d H:i:s" |
||||
$dateFormat = "Y n j, g:i a"; |
||||
// the default output format is "[%datetime%] %channel%.%level_name%: %message% %context% %extra%\n" |
||||
$output = "%datetime% > %level_name% > %message% %context% %extra%\n"; |
||||
// finally, create a formatter |
||||
$formatter = new LineFormatter($output, $dateFormat); |
||||
|
||||
// Create a handler |
||||
$stream = new StreamHandler(__DIR__.'/my_app.log', Logger::DEBUG); |
||||
$stream->setFormatter($formatter); |
||||
// bind it to a logger object |
||||
$securityLogger = new Logger('security'); |
||||
$securityLogger->pushHandler($stream); |
||||
``` |
||||
|
||||
You may also reuse the same formatter between multiple handlers and share those |
||||
handlers between multiple loggers. |
||||
@ -0,0 +1,15 @@ |
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
||||
<phpunit bootstrap="tests/bootstrap.php" colors="true"> |
||||
<testsuites> |
||||
<testsuite name="Monolog Test Suite"> |
||||
<directory>tests/Monolog/</directory> |
||||
</testsuite> |
||||
</testsuites> |
||||
|
||||
<filter> |
||||
<whitelist> |
||||
<directory suffix=".php">src/Monolog/</directory> |
||||
</whitelist> |
||||
</filter> |
||||
</phpunit> |
||||
@ -0,0 +1,209 @@ |
||||
<?php |
||||
|
||||
/* |
||||
* This file is part of the Monolog package. |
||||
* |
||||
* (c) Jordi Boggiano <j.boggiano@seld.be> |
||||
* |
||||
* For the full copyright and license information, please view the LICENSE |
||||
* file that was distributed with this source code. |
||||
*/ |
||||
|
||||
namespace Monolog; |
||||
|
||||
use Psr\Log\LoggerInterface; |
||||
use Psr\Log\LogLevel; |
||||
|
||||
/** |
||||
* Monolog error handler |
||||
* |
||||
* A facility to enable logging of runtime errors, exceptions and fatal errors. |
||||
* |
||||
* Quick setup: <code>ErrorHandler::register($logger);</code> |
||||
* |
||||
* @author Jordi Boggiano <j.boggiano@seld.be> |
||||
*/ |
||||
class ErrorHandler |
||||
{ |
||||
private $logger; |
||||
|
||||
private $previousExceptionHandler; |
||||
private $uncaughtExceptionLevel; |
||||
|
||||
private $previousErrorHandler; |
||||
private $errorLevelMap; |
||||
|
||||
private $fatalLevel; |
||||
private $reservedMemory; |
||||
private static $fatalErrors = array(E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR); |
||||
|
||||
public function __construct(LoggerInterface $logger) |
||||
{ |
||||
$this->logger = $logger; |
||||
} |
||||
|
||||
/** |
||||
* Registers a new ErrorHandler for a given Logger |
||||
* |
||||
* By default it will handle errors, exceptions and fatal errors |
||||
* |
||||
* @param LoggerInterface $logger |
||||
* @param array|false $errorLevelMap an array of E_* constant to LogLevel::* constant mapping, or false to disable error handling |
||||
* @param int|false $exceptionLevel a LogLevel::* constant, or false to disable exception handling |
||||
* @param int|false $fatalLevel a LogLevel::* constant, or false to disable fatal error handling |
||||
* @return ErrorHandler |
||||
*/ |
||||
public static function register(LoggerInterface $logger, $errorLevelMap = array(), $exceptionLevel = null, $fatalLevel = null) |
||||
{ |
||||
$handler = new static($logger); |
||||
if ($errorLevelMap !== false) { |
||||
$handler->registerErrorHandler($errorLevelMap); |
||||
} |
||||
if ($exceptionLevel !== false) { |
||||
$handler->registerExceptionHandler($exceptionLevel); |
||||
} |
||||
if ($fatalLevel !== false) { |
||||
$handler->registerFatalHandler($fatalLevel); |
||||
} |
||||
|
||||
return $handler; |
||||
} |
||||
|
||||
public function registerExceptionHandler($level = null, $callPrevious = true) |
||||
{ |
||||
$prev = set_exception_handler(array($this, 'handleException')); |
||||
$this->uncaughtExceptionLevel = $level === null ? LogLevel::ERROR : $level; |
||||
if ($callPrevious && $prev) { |
||||
$this->previousExceptionHandler = $prev; |
||||
} |
||||
} |
||||
|
||||
public function registerErrorHandler(array $levelMap = array(), $callPrevious = true, $errorTypes = -1) |
||||
{ |
||||
$prev = set_error_handler(array($this, 'handleError'), $errorTypes); |
||||
$this->errorLevelMap = $this->defaultErrorLevelMap(); |
||||
// merging the map into the defaults by hand because array_merge |
||||
// trips up on numeric keys |
||||
foreach ($levelMap as $key => $val) { |
||||
$this->errorLevelMap[$key] = $val; |
||||
} |
||||
if ($callPrevious) { |
||||
$this->previousErrorHandler = $prev ?: true; |
||||
} |
||||
} |
||||
|
||||
public function registerFatalHandler($level = null, $reservedMemorySize = 20) |
||||
{ |
||||
register_shutdown_function(array($this, 'handleFatalError')); |
||||
|
||||
$this->reservedMemory = str_repeat(' ', 1024 * $reservedMemorySize); |
||||
$this->fatalLevel = $level === null ? LogLevel::ALERT : $level; |
||||
} |
||||
|
||||
protected function defaultErrorLevelMap() |
||||
{ |
||||
return array( |
||||
E_ERROR => LogLevel::CRITICAL, |
||||
E_WARNING => LogLevel::WARNING, |
||||
E_PARSE => LogLevel::ALERT, |
||||
E_NOTICE => LogLevel::NOTICE, |
||||
E_CORE_ERROR => LogLevel::CRITICAL, |
||||
E_CORE_WARNING => LogLevel::WARNING, |
||||
E_COMPILE_ERROR => LogLevel::ALERT, |
||||
E_COMPILE_WARNING => LogLevel::WARNING, |
||||
E_USER_ERROR => LogLevel::ERROR, |
||||
E_USER_WARNING => LogLevel::WARNING, |
||||
E_USER_NOTICE => LogLevel::NOTICE, |
||||
E_STRICT => LogLevel::NOTICE, |
||||
E_RECOVERABLE_ERROR => LogLevel::ERROR, |
||||
E_DEPRECATED => LogLevel::NOTICE, |
||||
E_USER_DEPRECATED => LogLevel::NOTICE, |
||||
); |
||||
} |
||||
|
||||
/** |
||||
* @private |
||||
*/ |
||||
public function handleException(\Exception $e) |
||||
{ |
||||
$this->logger->log($this->uncaughtExceptionLevel, 'Uncaught exception', array('exception' => $e)); |
||||
|
||||
if ($this->previousExceptionHandler) { |
||||
call_user_func($this->previousExceptionHandler, $e); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* @private |
||||
*/ |
||||
public function handleError($code, $message, $file = '', $line = 0, $context = array()) |
||||
{ |
||||
if (!(error_reporting() & $code)) { |
||||
return; |
||||
} |
||||
|
||||
$level = isset($this->errorLevelMap[$code]) ? $this->errorLevelMap[$code] : LogLevel::CRITICAL; |
||||
$this->logger->log($level, self::codeToString($code).': '.$message, array('file' => $file, 'line' => $line)); |
||||
|
||||
if ($this->previousErrorHandler === true) { |
||||
return false; |
||||
} elseif ($this->previousErrorHandler) { |
||||
return call_user_func($this->previousErrorHandler, $code, $message, $file, $line, $context); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* @private |
||||
*/ |
||||
public function handleFatalError() |
||||
{ |
||||
$this->reservedMemory = null; |
||||
|
||||
$lastError = error_get_last(); |
||||
if ($lastError && in_array($lastError['type'], self::$fatalErrors)) { |
||||
$this->logger->log( |
||||
$this->fatalLevel, |
||||
'Fatal Error ('.self::codeToString($lastError['type']).'): '.$lastError['message'], |
||||
array('file' => $lastError['file'], 'line' => $lastError['line']) |
||||
); |
||||
} |
||||
} |
||||
|
||||
private static function codeToString($code) |
||||
{ |
||||
switch ($code) { |
||||
case E_ERROR: |
||||
return 'E_ERROR'; |
||||
case E_WARNING: |
||||
return 'E_WARNING'; |
||||
case E_PARSE: |
||||
return 'E_PARSE'; |
||||
case E_NOTICE: |
||||
return 'E_NOTICE'; |
||||
case E_CORE_ERROR: |
||||
return 'E_CORE_ERROR'; |
||||
case E_CORE_WARNING: |
||||
return 'E_CORE_WARNING'; |
||||
case E_COMPILE_ERROR: |
||||
return 'E_COMPILE_ERROR'; |
||||
case E_COMPILE_WARNING: |
||||
return 'E_COMPILE_WARNING'; |
||||
case E_USER_ERROR: |
||||
return 'E_USER_ERROR'; |
||||
case E_USER_WARNING: |
||||
return 'E_USER_WARNING'; |
||||
case E_USER_NOTICE: |
||||
return 'E_USER_NOTICE'; |
||||
case E_STRICT: |
||||
return 'E_STRICT'; |
||||
case E_RECOVERABLE_ERROR: |
||||
return 'E_RECOVERABLE_ERROR'; |
||||
case E_DEPRECATED: |
||||
return 'E_DEPRECATED'; |
||||
case E_USER_DEPRECATED: |
||||
return 'E_USER_DEPRECATED'; |
||||
} |
||||
|
||||
return 'Unknown PHP error'; |
||||
} |
||||
} |
||||
@ -0,0 +1,79 @@ |
||||
<?php |
||||
|
||||
/* |
||||
* This file is part of the Monolog package. |
||||
* |
||||
* (c) Jordi Boggiano <j.boggiano@seld.be> |
||||
* |
||||
* For the full copyright and license information, please view the LICENSE |
||||
* file that was distributed with this source code. |
||||
*/ |
||||
|
||||
namespace Monolog\Formatter; |
||||
|
||||
use Monolog\Logger; |
||||
|
||||
/** |
||||
* Formats a log message according to the ChromePHP array format |
||||
* |
||||
* @author Christophe Coevoet <stof@notk.org> |
||||
*/ |
||||
class ChromePHPFormatter implements FormatterInterface |
||||
{ |
||||
/** |
||||
* Translates Monolog log levels to Wildfire levels. |
||||
*/ |
||||
private $logLevels = array( |
||||
Logger::DEBUG => 'log', |
||||
Logger::INFO => 'info', |
||||
Logger::NOTICE => 'info', |
||||
Logger::WARNING => 'warn', |
||||
Logger::ERROR => 'error', |
||||
Logger::CRITICAL => 'error', |
||||
Logger::ALERT => 'error', |
||||
Logger::EMERGENCY => 'error', |
||||
); |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
public function format(array $record) |
||||
{ |
||||
// Retrieve the line and file if set and remove them from the formatted extra |
||||
$backtrace = 'unknown'; |
||||
if (isset($record['extra']['file']) && isset($record['extra']['line'])) { |
||||
$backtrace = $record['extra']['file'].' : '.$record['extra']['line']; |
||||
unset($record['extra']['file']); |
||||
unset($record['extra']['line']); |
||||
} |
||||
|
||||
$message = array('message' => $record['message']); |
||||
if ($record['context']) { |
||||
$message['context'] = $record['context']; |
||||
} |
||||
if ($record['extra']) { |
||||
$message['extra'] = $record['extra']; |
||||
} |
||||
if (count($message) === 1) { |
||||
$message = reset($message); |
||||
} |
||||
|
||||
return array( |
||||
$record['channel'], |
||||
$message, |
||||
$backtrace, |
||||
$this->logLevels[$record['level']], |
||||
); |
||||
} |
||||
|
||||
public function formatBatch(array $records) |
||||
{ |
||||
$formatted = array(); |
||||
|
||||
foreach ($records as $record) { |
||||
$formatted[] = $this->format($record); |
||||
} |
||||
|
||||
return $formatted; |
||||
} |
||||
} |
||||
@ -0,0 +1,36 @@ |
||||
<?php |
||||
|
||||
/* |
||||
* This file is part of the Monolog package. |
||||
* |
||||
* (c) Jordi Boggiano <j.boggiano@seld.be> |
||||
* |
||||
* For the full copyright and license information, please view the LICENSE |
||||
* file that was distributed with this source code. |
||||
*/ |
||||
|
||||
namespace Monolog\Formatter; |
||||
|
||||
/** |
||||
* Interface for formatters |
||||
* |
||||
* @author Jordi Boggiano <j.boggiano@seld.be> |
||||
*/ |
||||
interface FormatterInterface |
||||
{ |
||||
/** |
||||
* Formats a log record. |
||||
* |
||||
* @param array $record A record to format |
||||
* @return mixed The formatted record |
||||
*/ |
||||
public function format(array $record); |
||||
|
||||
/** |
||||
* Formats a set of log records. |
||||
* |
||||
* @param array $records A set of records to format |
||||
* @return mixed The formatted set of records |
||||
*/ |
||||
public function formatBatch(array $records); |
||||
} |
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue