├── .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 | [](https://travis-ci.org/joshdifabio/resource-pool)
4 | [](http://codecov.io/github/joshdifabio/resource-pool)
5 | [](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 |
--------------------------------------------------------------------------------