├── .gitignore ├── CHANGELOG.md ├── phpunit.xml.dist ├── .travis.yml ├── src ├── PartialAllocationPromise.php ├── Allocation.php ├── Pool.php ├── AllocationPromise.php └── PoolInternal.php ├── composer.json ├── LICENSE ├── README.md ├── tests └── FunctionalTest.php └── composer.lock /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor/ 2 | /nbproject/ 3 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | All notable changes to this project will be documented in this file. 3 | This project adheres to [Semantic Versioning](http://semver.org/). 4 | 5 | ## Unreleased 6 | ### Added 7 | - `Pool`, `Allocation` and `AllocationPromise` classes 8 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | tests 6 | 7 | 8 | 9 | 10 | 11 | src 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 5.4 5 | - 5.5 6 | - 5.6 7 | - 7.0 8 | - hhvm 9 | - hhvm-nightly 10 | 11 | matrix: 12 | fast_finish: true 13 | allow_failures: 14 | - php: hhvm-nightly 15 | 16 | sudo: false 17 | 18 | before_install: 19 | - if [[ -n $GITHUB_TOKEN ]]; then composer config -g github-oauth.github.com $GITHUB_TOKEN; fi 20 | - composer self-update 21 | 22 | install: composer install --no-interaction 23 | 24 | script: php vendor/bin/phpunit --coverage-clover build/logs/clover.xml 25 | 26 | cache: 27 | directories: vendor 28 | 29 | after_success: if [[ ! $TRAVIS_PHP_VERSION = "hhvm" ]]; then bash <(curl -s https://codecov.io/bash); fi; 30 | -------------------------------------------------------------------------------- /src/PartialAllocationPromise.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * @api 8 | */ 9 | class PartialAllocationPromise extends AllocationPromise 10 | { 11 | /** 12 | * Returns the allocation now, synchronously 13 | * 14 | * If the pool does not have sufficient resources available then the number of resources will 15 | * burst beyond the pool size in order to facilitate this allocation 16 | * 17 | * @return Allocation 18 | * @throws \RuntimeException thrown if the allocation has previously failed 19 | */ 20 | public function force() 21 | { 22 | return $this->getResult(true); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "joshdifabio/resource-pool", 3 | "description": "Regulate the concurrency level of your async components", 4 | "keywords": ["lock", "concurrency", "ReactPHP", "async", "promise"], 5 | "license": "MIT", 6 | "authors": [ 7 | { 8 | "name": "Josh Di Fabio", 9 | "email": "joshdifabio@gmail.com" 10 | } 11 | ], 12 | "require": { 13 | "php": ">=5.4", 14 | "react/promise": "^2.0" 15 | }, 16 | "require-dev": { 17 | "phpunit/phpunit": "^4.6" 18 | }, 19 | "autoload": { 20 | "psr-4": { 21 | "ResourcePool\\": "src" 22 | } 23 | }, 24 | "extra": { 25 | "branch-alias": { 26 | "dev-master": "0.1-dev" 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Joshua Di Fabio 2 | 3 | Permission is hereby granted, free of charge, to any person 4 | obtaining a copy of this software and associated documentation 5 | files (the "Software"), to deal in the Software without 6 | restriction, including without limitation the rights to use, 7 | copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the 9 | Software is furnished to do so, subject to the following 10 | conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /src/Allocation.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * @api 8 | */ 9 | class Allocation 10 | { 11 | private $releaseFn; 12 | private $size; 13 | 14 | public function __construct($releaseFn, $size) 15 | { 16 | $this->releaseFn = $releaseFn; 17 | $this->size = $size; 18 | } 19 | 20 | /** 21 | * Releases a single resource from this allocation 22 | */ 23 | public function releaseOne() 24 | { 25 | $this->release(1); 26 | } 27 | 28 | /** 29 | * Releases all resources from this allocation 30 | */ 31 | public function releaseAll() 32 | { 33 | if ($this->size) { 34 | $this->release($this->size); 35 | } 36 | } 37 | 38 | /** 39 | * Releases the specified number of resources from this allocation 40 | * 41 | * @param int $count 42 | */ 43 | public function release($count) 44 | { 45 | $count = min($count, $this->size); 46 | 47 | if (!$count) { 48 | return; 49 | } 50 | 51 | $this->size -= $count; 52 | call_user_func($this->releaseFn, $count); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/Pool.php: -------------------------------------------------------------------------------- 1 | 8 | * 9 | * @api 10 | */ 11 | class Pool 12 | { 13 | private $internal; 14 | 15 | public function __construct($size = null) 16 | { 17 | $this->internal = new PoolInternal($size); 18 | } 19 | 20 | /** 21 | * Allocates a single resource when one becomes available 22 | * 23 | * @return PartialAllocationPromise 24 | */ 25 | public function allocateOne() 26 | { 27 | return $this->internal->allocate(1); 28 | } 29 | 30 | /** 31 | * Allocates the specified number of resources when they become available 32 | * 33 | * @param int $count 34 | * @return PartialAllocationPromise 35 | */ 36 | public function allocate($count) 37 | { 38 | return $this->internal->allocate($count); 39 | } 40 | 41 | /** 42 | * Allocates all of the pool's resources when they become available 43 | * 44 | * @return AllocationPromise 45 | */ 46 | public function allocateAll() 47 | { 48 | return $this->internal->allocateAll(); 49 | } 50 | 51 | /** 52 | * Sets the number of resources in the pool 53 | * 54 | * @param int $size 55 | */ 56 | public function setSize($size) 57 | { 58 | $this->internal->setSize($size); 59 | } 60 | 61 | /** 62 | * Returns the number of resources which are not currently allocated 63 | * 64 | * @return int 65 | */ 66 | public function getAvailability() 67 | { 68 | return $this->internal->getAvailability(); 69 | } 70 | 71 | /** 72 | * Returns the number of resources which are currently allocated 73 | * 74 | * @return int 75 | */ 76 | public function getUsage() 77 | { 78 | return $this->internal->getUsage(); 79 | } 80 | 81 | /** 82 | * @param callable|null $fulfilledHandler 83 | * @return PromiseInterface 84 | */ 85 | public function whenNextIdle($fulfilledHandler = null) 86 | { 87 | return $this->internal->whenNextIdle($fulfilledHandler); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/AllocationPromise.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @api 11 | */ 12 | class AllocationPromise implements PromiseInterface 13 | { 14 | private $promise; 15 | private $resolver; 16 | private $result; 17 | 18 | public function __construct(PromiseInterface $promise, $resolver = null) 19 | { 20 | $this->promise = $promise; 21 | $this->resolver = $resolver; 22 | } 23 | 24 | /** 25 | * {@inheritdoc} 26 | */ 27 | public function then(callable $fulfilledHandler = null, callable $errorHandler = null, callable $progressHandler = null) 28 | { 29 | return $this->promise->then($fulfilledHandler, $errorHandler, $progressHandler); 30 | } 31 | 32 | /** 33 | * Calls the specified handler when this promise is fulfilled. 34 | * 35 | * If the handler returns a promise, 36 | * 37 | * @param callable $handler 38 | * @return PromiseInterface 39 | */ 40 | public function to($handler /*, $args… */) 41 | { 42 | $args = array_slice(func_get_args(), 1); 43 | 44 | return $this->then( 45 | function (Allocation $allocation) use ($handler, $args) { 46 | try { 47 | $result = call_user_func_array($handler, $args); 48 | $result = \React\Promise\resolve($result); 49 | $result->then(array($allocation, 'releaseAll'), array($allocation, 'releaseAll')); 50 | } catch (\Exception $e) { 51 | $result = new RejectedPromise($e); 52 | $allocation->releaseAll(); 53 | } 54 | 55 | return $result; 56 | } 57 | ); 58 | } 59 | 60 | /** 61 | * Tries to return the allocation now, synchronously 62 | * 63 | * If the pool does not have sufficient resources available then an exception is thrown and this 64 | * promise is rejected 65 | * 66 | * @return Allocation 67 | * @throws \RuntimeException thrown if the allocation fails or has previously failed 68 | */ 69 | public function now() 70 | { 71 | return $this->getResult(false); 72 | } 73 | 74 | protected function getResult($burst) 75 | { 76 | if (null === $this->result) { 77 | $this->result = $this->resolve($burst); 78 | } 79 | 80 | if ($this->result instanceof \Exception) { 81 | throw $this->result; 82 | } 83 | 84 | return $this->result; 85 | } 86 | 87 | private function resolve($burst) 88 | { 89 | $result = null; 90 | 91 | $this->promise->then( 92 | function ($allocation) use (&$result) { 93 | $result = $allocation; 94 | }, 95 | function ($error) use (&$result) { 96 | $result = $error; 97 | } 98 | ); 99 | 100 | if (null === $result) { 101 | call_user_func($this->resolver, $burst); 102 | } 103 | 104 | return $result ?: new \LogicException('The resolver did not resolve the promise'); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Resource Pool 2 | 3 | [![Build Status](https://img.shields.io/travis/joshdifabio/resource-pool.svg?style=flat-square)](https://travis-ci.org/joshdifabio/resource-pool) 4 | [![Coverage](https://img.shields.io/codecov/c/github/joshdifabio/resource-pool.svg?style=flat-square)](http://codecov.io/github/joshdifabio/resource-pool) 5 | [![Code Quality](https://img.shields.io/scrutinizer/g/joshdifabio/resource-pool.svg?style=flat-square)](https://scrutinizer-ci.com/g/joshdifabio/resource-pool/) 6 | 7 | Don't pwn your resources, pool them! 8 | 9 | ## Introduction 10 | 11 | Resource pools allow you to regulate the concurrency level of your asynchronous PHP components and spare your servers from excessive load. You'll find them particularly useful if your application sends HTTP requests or spawns child processes using something like [ReactPHP](https://github.com/reactphp/react). 12 | 13 | ## Basic usage 14 | 15 | If you aren't familiar with [Promises](https://github.com/reactphp/promise), this section isn't going to make a lot of sense. 16 | 17 | Consider an application which sends HTTP requests to a remote endpoint asynchronously. 18 | 19 | ```php 20 | function sendRequest($httpRequest) : PromiseInterface { 21 | // this would probably be something like Guzzle or React/HttpClient 22 | } 23 | ``` 24 | 25 | ### How you shouldn't do it 26 | 27 | ```php 28 | foreach (getThousandsOfRequests() as $request) { 29 | sendRequest($request)->then(function ($response) { 30 | // the response came back! 31 | }); 32 | } 33 | 34 | // thousands of requests have been initiated concurrently 35 | ``` 36 | 37 | An implementation like this could easily send 100s or even 1000s of requests within a single second, causing huge load on the remote server as it tries to serve your requests. This is essentially a DoS attack, and will make sysadmins cry, who will then make you cry. 38 | 39 | ### How you should do it 40 | 41 | Create a resource pool representing a fixed number of resources, for example five. 42 | 43 | ```php 44 | $pool = new \ResourcePool\Pool(5); 45 | ``` 46 | 47 | Before sending a request, allocate a resource from the pool. `Pool::allocateOne()` returns an `AllocationPromise` which resolves as soon as a resource becomes available. 48 | 49 | ```php 50 | foreach (getThousandsOfRequests() as $request) { 51 | // to() will invoke a function and then release the allocated resources once it's done 52 | $pool->allocateOne()->to('sendRequest', $request)->then(function ($response) { 53 | // the response came back! 54 | }); 55 | } 56 | 57 | // five requests are running; the rest are queued and will be sent as others complete 58 | ``` 59 | 60 | That's it! You did it! This implementation will spawn a maximum of five concurrent requests. 61 | 62 | ## Advanced usage 63 | 64 | Advanced user? Read on. 65 | 66 | ### Allocate multiple resources 67 | 68 | ```php 69 | $pool->allocate(5)->to(function () { 70 | // this task requires five resources to run! 71 | }); 72 | ``` 73 | 74 | ### Allocate all the resources 75 | 76 | ```php 77 | $pool->allocateAll()->to(function () { 78 | // this requires all the resources! 79 | }); 80 | ``` 81 | 82 | ### Release allocations manually 83 | 84 | ```php 85 | // call then() instead of to() to work with the allocation directly 86 | $pool->allocate(2)->then(function ($allocation) { 87 | // two things which need to run at the same time 88 | firstThing()->done([$allocation, 'releaseOne']); 89 | secondThing()->done([$allocation, 'releaseOne']); 90 | }); 91 | ``` 92 | 93 | ### Force an allocation to resolve immediately 94 | 95 | ```php 96 | try { 97 | $allocation = $pool->allocate(2)->now(); 98 | } catch (\RuntimeException $e) { 99 | // throws a \RuntimeException if the pool cannot allocate two resources 100 | } 101 | ``` 102 | 103 | You can also choose to burst beyond the size of the pool for a specific allocation. 104 | 105 | ```php 106 | $pool = new \ResourcePool\Pool(1); 107 | $allocation = $pool->allocate(2)->force(); 108 | $pool->getUsage(); // 2 109 | $pool->getAvailability(); // 0 110 | $allocation->releaseAll(); 111 | $pool->getAvailability(); // 1 112 | ``` 113 | 114 | ### Find out when a pool is idle 115 | 116 | ```php 117 | $pool->whenNextIdle(function () { 118 | // the pool is idle! 119 | }); 120 | ``` 121 | 122 | ### Change the size of a pool 123 | 124 | ```php 125 | $pool->setSize(100); 126 | ``` 127 | 128 | ### Find out how many resources are allocated 129 | 130 | ```php 131 | $pool->getUsage(); 132 | ``` 133 | 134 | ### Find out how many resources are available 135 | 136 | ```php 137 | $pool->getAvailability(); 138 | ``` 139 | 140 | ## Installation 141 | 142 | Install Resource Pool using [composer](https://getcomposer.org/). 143 | 144 | ``` 145 | composer require joshdifabio/resource-pool 146 | ``` 147 | 148 | ## License 149 | 150 | Resource Pool is released under the [MIT](https://github.com/joshdifabio/resource-pool/blob/master/LICENSE) license. 151 | -------------------------------------------------------------------------------- /src/PoolInternal.php: -------------------------------------------------------------------------------- 1 | 12 | * 13 | * @internal 14 | */ 15 | class PoolInternal 16 | { 17 | private $size; 18 | private $usage = 0; 19 | private $queue; 20 | private $whenNextIdle; 21 | 22 | public function __construct($size = null) 23 | { 24 | $this->size = $size; 25 | $this->queue = new \SplQueue; 26 | $this->whenNextIdle = new Deferred; 27 | $this->whenNextIdle->resolve(); 28 | } 29 | 30 | public function allocate($count) 31 | { 32 | return $this->createAllocationPromise('ResourcePool\PartialAllocationPromise', $count); 33 | } 34 | 35 | public function allocateAll() 36 | { 37 | return $this->createAllocationPromise('ResourcePool\AllocationPromise'); 38 | } 39 | 40 | private function createAllocationPromise($promiseClass, $count = null) 41 | { 42 | $this->beforeAllocate(); 43 | 44 | if ($this->canAllocate($count)) { 45 | $allocation = $this->createAllocation($count); 46 | $promise = new FulfilledPromise($allocation); 47 | $resolver = null; 48 | } else { 49 | $deferred = new Deferred; 50 | $promise = $deferred->promise(); 51 | $isResolved = false; 52 | $resolver = $this->createResolver($isResolved, $count, $deferred); 53 | $promise->then(null, array($this, 'afterPromiseCancelled')); 54 | $this->queue->enqueue(array($count, $deferred, &$isResolved)); 55 | } 56 | 57 | return new $promiseClass($promise, $resolver); 58 | } 59 | 60 | public function setSize($size) 61 | { 62 | $this->size = $size; 63 | $this->processQueue(); 64 | } 65 | 66 | public function getAvailability() 67 | { 68 | return max(0, $this->size - $this->usage); 69 | } 70 | 71 | public function getUsage() 72 | { 73 | return $this->usage; 74 | } 75 | 76 | private function processQueue() 77 | { 78 | foreach ($this->queue as $allocationInfo) { 79 | if (true === $allocationInfo[2]) { 80 | $this->queue->dequeue(); 81 | continue; 82 | } 83 | 84 | if (!$this->canAllocate($allocationInfo[0])) { 85 | break; 86 | } 87 | 88 | $this->queue->dequeue(); 89 | $allocation = $this->createAllocation($allocationInfo[0]); 90 | $allocationInfo[1]->resolve($allocation); 91 | } 92 | } 93 | 94 | public function afterPromiseCancelled() 95 | { 96 | $this->decrementUsage(0); 97 | } 98 | 99 | public function decrementUsage($amount) 100 | { 101 | $this->usage -= $amount; 102 | $this->processQueue(); 103 | if ($this->isIdle()) { 104 | $this->whenNextIdle->resolve(); 105 | } 106 | } 107 | 108 | public function canAllocate($count = null) 109 | { 110 | if (null === $count) { 111 | return 0 === $this->usage && $this->size > 0; 112 | } 113 | 114 | return $count <= $this->getAvailability(); 115 | } 116 | 117 | public function createAllocation($size) 118 | { 119 | if (null === $size) { 120 | $size = $this->size; 121 | } 122 | 123 | $this->usage += $size; 124 | 125 | return new Allocation(array($this, 'decrementUsage'), $size); 126 | } 127 | 128 | public function whenNextIdle($fulfilledHandler = null) 129 | { 130 | $promise = $this->whenNextIdle->promise(); 131 | 132 | if (null !== $fulfilledHandler) { 133 | return $promise->then($fulfilledHandler); 134 | } 135 | 136 | return $promise; 137 | } 138 | 139 | private function beforeAllocate() 140 | { 141 | if ($this->isIdle()) { 142 | $this->whenNextIdle = new Deferred; 143 | } 144 | } 145 | 146 | private function isIdle() 147 | { 148 | return (0 == $this->usage && 0 == $this->queue->count()); 149 | } 150 | 151 | private function createResolver(&$isResolved, $count, $deferred) 152 | { 153 | $that = $this; 154 | 155 | return function ($burst) use (&$isResolved, $that, $count, $deferred) { 156 | if ($isResolved) { 157 | throw new \LogicException; 158 | } 159 | 160 | $isResolved = true; 161 | 162 | if ($burst || $that->canAllocate($count)) { 163 | $allocation = $that->createAllocation($count); 164 | $deferred->resolve($allocation); 165 | } else { 166 | $deferred->reject(new \RuntimeException('The resource pool cannot allocate the specified number of resources')); 167 | } 168 | }; 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /tests/FunctionalTest.php: -------------------------------------------------------------------------------- 1 | 8 | */ 9 | class PoolTest extends \PHPUnit_Framework_TestCase 10 | { 11 | public function testAllocate() 12 | { 13 | $pool = new Pool(1); 14 | 15 | $this->assertEquals(0, $pool->getUsage()); 16 | 17 | $firstAllocation = null; 18 | 19 | $pool->allocateOne()->then(function ($allocation) use (&$firstAllocation) { 20 | $firstAllocation = $allocation; 21 | }); 22 | $this->assertNotNull($firstAllocation); 23 | $this->assertEquals(1, $pool->getUsage()); 24 | 25 | $secondAllocation = null; 26 | 27 | $pool->allocateOne()->then(function ($allocation) use (&$secondAllocation) { 28 | $secondAllocation = $allocation; 29 | }); 30 | $this->assertNull($secondAllocation); 31 | $this->assertEquals(1, $pool->getUsage()); 32 | 33 | $thirdAllocation = $pool->allocateOne()->force(); 34 | $this->assertNull($secondAllocation); 35 | $this->assertEquals(2, $pool->getUsage()); 36 | 37 | for ($i = 0; $i < 2; $i++) { 38 | $thirdAllocation->releaseAll(); 39 | $this->assertNull($secondAllocation); 40 | $this->assertEquals(1, $pool->getUsage()); 41 | } 42 | 43 | $firstAllocation->releaseAll(); 44 | $this->assertNotNull($secondAllocation); 45 | $this->assertEquals(1, $pool->getUsage()); 46 | 47 | $secondAllocation->releaseAll(); 48 | $this->assertEquals(0, $pool->getUsage()); 49 | } 50 | 51 | public function testSizeIncrease() 52 | { 53 | $pool = new Pool(1); 54 | 55 | $this->assertEquals(0, $pool->getUsage()); 56 | 57 | $firstAllocation = null; 58 | 59 | $pool->allocateOne()->then(function ($allocation) use (&$firstAllocation) { 60 | $firstAllocation = $allocation; 61 | }); 62 | $this->assertNotNull($firstAllocation); 63 | $this->assertEquals(1, $pool->getUsage()); 64 | 65 | $secondAllocation = null; 66 | 67 | $pool->allocateOne()->then(function ($allocation) use (&$secondAllocation) { 68 | $secondAllocation = $allocation; 69 | }); 70 | $this->assertNull($secondAllocation); 71 | $this->assertEquals(1, $pool->getUsage()); 72 | 73 | $pool->setSize(1); 74 | $this->assertNull($secondAllocation); 75 | $this->assertEquals(1, $pool->getUsage()); 76 | 77 | $pool->setSize(0); 78 | $this->assertNull($secondAllocation); 79 | $this->assertEquals(1, $pool->getUsage()); 80 | 81 | $pool->setSize(2); 82 | $this->assertNotNull($secondAllocation); 83 | $this->assertEquals(2, $pool->getUsage()); 84 | } 85 | 86 | public function testAllocateAll() 87 | { 88 | $pool = new Pool(2); 89 | 90 | $this->assertEquals(0, $pool->getUsage()); 91 | 92 | $firstAllocation = null; 93 | 94 | $pool->allocateOne()->then(function ($allocation) use (&$firstAllocation) { 95 | $firstAllocation = $allocation; 96 | }); 97 | $this->assertNotNull($firstAllocation); 98 | $this->assertEquals(1, $pool->getUsage()); 99 | 100 | $secondAllocation = null; 101 | 102 | $pool->allocateOne()->then(function ($allocation) use (&$secondAllocation) { 103 | $secondAllocation = $allocation; 104 | }); 105 | $this->assertNotNull($secondAllocation); 106 | $this->assertEquals(2, $pool->getUsage()); 107 | 108 | $fullAllocation = null; 109 | 110 | $pool->allocateAll()->then(function ($allocation) use (&$fullAllocation) { 111 | $fullAllocation = $allocation; 112 | }); 113 | $this->assertNull($fullAllocation); 114 | $this->assertEquals(2, $pool->getUsage()); 115 | 116 | $pool->setSize(3); 117 | 118 | $firstAllocation->releaseAll(); 119 | $this->assertNull($fullAllocation); 120 | $this->assertEquals(1, $pool->getUsage()); 121 | 122 | $secondAllocation->releaseAll(); 123 | $this->assertNotNull($fullAllocation); 124 | $this->assertEquals(3, $pool->getUsage()); 125 | } 126 | 127 | public function testAllocateTo() 128 | { 129 | $pool = new Pool(2); 130 | $deferred = new Deferred; 131 | $resultPromise = $pool->allocate(2)->to(array($deferred, 'promise')); 132 | $this->assertEquals(2, $pool->getUsage()); 133 | $deferred->resolve('Hello!'); 134 | $this->assertEquals(0, $pool->getUsage()); 135 | $result = null; 136 | $resultPromise->then(function ($_result) use (&$result) { 137 | $result = $_result; 138 | }); 139 | $this->assertSame('Hello!', $result); 140 | } 141 | 142 | public function testAllocateToSyncCallback() 143 | { 144 | $pool = new Pool(2); 145 | $result = null; 146 | $resultPromise = $pool->allocate(2)->to(function () { 147 | return 'Hello!'; 148 | }); 149 | $resultPromise->then(function ($_result) use (&$result) { 150 | $result = $_result; 151 | }); 152 | $this->assertEquals(0, $pool->getUsage()); 153 | $this->assertSame('Hello!', $result); 154 | } 155 | 156 | public function testAllocateToWithParam() 157 | { 158 | $pool = new Pool(1); 159 | $resultPromise = $pool->allocate(1)->to('strtolower', 'Hello, world!'); 160 | $result = null; 161 | $resultPromise->then(function ($_result) use (&$result) { 162 | $result = $_result; 163 | }); 164 | $this->assertSame('hello, world!', $result); 165 | } 166 | 167 | public function testAllocateToWithParams() 168 | { 169 | $pool = new Pool(1); 170 | $resultPromise = $pool->allocate(1)->to('implode', ',', array('Hello', ' world!')); 171 | $result = null; 172 | $resultPromise->then(function ($_result) use (&$result) { 173 | $result = $_result; 174 | }); 175 | $this->assertSame('Hello, world!', $result); 176 | } 177 | 178 | public function testWhenNextIdle() 179 | { 180 | $pool = new Pool(2); 181 | 182 | $isIdle1 = false; 183 | $pool->whenNextIdle(function () use (&$isIdle1) { 184 | $isIdle1 = true; 185 | }); 186 | $this->assertTrue($isIdle1); 187 | 188 | $isIdle2 = false; 189 | $pool->whenNextIdle()->then(function () use (&$isIdle2) { 190 | $isIdle2 = true; 191 | }); 192 | $this->assertTrue($isIdle2); 193 | 194 | $deferred = new Deferred; 195 | $isIdle3 = false; 196 | $pool->allocate(1)->to(array($deferred, 'promise')); 197 | $pool->whenNextIdle(function () use (&$isIdle3) { 198 | $isIdle3 = true; 199 | }); 200 | $this->assertFalse($isIdle3); 201 | $deferred->resolve(); 202 | $this->assertTrue($isIdle3); 203 | } 204 | 205 | public function testAllocationFailureCleanup() 206 | { 207 | $pool = new Pool(0); 208 | 209 | $allocationPromise = $pool->allocateOne(); 210 | 211 | $isIdle1 = false; 212 | $pool->whenNextIdle(function () use (&$isIdle1) { 213 | $isIdle1 = true; 214 | }); 215 | $this->assertFalse($isIdle1); 216 | 217 | $e1 = null; 218 | try { 219 | $allocationPromise->now(); 220 | } catch (\Exception $e1) { 221 | 222 | } 223 | $this->assertNotNull($e1); 224 | 225 | $e2 = null; 226 | try { 227 | $allocationPromise->force(); 228 | } catch (\Exception $e2) { 229 | 230 | } 231 | $this->assertNotNull($e2); 232 | 233 | $isIdle2 = false; 234 | $pool->whenNextIdle(function () use (&$isIdle2) { 235 | $isIdle2 = true; 236 | }); 237 | $this->assertTrue($isIdle2); 238 | } 239 | } 240 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", 5 | "This file is @generated automatically" 6 | ], 7 | "hash": "cc6ed73dc523bd2a0126e21813460cf0", 8 | "content-hash": "8b9a4d95c0102534ef76c0409241fb9d", 9 | "packages": [ 10 | { 11 | "name": "react/promise", 12 | "version": "v2.5.0", 13 | "source": { 14 | "type": "git", 15 | "url": "https://github.com/reactphp/promise.git", 16 | "reference": "2760f3898b7e931aa71153852dcd48a75c9b95db" 17 | }, 18 | "dist": { 19 | "type": "zip", 20 | "url": "https://api.github.com/repos/reactphp/promise/zipball/2760f3898b7e931aa71153852dcd48a75c9b95db", 21 | "reference": "2760f3898b7e931aa71153852dcd48a75c9b95db", 22 | "shasum": "" 23 | }, 24 | "require": { 25 | "php": ">=5.4.0" 26 | }, 27 | "type": "library", 28 | "autoload": { 29 | "psr-4": { 30 | "React\\Promise\\": "src/" 31 | }, 32 | "files": [ 33 | "src/functions_include.php" 34 | ] 35 | }, 36 | "notification-url": "https://packagist.org/downloads/", 37 | "license": [ 38 | "MIT" 39 | ], 40 | "authors": [ 41 | { 42 | "name": "Jan Sorgalla", 43 | "email": "jsorgalla@gmail.com" 44 | } 45 | ], 46 | "description": "A lightweight implementation of CommonJS Promises/A for PHP", 47 | "keywords": [ 48 | "promise", 49 | "promises" 50 | ], 51 | "time": "2016-12-22 14:09:01" 52 | } 53 | ], 54 | "packages-dev": [ 55 | { 56 | "name": "doctrine/instantiator", 57 | "version": "1.0.5", 58 | "source": { 59 | "type": "git", 60 | "url": "https://github.com/doctrine/instantiator.git", 61 | "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d" 62 | }, 63 | "dist": { 64 | "type": "zip", 65 | "url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d", 66 | "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d", 67 | "shasum": "" 68 | }, 69 | "require": { 70 | "php": ">=5.3,<8.0-DEV" 71 | }, 72 | "require-dev": { 73 | "athletic/athletic": "~0.1.8", 74 | "ext-pdo": "*", 75 | "ext-phar": "*", 76 | "phpunit/phpunit": "~4.0", 77 | "squizlabs/php_codesniffer": "~2.0" 78 | }, 79 | "type": "library", 80 | "extra": { 81 | "branch-alias": { 82 | "dev-master": "1.0.x-dev" 83 | } 84 | }, 85 | "autoload": { 86 | "psr-4": { 87 | "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" 88 | } 89 | }, 90 | "notification-url": "https://packagist.org/downloads/", 91 | "license": [ 92 | "MIT" 93 | ], 94 | "authors": [ 95 | { 96 | "name": "Marco Pivetta", 97 | "email": "ocramius@gmail.com", 98 | "homepage": "http://ocramius.github.com/" 99 | } 100 | ], 101 | "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", 102 | "homepage": "https://github.com/doctrine/instantiator", 103 | "keywords": [ 104 | "constructor", 105 | "instantiate" 106 | ], 107 | "time": "2015-06-14 21:17:01" 108 | }, 109 | { 110 | "name": "phpdocumentor/reflection-docblock", 111 | "version": "2.0.4", 112 | "source": { 113 | "type": "git", 114 | "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", 115 | "reference": "d68dbdc53dc358a816f00b300704702b2eaff7b8" 116 | }, 117 | "dist": { 118 | "type": "zip", 119 | "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/d68dbdc53dc358a816f00b300704702b2eaff7b8", 120 | "reference": "d68dbdc53dc358a816f00b300704702b2eaff7b8", 121 | "shasum": "" 122 | }, 123 | "require": { 124 | "php": ">=5.3.3" 125 | }, 126 | "require-dev": { 127 | "phpunit/phpunit": "~4.0" 128 | }, 129 | "suggest": { 130 | "dflydev/markdown": "~1.0", 131 | "erusev/parsedown": "~1.0" 132 | }, 133 | "type": "library", 134 | "extra": { 135 | "branch-alias": { 136 | "dev-master": "2.0.x-dev" 137 | } 138 | }, 139 | "autoload": { 140 | "psr-0": { 141 | "phpDocumentor": [ 142 | "src/" 143 | ] 144 | } 145 | }, 146 | "notification-url": "https://packagist.org/downloads/", 147 | "license": [ 148 | "MIT" 149 | ], 150 | "authors": [ 151 | { 152 | "name": "Mike van Riel", 153 | "email": "mike.vanriel@naenius.com" 154 | } 155 | ], 156 | "time": "2015-02-03 12:10:50" 157 | }, 158 | { 159 | "name": "phpspec/prophecy", 160 | "version": "v1.6.2", 161 | "source": { 162 | "type": "git", 163 | "url": "https://github.com/phpspec/prophecy.git", 164 | "reference": "6c52c2722f8460122f96f86346600e1077ce22cb" 165 | }, 166 | "dist": { 167 | "type": "zip", 168 | "url": "https://api.github.com/repos/phpspec/prophecy/zipball/6c52c2722f8460122f96f86346600e1077ce22cb", 169 | "reference": "6c52c2722f8460122f96f86346600e1077ce22cb", 170 | "shasum": "" 171 | }, 172 | "require": { 173 | "doctrine/instantiator": "^1.0.2", 174 | "php": "^5.3|^7.0", 175 | "phpdocumentor/reflection-docblock": "^2.0|^3.0.2", 176 | "sebastian/comparator": "^1.1", 177 | "sebastian/recursion-context": "^1.0|^2.0" 178 | }, 179 | "require-dev": { 180 | "phpspec/phpspec": "^2.0", 181 | "phpunit/phpunit": "^4.8 || ^5.6.5" 182 | }, 183 | "type": "library", 184 | "extra": { 185 | "branch-alias": { 186 | "dev-master": "1.6.x-dev" 187 | } 188 | }, 189 | "autoload": { 190 | "psr-0": { 191 | "Prophecy\\": "src/" 192 | } 193 | }, 194 | "notification-url": "https://packagist.org/downloads/", 195 | "license": [ 196 | "MIT" 197 | ], 198 | "authors": [ 199 | { 200 | "name": "Konstantin Kudryashov", 201 | "email": "ever.zet@gmail.com", 202 | "homepage": "http://everzet.com" 203 | }, 204 | { 205 | "name": "Marcello Duarte", 206 | "email": "marcello.duarte@gmail.com" 207 | } 208 | ], 209 | "description": "Highly opinionated mocking framework for PHP 5.3+", 210 | "homepage": "https://github.com/phpspec/prophecy", 211 | "keywords": [ 212 | "Double", 213 | "Dummy", 214 | "fake", 215 | "mock", 216 | "spy", 217 | "stub" 218 | ], 219 | "time": "2016-11-21 14:58:47" 220 | }, 221 | { 222 | "name": "phpunit/php-code-coverage", 223 | "version": "2.2.4", 224 | "source": { 225 | "type": "git", 226 | "url": "https://github.com/sebastianbergmann/php-code-coverage.git", 227 | "reference": "eabf68b476ac7d0f73793aada060f1c1a9bf8979" 228 | }, 229 | "dist": { 230 | "type": "zip", 231 | "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/eabf68b476ac7d0f73793aada060f1c1a9bf8979", 232 | "reference": "eabf68b476ac7d0f73793aada060f1c1a9bf8979", 233 | "shasum": "" 234 | }, 235 | "require": { 236 | "php": ">=5.3.3", 237 | "phpunit/php-file-iterator": "~1.3", 238 | "phpunit/php-text-template": "~1.2", 239 | "phpunit/php-token-stream": "~1.3", 240 | "sebastian/environment": "^1.3.2", 241 | "sebastian/version": "~1.0" 242 | }, 243 | "require-dev": { 244 | "ext-xdebug": ">=2.1.4", 245 | "phpunit/phpunit": "~4" 246 | }, 247 | "suggest": { 248 | "ext-dom": "*", 249 | "ext-xdebug": ">=2.2.1", 250 | "ext-xmlwriter": "*" 251 | }, 252 | "type": "library", 253 | "extra": { 254 | "branch-alias": { 255 | "dev-master": "2.2.x-dev" 256 | } 257 | }, 258 | "autoload": { 259 | "classmap": [ 260 | "src/" 261 | ] 262 | }, 263 | "notification-url": "https://packagist.org/downloads/", 264 | "license": [ 265 | "BSD-3-Clause" 266 | ], 267 | "authors": [ 268 | { 269 | "name": "Sebastian Bergmann", 270 | "email": "sb@sebastian-bergmann.de", 271 | "role": "lead" 272 | } 273 | ], 274 | "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", 275 | "homepage": "https://github.com/sebastianbergmann/php-code-coverage", 276 | "keywords": [ 277 | "coverage", 278 | "testing", 279 | "xunit" 280 | ], 281 | "time": "2015-10-06 15:47:00" 282 | }, 283 | { 284 | "name": "phpunit/php-file-iterator", 285 | "version": "1.4.2", 286 | "source": { 287 | "type": "git", 288 | "url": "https://github.com/sebastianbergmann/php-file-iterator.git", 289 | "reference": "3cc8f69b3028d0f96a9078e6295d86e9bf019be5" 290 | }, 291 | "dist": { 292 | "type": "zip", 293 | "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/3cc8f69b3028d0f96a9078e6295d86e9bf019be5", 294 | "reference": "3cc8f69b3028d0f96a9078e6295d86e9bf019be5", 295 | "shasum": "" 296 | }, 297 | "require": { 298 | "php": ">=5.3.3" 299 | }, 300 | "type": "library", 301 | "extra": { 302 | "branch-alias": { 303 | "dev-master": "1.4.x-dev" 304 | } 305 | }, 306 | "autoload": { 307 | "classmap": [ 308 | "src/" 309 | ] 310 | }, 311 | "notification-url": "https://packagist.org/downloads/", 312 | "license": [ 313 | "BSD-3-Clause" 314 | ], 315 | "authors": [ 316 | { 317 | "name": "Sebastian Bergmann", 318 | "email": "sb@sebastian-bergmann.de", 319 | "role": "lead" 320 | } 321 | ], 322 | "description": "FilterIterator implementation that filters files based on a list of suffixes.", 323 | "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", 324 | "keywords": [ 325 | "filesystem", 326 | "iterator" 327 | ], 328 | "time": "2016-10-03 07:40:28" 329 | }, 330 | { 331 | "name": "phpunit/php-text-template", 332 | "version": "1.2.1", 333 | "source": { 334 | "type": "git", 335 | "url": "https://github.com/sebastianbergmann/php-text-template.git", 336 | "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" 337 | }, 338 | "dist": { 339 | "type": "zip", 340 | "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", 341 | "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", 342 | "shasum": "" 343 | }, 344 | "require": { 345 | "php": ">=5.3.3" 346 | }, 347 | "type": "library", 348 | "autoload": { 349 | "classmap": [ 350 | "src/" 351 | ] 352 | }, 353 | "notification-url": "https://packagist.org/downloads/", 354 | "license": [ 355 | "BSD-3-Clause" 356 | ], 357 | "authors": [ 358 | { 359 | "name": "Sebastian Bergmann", 360 | "email": "sebastian@phpunit.de", 361 | "role": "lead" 362 | } 363 | ], 364 | "description": "Simple template engine.", 365 | "homepage": "https://github.com/sebastianbergmann/php-text-template/", 366 | "keywords": [ 367 | "template" 368 | ], 369 | "time": "2015-06-21 13:50:34" 370 | }, 371 | { 372 | "name": "phpunit/php-timer", 373 | "version": "1.0.8", 374 | "source": { 375 | "type": "git", 376 | "url": "https://github.com/sebastianbergmann/php-timer.git", 377 | "reference": "38e9124049cf1a164f1e4537caf19c99bf1eb260" 378 | }, 379 | "dist": { 380 | "type": "zip", 381 | "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/38e9124049cf1a164f1e4537caf19c99bf1eb260", 382 | "reference": "38e9124049cf1a164f1e4537caf19c99bf1eb260", 383 | "shasum": "" 384 | }, 385 | "require": { 386 | "php": ">=5.3.3" 387 | }, 388 | "require-dev": { 389 | "phpunit/phpunit": "~4|~5" 390 | }, 391 | "type": "library", 392 | "autoload": { 393 | "classmap": [ 394 | "src/" 395 | ] 396 | }, 397 | "notification-url": "https://packagist.org/downloads/", 398 | "license": [ 399 | "BSD-3-Clause" 400 | ], 401 | "authors": [ 402 | { 403 | "name": "Sebastian Bergmann", 404 | "email": "sb@sebastian-bergmann.de", 405 | "role": "lead" 406 | } 407 | ], 408 | "description": "Utility class for timing", 409 | "homepage": "https://github.com/sebastianbergmann/php-timer/", 410 | "keywords": [ 411 | "timer" 412 | ], 413 | "time": "2016-05-12 18:03:57" 414 | }, 415 | { 416 | "name": "phpunit/php-token-stream", 417 | "version": "1.4.9", 418 | "source": { 419 | "type": "git", 420 | "url": "https://github.com/sebastianbergmann/php-token-stream.git", 421 | "reference": "3b402f65a4cc90abf6e1104e388b896ce209631b" 422 | }, 423 | "dist": { 424 | "type": "zip", 425 | "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/3b402f65a4cc90abf6e1104e388b896ce209631b", 426 | "reference": "3b402f65a4cc90abf6e1104e388b896ce209631b", 427 | "shasum": "" 428 | }, 429 | "require": { 430 | "ext-tokenizer": "*", 431 | "php": ">=5.3.3" 432 | }, 433 | "require-dev": { 434 | "phpunit/phpunit": "~4.2" 435 | }, 436 | "type": "library", 437 | "extra": { 438 | "branch-alias": { 439 | "dev-master": "1.4-dev" 440 | } 441 | }, 442 | "autoload": { 443 | "classmap": [ 444 | "src/" 445 | ] 446 | }, 447 | "notification-url": "https://packagist.org/downloads/", 448 | "license": [ 449 | "BSD-3-Clause" 450 | ], 451 | "authors": [ 452 | { 453 | "name": "Sebastian Bergmann", 454 | "email": "sebastian@phpunit.de" 455 | } 456 | ], 457 | "description": "Wrapper around PHP's tokenizer extension.", 458 | "homepage": "https://github.com/sebastianbergmann/php-token-stream/", 459 | "keywords": [ 460 | "tokenizer" 461 | ], 462 | "time": "2016-11-15 14:06:22" 463 | }, 464 | { 465 | "name": "phpunit/phpunit", 466 | "version": "4.8.31", 467 | "source": { 468 | "type": "git", 469 | "url": "https://github.com/sebastianbergmann/phpunit.git", 470 | "reference": "98b2b39a520766bec663ff5b7ff1b729db9dbfe3" 471 | }, 472 | "dist": { 473 | "type": "zip", 474 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/98b2b39a520766bec663ff5b7ff1b729db9dbfe3", 475 | "reference": "98b2b39a520766bec663ff5b7ff1b729db9dbfe3", 476 | "shasum": "" 477 | }, 478 | "require": { 479 | "ext-dom": "*", 480 | "ext-json": "*", 481 | "ext-pcre": "*", 482 | "ext-reflection": "*", 483 | "ext-spl": "*", 484 | "php": ">=5.3.3", 485 | "phpspec/prophecy": "^1.3.1", 486 | "phpunit/php-code-coverage": "~2.1", 487 | "phpunit/php-file-iterator": "~1.4", 488 | "phpunit/php-text-template": "~1.2", 489 | "phpunit/php-timer": "^1.0.6", 490 | "phpunit/phpunit-mock-objects": "~2.3", 491 | "sebastian/comparator": "~1.2.2", 492 | "sebastian/diff": "~1.2", 493 | "sebastian/environment": "~1.3", 494 | "sebastian/exporter": "~1.2", 495 | "sebastian/global-state": "~1.0", 496 | "sebastian/version": "~1.0", 497 | "symfony/yaml": "~2.1|~3.0" 498 | }, 499 | "suggest": { 500 | "phpunit/php-invoker": "~1.1" 501 | }, 502 | "bin": [ 503 | "phpunit" 504 | ], 505 | "type": "library", 506 | "extra": { 507 | "branch-alias": { 508 | "dev-master": "4.8.x-dev" 509 | } 510 | }, 511 | "autoload": { 512 | "classmap": [ 513 | "src/" 514 | ] 515 | }, 516 | "notification-url": "https://packagist.org/downloads/", 517 | "license": [ 518 | "BSD-3-Clause" 519 | ], 520 | "authors": [ 521 | { 522 | "name": "Sebastian Bergmann", 523 | "email": "sebastian@phpunit.de", 524 | "role": "lead" 525 | } 526 | ], 527 | "description": "The PHP Unit Testing framework.", 528 | "homepage": "https://phpunit.de/", 529 | "keywords": [ 530 | "phpunit", 531 | "testing", 532 | "xunit" 533 | ], 534 | "time": "2016-12-09 02:45:31" 535 | }, 536 | { 537 | "name": "phpunit/phpunit-mock-objects", 538 | "version": "2.3.8", 539 | "source": { 540 | "type": "git", 541 | "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", 542 | "reference": "ac8e7a3db35738d56ee9a76e78a4e03d97628983" 543 | }, 544 | "dist": { 545 | "type": "zip", 546 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/ac8e7a3db35738d56ee9a76e78a4e03d97628983", 547 | "reference": "ac8e7a3db35738d56ee9a76e78a4e03d97628983", 548 | "shasum": "" 549 | }, 550 | "require": { 551 | "doctrine/instantiator": "^1.0.2", 552 | "php": ">=5.3.3", 553 | "phpunit/php-text-template": "~1.2", 554 | "sebastian/exporter": "~1.2" 555 | }, 556 | "require-dev": { 557 | "phpunit/phpunit": "~4.4" 558 | }, 559 | "suggest": { 560 | "ext-soap": "*" 561 | }, 562 | "type": "library", 563 | "extra": { 564 | "branch-alias": { 565 | "dev-master": "2.3.x-dev" 566 | } 567 | }, 568 | "autoload": { 569 | "classmap": [ 570 | "src/" 571 | ] 572 | }, 573 | "notification-url": "https://packagist.org/downloads/", 574 | "license": [ 575 | "BSD-3-Clause" 576 | ], 577 | "authors": [ 578 | { 579 | "name": "Sebastian Bergmann", 580 | "email": "sb@sebastian-bergmann.de", 581 | "role": "lead" 582 | } 583 | ], 584 | "description": "Mock Object library for PHPUnit", 585 | "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", 586 | "keywords": [ 587 | "mock", 588 | "xunit" 589 | ], 590 | "time": "2015-10-02 06:51:40" 591 | }, 592 | { 593 | "name": "sebastian/comparator", 594 | "version": "1.2.2", 595 | "source": { 596 | "type": "git", 597 | "url": "https://github.com/sebastianbergmann/comparator.git", 598 | "reference": "6a1ed12e8b2409076ab22e3897126211ff8b1f7f" 599 | }, 600 | "dist": { 601 | "type": "zip", 602 | "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/6a1ed12e8b2409076ab22e3897126211ff8b1f7f", 603 | "reference": "6a1ed12e8b2409076ab22e3897126211ff8b1f7f", 604 | "shasum": "" 605 | }, 606 | "require": { 607 | "php": ">=5.3.3", 608 | "sebastian/diff": "~1.2", 609 | "sebastian/exporter": "~1.2 || ~2.0" 610 | }, 611 | "require-dev": { 612 | "phpunit/phpunit": "~4.4" 613 | }, 614 | "type": "library", 615 | "extra": { 616 | "branch-alias": { 617 | "dev-master": "1.2.x-dev" 618 | } 619 | }, 620 | "autoload": { 621 | "classmap": [ 622 | "src/" 623 | ] 624 | }, 625 | "notification-url": "https://packagist.org/downloads/", 626 | "license": [ 627 | "BSD-3-Clause" 628 | ], 629 | "authors": [ 630 | { 631 | "name": "Jeff Welch", 632 | "email": "whatthejeff@gmail.com" 633 | }, 634 | { 635 | "name": "Volker Dusch", 636 | "email": "github@wallbash.com" 637 | }, 638 | { 639 | "name": "Bernhard Schussek", 640 | "email": "bschussek@2bepublished.at" 641 | }, 642 | { 643 | "name": "Sebastian Bergmann", 644 | "email": "sebastian@phpunit.de" 645 | } 646 | ], 647 | "description": "Provides the functionality to compare PHP values for equality", 648 | "homepage": "http://www.github.com/sebastianbergmann/comparator", 649 | "keywords": [ 650 | "comparator", 651 | "compare", 652 | "equality" 653 | ], 654 | "time": "2016-11-19 09:18:40" 655 | }, 656 | { 657 | "name": "sebastian/diff", 658 | "version": "1.4.1", 659 | "source": { 660 | "type": "git", 661 | "url": "https://github.com/sebastianbergmann/diff.git", 662 | "reference": "13edfd8706462032c2f52b4b862974dd46b71c9e" 663 | }, 664 | "dist": { 665 | "type": "zip", 666 | "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/13edfd8706462032c2f52b4b862974dd46b71c9e", 667 | "reference": "13edfd8706462032c2f52b4b862974dd46b71c9e", 668 | "shasum": "" 669 | }, 670 | "require": { 671 | "php": ">=5.3.3" 672 | }, 673 | "require-dev": { 674 | "phpunit/phpunit": "~4.8" 675 | }, 676 | "type": "library", 677 | "extra": { 678 | "branch-alias": { 679 | "dev-master": "1.4-dev" 680 | } 681 | }, 682 | "autoload": { 683 | "classmap": [ 684 | "src/" 685 | ] 686 | }, 687 | "notification-url": "https://packagist.org/downloads/", 688 | "license": [ 689 | "BSD-3-Clause" 690 | ], 691 | "authors": [ 692 | { 693 | "name": "Kore Nordmann", 694 | "email": "mail@kore-nordmann.de" 695 | }, 696 | { 697 | "name": "Sebastian Bergmann", 698 | "email": "sebastian@phpunit.de" 699 | } 700 | ], 701 | "description": "Diff implementation", 702 | "homepage": "https://github.com/sebastianbergmann/diff", 703 | "keywords": [ 704 | "diff" 705 | ], 706 | "time": "2015-12-08 07:14:41" 707 | }, 708 | { 709 | "name": "sebastian/environment", 710 | "version": "1.3.8", 711 | "source": { 712 | "type": "git", 713 | "url": "https://github.com/sebastianbergmann/environment.git", 714 | "reference": "be2c607e43ce4c89ecd60e75c6a85c126e754aea" 715 | }, 716 | "dist": { 717 | "type": "zip", 718 | "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/be2c607e43ce4c89ecd60e75c6a85c126e754aea", 719 | "reference": "be2c607e43ce4c89ecd60e75c6a85c126e754aea", 720 | "shasum": "" 721 | }, 722 | "require": { 723 | "php": "^5.3.3 || ^7.0" 724 | }, 725 | "require-dev": { 726 | "phpunit/phpunit": "^4.8 || ^5.0" 727 | }, 728 | "type": "library", 729 | "extra": { 730 | "branch-alias": { 731 | "dev-master": "1.3.x-dev" 732 | } 733 | }, 734 | "autoload": { 735 | "classmap": [ 736 | "src/" 737 | ] 738 | }, 739 | "notification-url": "https://packagist.org/downloads/", 740 | "license": [ 741 | "BSD-3-Clause" 742 | ], 743 | "authors": [ 744 | { 745 | "name": "Sebastian Bergmann", 746 | "email": "sebastian@phpunit.de" 747 | } 748 | ], 749 | "description": "Provides functionality to handle HHVM/PHP environments", 750 | "homepage": "http://www.github.com/sebastianbergmann/environment", 751 | "keywords": [ 752 | "Xdebug", 753 | "environment", 754 | "hhvm" 755 | ], 756 | "time": "2016-08-18 05:49:44" 757 | }, 758 | { 759 | "name": "sebastian/exporter", 760 | "version": "1.2.2", 761 | "source": { 762 | "type": "git", 763 | "url": "https://github.com/sebastianbergmann/exporter.git", 764 | "reference": "42c4c2eec485ee3e159ec9884f95b431287edde4" 765 | }, 766 | "dist": { 767 | "type": "zip", 768 | "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/42c4c2eec485ee3e159ec9884f95b431287edde4", 769 | "reference": "42c4c2eec485ee3e159ec9884f95b431287edde4", 770 | "shasum": "" 771 | }, 772 | "require": { 773 | "php": ">=5.3.3", 774 | "sebastian/recursion-context": "~1.0" 775 | }, 776 | "require-dev": { 777 | "ext-mbstring": "*", 778 | "phpunit/phpunit": "~4.4" 779 | }, 780 | "type": "library", 781 | "extra": { 782 | "branch-alias": { 783 | "dev-master": "1.3.x-dev" 784 | } 785 | }, 786 | "autoload": { 787 | "classmap": [ 788 | "src/" 789 | ] 790 | }, 791 | "notification-url": "https://packagist.org/downloads/", 792 | "license": [ 793 | "BSD-3-Clause" 794 | ], 795 | "authors": [ 796 | { 797 | "name": "Jeff Welch", 798 | "email": "whatthejeff@gmail.com" 799 | }, 800 | { 801 | "name": "Volker Dusch", 802 | "email": "github@wallbash.com" 803 | }, 804 | { 805 | "name": "Bernhard Schussek", 806 | "email": "bschussek@2bepublished.at" 807 | }, 808 | { 809 | "name": "Sebastian Bergmann", 810 | "email": "sebastian@phpunit.de" 811 | }, 812 | { 813 | "name": "Adam Harvey", 814 | "email": "aharvey@php.net" 815 | } 816 | ], 817 | "description": "Provides the functionality to export PHP variables for visualization", 818 | "homepage": "http://www.github.com/sebastianbergmann/exporter", 819 | "keywords": [ 820 | "export", 821 | "exporter" 822 | ], 823 | "time": "2016-06-17 09:04:28" 824 | }, 825 | { 826 | "name": "sebastian/global-state", 827 | "version": "1.1.1", 828 | "source": { 829 | "type": "git", 830 | "url": "https://github.com/sebastianbergmann/global-state.git", 831 | "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4" 832 | }, 833 | "dist": { 834 | "type": "zip", 835 | "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bc37d50fea7d017d3d340f230811c9f1d7280af4", 836 | "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4", 837 | "shasum": "" 838 | }, 839 | "require": { 840 | "php": ">=5.3.3" 841 | }, 842 | "require-dev": { 843 | "phpunit/phpunit": "~4.2" 844 | }, 845 | "suggest": { 846 | "ext-uopz": "*" 847 | }, 848 | "type": "library", 849 | "extra": { 850 | "branch-alias": { 851 | "dev-master": "1.0-dev" 852 | } 853 | }, 854 | "autoload": { 855 | "classmap": [ 856 | "src/" 857 | ] 858 | }, 859 | "notification-url": "https://packagist.org/downloads/", 860 | "license": [ 861 | "BSD-3-Clause" 862 | ], 863 | "authors": [ 864 | { 865 | "name": "Sebastian Bergmann", 866 | "email": "sebastian@phpunit.de" 867 | } 868 | ], 869 | "description": "Snapshotting of global state", 870 | "homepage": "http://www.github.com/sebastianbergmann/global-state", 871 | "keywords": [ 872 | "global state" 873 | ], 874 | "time": "2015-10-12 03:26:01" 875 | }, 876 | { 877 | "name": "sebastian/recursion-context", 878 | "version": "1.0.2", 879 | "source": { 880 | "type": "git", 881 | "url": "https://github.com/sebastianbergmann/recursion-context.git", 882 | "reference": "913401df809e99e4f47b27cdd781f4a258d58791" 883 | }, 884 | "dist": { 885 | "type": "zip", 886 | "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/913401df809e99e4f47b27cdd781f4a258d58791", 887 | "reference": "913401df809e99e4f47b27cdd781f4a258d58791", 888 | "shasum": "" 889 | }, 890 | "require": { 891 | "php": ">=5.3.3" 892 | }, 893 | "require-dev": { 894 | "phpunit/phpunit": "~4.4" 895 | }, 896 | "type": "library", 897 | "extra": { 898 | "branch-alias": { 899 | "dev-master": "1.0.x-dev" 900 | } 901 | }, 902 | "autoload": { 903 | "classmap": [ 904 | "src/" 905 | ] 906 | }, 907 | "notification-url": "https://packagist.org/downloads/", 908 | "license": [ 909 | "BSD-3-Clause" 910 | ], 911 | "authors": [ 912 | { 913 | "name": "Jeff Welch", 914 | "email": "whatthejeff@gmail.com" 915 | }, 916 | { 917 | "name": "Sebastian Bergmann", 918 | "email": "sebastian@phpunit.de" 919 | }, 920 | { 921 | "name": "Adam Harvey", 922 | "email": "aharvey@php.net" 923 | } 924 | ], 925 | "description": "Provides functionality to recursively process PHP variables", 926 | "homepage": "http://www.github.com/sebastianbergmann/recursion-context", 927 | "time": "2015-11-11 19:50:13" 928 | }, 929 | { 930 | "name": "sebastian/version", 931 | "version": "1.0.6", 932 | "source": { 933 | "type": "git", 934 | "url": "https://github.com/sebastianbergmann/version.git", 935 | "reference": "58b3a85e7999757d6ad81c787a1fbf5ff6c628c6" 936 | }, 937 | "dist": { 938 | "type": "zip", 939 | "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/58b3a85e7999757d6ad81c787a1fbf5ff6c628c6", 940 | "reference": "58b3a85e7999757d6ad81c787a1fbf5ff6c628c6", 941 | "shasum": "" 942 | }, 943 | "type": "library", 944 | "autoload": { 945 | "classmap": [ 946 | "src/" 947 | ] 948 | }, 949 | "notification-url": "https://packagist.org/downloads/", 950 | "license": [ 951 | "BSD-3-Clause" 952 | ], 953 | "authors": [ 954 | { 955 | "name": "Sebastian Bergmann", 956 | "email": "sebastian@phpunit.de", 957 | "role": "lead" 958 | } 959 | ], 960 | "description": "Library that helps with managing the version number of Git-hosted PHP projects", 961 | "homepage": "https://github.com/sebastianbergmann/version", 962 | "time": "2015-06-21 13:59:46" 963 | }, 964 | { 965 | "name": "symfony/yaml", 966 | "version": "v2.8.15", 967 | "source": { 968 | "type": "git", 969 | "url": "https://github.com/symfony/yaml.git", 970 | "reference": "befb26a3713c97af90d25dd12e75621ef14d91ff" 971 | }, 972 | "dist": { 973 | "type": "zip", 974 | "url": "https://api.github.com/repos/symfony/yaml/zipball/befb26a3713c97af90d25dd12e75621ef14d91ff", 975 | "reference": "befb26a3713c97af90d25dd12e75621ef14d91ff", 976 | "shasum": "" 977 | }, 978 | "require": { 979 | "php": ">=5.3.9" 980 | }, 981 | "type": "library", 982 | "extra": { 983 | "branch-alias": { 984 | "dev-master": "2.8-dev" 985 | } 986 | }, 987 | "autoload": { 988 | "psr-4": { 989 | "Symfony\\Component\\Yaml\\": "" 990 | }, 991 | "exclude-from-classmap": [ 992 | "/Tests/" 993 | ] 994 | }, 995 | "notification-url": "https://packagist.org/downloads/", 996 | "license": [ 997 | "MIT" 998 | ], 999 | "authors": [ 1000 | { 1001 | "name": "Fabien Potencier", 1002 | "email": "fabien@symfony.com" 1003 | }, 1004 | { 1005 | "name": "Symfony Community", 1006 | "homepage": "https://symfony.com/contributors" 1007 | } 1008 | ], 1009 | "description": "Symfony Yaml Component", 1010 | "homepage": "https://symfony.com", 1011 | "time": "2016-11-14 16:15:57" 1012 | } 1013 | ], 1014 | "aliases": [], 1015 | "minimum-stability": "stable", 1016 | "stability-flags": [], 1017 | "prefer-stable": false, 1018 | "prefer-lowest": false, 1019 | "platform": { 1020 | "php": ">=5.4" 1021 | }, 1022 | "platform-dev": [] 1023 | } 1024 | --------------------------------------------------------------------------------