Skip to content

Commit 6f5d0cd

Browse files
committedNov 13, 2016
Initiliazed
* Add `PromiseFactoryInterface` interface * Add Factory for GuzzleHttp and React lib * Add Tests * Add Documentation
1 parent 874a64b commit 6f5d0cd

12 files changed

+688
-0
lines changed
 

‎.gitignore

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
/vendor/
2+
composer.lock
3+
composer.phar
4+
phpunit.xml
5+
.php_cs.cache

‎.php_cs

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the PromiseFactory package.
5+
*
6+
* (c) McGWeb <http://github.com/mcg-web>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
require_once __DIR__.'/vendor/autoload.php';
13+
14+
use SLLH\StyleCIBridge\ConfigBridge;
15+
use Symfony\CS\Fixer\Contrib\HeaderCommentFixer;
16+
17+
$header = <<<EOF
18+
This file is part of the PromiseFactory package.
19+
20+
(c) McGWeb <http://github.com/mcg-web>
21+
22+
For the full copyright and license information, please view the LICENSE
23+
file that was distributed with this source code.
24+
EOF;
25+
26+
// PHP-CS-Fixer 1.x
27+
if (method_exists('Symfony\CS\Fixer\Contrib\HeaderCommentFixer', 'getHeader')) {
28+
HeaderCommentFixer::setHeader($header);
29+
}
30+
31+
$config = ConfigBridge::create();
32+
33+
// PHP-CS-Fixer 2.x
34+
if (method_exists($config, 'setRules')) {
35+
$config->setRules(array_merge($config->getRules(), [
36+
'header_comment' => ['header' => $header]
37+
]));
38+
}
39+
40+
return $config
41+
->setUsingCache(true)
42+
->fixers(array_merge($config->getFixers(), ['header_comment']))
43+
;

‎.styleci.yml

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
preset: psr2
2+
enabled:
3+
- ordered_use
4+
- short_array_syntax
5+
6+
disabled:
7+
- unalign_equals

‎.travis.yml

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
language: php
2+
3+
php:
4+
- nightly
5+
- hhvm
6+
- 5.5
7+
- 5.6
8+
- 7.0
9+
- 7.1
10+
11+
branches:
12+
only:
13+
- master
14+
- /^\d+\.\d+$/
15+
16+
allow_failures:
17+
- php: nightly
18+
19+
cache:
20+
directories:
21+
- $HOME/.composer/cache
22+
23+
before_install:
24+
- if [[ "$TRAVIS_PHP_VERSION" != "5.6" && "$TRAVIS_PHP_VERSION" != "hhvm" ]]; then phpenv config-rm xdebug.ini || true; fi
25+
- composer selfupdate
26+
- composer require "react/promise" "guzzlehttp/promises"
27+
28+
install: composer update --prefer-dist --no-interaction
29+
30+
script: if [ "$TRAVIS_PHP_VERSION" == "5.6" ]; then phpunit -d xdebug.max_nesting_level=1000 --debug --coverage-clover build/logs/clover.xml; else phpunit --debug; fi
31+
32+
after_success:
33+
- if [ "$TRAVIS_PHP_VERSION" == "5.6" ]; then composer require "satooshi/php-coveralls:^1.0" && travis_retry php vendor/bin/coveralls -v; fi

‎LICENSE.txt

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
The MIT License (MIT)
2+
3+
Copyright (c) McGWeb
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in
13+
all copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
THE SOFTWARE.

‎README.md

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# PromiseFactory
2+
3+
This library tries to create a simple promise factory standard while waiting for a psr.
4+
It Comes out of the box with factory for [ReactPhp/Promise](https://github.com/reactphp/promise) and [Guzzle/Promises](https://github.com/guzzle/promises).
5+
6+
[![Build Status](https://travis-ci.org/mcg-web/promise-factory.svg?branch=master)](https://travis-ci.org/mcg-web/promise-factory)
7+
[![Coverage Status](https://coveralls.io/repos/github/mcg-web/promise-factory/badge.svg?branch=master)](https://coveralls.io/github/mcg-web/promise-factory?branch=master)
8+
[![Latest Stable Version](https://poser.pugx.org/mcg-web/promise-factory/version)](https://packagist.org/packages/mcg-web/promise-factory)
9+
[![License](https://poser.pugx.org/mcg-web/promise-factory/license)](https://packagist.org/packages/mcg-web/promise-factory)
10+
11+
## Getting Started
12+
13+
First, install PromiseFactory using composer.
14+
15+
```sh
16+
composer require "mcg-web/promise-factory"
17+
```
18+
19+
Optional to use Guzzle:
20+
21+
```sh
22+
composer require "guzzlehttp/promises"
23+
```
24+
25+
Optional to use ReactPhp:
26+
27+
```sh
28+
composer require "react/promise"
29+
```
30+
31+
## Supported Factory
32+
33+
*Guzzle*: `McGWeb\PromiseFactory\Factory\GuzzleHttpPromiseFactory`
34+
35+
*ReactPhp*: `McGWeb\PromiseFactory\Factory\ReactPromiseFactory`
36+
37+
To use a custom Promise lib you can implement `McGWeb\PromiseFactory\PromiseFactoryInterface`
38+
39+
## License
40+
41+
McGWeb/PromiseFactory is released under the [MIT](https://github.com/mcg-web/promise-factory/blob/master/LICENSE) license.

‎composer.json

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"name": "mcg-web/promise-factory",
3+
"description": "This library tries to create a simple promise factory standard while waiting for a psr.",
4+
"type": "library",
5+
"autoload": {
6+
"psr-4": {
7+
"McGWeb\\PromiseFactory\\": "src/"
8+
}
9+
},
10+
"autoload-dev": {
11+
"psr-4": {
12+
"McGWeb\\PromiseFactory\\Test\\": "tests/"
13+
}
14+
},
15+
"require": {
16+
"php": "^5.5|^7.0"
17+
},
18+
"require-dev": {
19+
"phpunit/phpunit": "^4.1|^5.1"
20+
},
21+
"license": "MIT"
22+
}

‎phpunit.xml.dist

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
3+
<!-- http://phpunit.de/manual/4.1/en/appendixes.configuration.html -->
4+
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5+
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.1/phpunit.xsd"
6+
backupGlobals="false"
7+
colors="true"
8+
bootstrap="./vendor/autoload.php"
9+
>
10+
<php>
11+
<ini name="error_reporting" value="-1" />
12+
</php>
13+
14+
<testsuites>
15+
<testsuite name="Promise Factory Test Suite">
16+
<directory>./tests</directory>
17+
</testsuite>
18+
</testsuites>
19+
20+
<filter>
21+
<whitelist>
22+
<directory>./src</directory>
23+
<exclude>
24+
<directory>./vendor</directory>
25+
<directory>./tests</directory>
26+
</exclude>
27+
</whitelist>
28+
</filter>
29+
</phpunit>
+119
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the PromiseFactory package.
5+
*
6+
* (c) McGWeb <http://github.com/mcg-web>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace McGWeb\PromiseFactory\Factory;
13+
14+
use GuzzleHttp\Promise\FulfilledPromise;
15+
use GuzzleHttp\Promise\Promise;
16+
use GuzzleHttp\Promise\PromiseInterface;
17+
use GuzzleHttp\Promise\RejectedPromise;
18+
use McGWeb\PromiseFactory\PromiseFactoryInterface;
19+
20+
class GuzzleHttpPromiseFactory implements PromiseFactoryInterface
21+
{
22+
/**
23+
* @inheritdoc
24+
*
25+
* @return Promise
26+
*/
27+
public static function create(&$resolve = null, &$reject = null, callable $canceller = null)
28+
{
29+
$promise = new Promise(null, $canceller);
30+
31+
$reject = [$promise, 'reject'];
32+
$resolve = [$promise, 'resolve'];
33+
34+
return $promise;
35+
}
36+
37+
/**
38+
* @inheritdoc
39+
*
40+
* @return FulfilledPromise a full filed Promise
41+
*/
42+
public static function createResolve($promiseOrValue)
43+
{
44+
$promise = \GuzzleHttp\Promise\promise_for($promiseOrValue);
45+
46+
return $promise;
47+
}
48+
49+
/**
50+
* @inheritdoc
51+
*
52+
* @return RejectedPromise a rejected promise
53+
*/
54+
public static function createReject($promiseOrValue)
55+
{
56+
$promise = \GuzzleHttp\Promise\rejection_for($promiseOrValue);
57+
58+
return $promise;
59+
}
60+
61+
/**
62+
* @inheritdoc
63+
*
64+
* @return Promise
65+
*/
66+
public static function createAll($promisesOrValues)
67+
{
68+
$promise = \GuzzleHttp\Promise\all($promisesOrValues);
69+
70+
return $promise;
71+
}
72+
73+
/**
74+
* @inheritdoc
75+
*/
76+
public static function isPromise($value, $strict = false)
77+
{
78+
$isStrictPromise = $value instanceof PromiseInterface;
79+
80+
if ($strict) {
81+
return $isStrictPromise;
82+
}
83+
84+
return $isStrictPromise || is_callable([$value, 'then']);
85+
}
86+
87+
/**
88+
* @inheritdoc
89+
*/
90+
public static function await($promise = null, $unwrap = false)
91+
{
92+
$resolvedValue = null;
93+
94+
if (null !== $promise) {
95+
$exception = null;
96+
if (!static::isPromise($promise)) {
97+
throw new \InvalidArgumentException(sprintf('The "%s" method must be called with a Promise ("then" method).', __METHOD__));
98+
}
99+
/** @var Promise $promise */
100+
$promise->then(function ($values) use (&$resolvedValue) {
101+
$resolvedValue = $values;
102+
}, function ($reason) use (&$exception) {
103+
$exception = $reason;
104+
});
105+
\GuzzleHttp\Promise\queue()->run();
106+
107+
if ($exception instanceof \Exception) {
108+
if (!$unwrap) {
109+
return $exception;
110+
}
111+
throw $exception;
112+
}
113+
} else {
114+
\GuzzleHttp\Promise\queue()->run();
115+
}
116+
117+
return $resolvedValue;
118+
}
119+
}

‎src/Factory/ReactPromiseFactory.php

+110
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the PromiseFactory package.
5+
*
6+
* (c) McGWeb <http://github.com/mcg-web>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace McGWeb\PromiseFactory\Factory;
13+
14+
use McGWeb\PromiseFactory\PromiseFactoryInterface;
15+
use React\Promise\Deferred;
16+
use React\Promise\FulfilledPromise;
17+
use React\Promise\Promise;
18+
use React\Promise\PromiseInterface;
19+
use React\Promise\RejectedPromise;
20+
21+
class ReactPromiseFactory implements PromiseFactoryInterface
22+
{
23+
/**
24+
* @inheritdoc
25+
*
26+
* @return Promise
27+
*/
28+
public static function create(&$resolve = null, &$reject = null, callable $canceller = null)
29+
{
30+
$deferred = new Deferred($canceller);
31+
32+
$reject = [$deferred, 'reject'];
33+
$resolve = [$deferred, 'resolve'];
34+
35+
return $deferred->promise();
36+
}
37+
38+
/**
39+
* @inheritdoc
40+
*
41+
* @return FulfilledPromise a full filed Promise
42+
*/
43+
public static function createResolve($promiseOrValue)
44+
{
45+
return \React\Promise\resolve($promiseOrValue);
46+
}
47+
48+
/**
49+
* @inheritdoc
50+
*
51+
* @return RejectedPromise a rejected promise
52+
*/
53+
public static function createReject($promiseOrValue)
54+
{
55+
return \React\Promise\reject($promiseOrValue);
56+
}
57+
58+
/**
59+
* @inheritdoc
60+
*
61+
* @return Promise
62+
*/
63+
public static function createAll($promisesOrValues)
64+
{
65+
return \React\Promise\all($promisesOrValues);
66+
}
67+
68+
/**
69+
* @inheritdoc
70+
*/
71+
public static function isPromise($value, $strict = false)
72+
{
73+
$isStrictPromise = $value instanceof PromiseInterface;
74+
75+
if ($strict) {
76+
return $isStrictPromise;
77+
}
78+
79+
return $isStrictPromise || is_callable([$value, 'then']);
80+
}
81+
82+
/**
83+
* @inheritdoc
84+
*/
85+
public static function await($promise = null, $unwrap = false)
86+
{
87+
if (null === $promise) {
88+
return null;
89+
}
90+
91+
$resolvedValue = null;
92+
$exception = null;
93+
if (!static::isPromise($promise)) {
94+
throw new \InvalidArgumentException(sprintf('The "%s" method must be called with a Promise ("then" method).', __METHOD__));
95+
}
96+
$promise->then(function ($values) use (&$resolvedValue) {
97+
$resolvedValue = $values;
98+
}, function ($reason) use (&$exception) {
99+
$exception = $reason;
100+
});
101+
if ($exception instanceof \Exception) {
102+
if (!$unwrap) {
103+
return $exception;
104+
}
105+
throw $exception;
106+
}
107+
108+
return $resolvedValue;
109+
}
110+
}

‎src/PromiseFactoryInterface.php

+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the PromiseFactory package.
5+
*
6+
* (c) McGWeb <http://github.com/mcg-web>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace McGWeb\PromiseFactory;
13+
14+
interface PromiseFactoryInterface
15+
{
16+
/**
17+
* Creates a Promise
18+
*
19+
* @param $resolve
20+
* @param $reject
21+
* @param callable $canceller
22+
*
23+
* @return mixed a Promise
24+
*/
25+
public static function create(&$resolve = null, &$reject = null, callable $canceller = null);
26+
27+
/**
28+
* Creates a full filed Promise for a value if the value is not a promise.
29+
*
30+
* @param mixed $promiseOrValue
31+
*
32+
* @return mixed a full filed Promise
33+
*/
34+
public static function createResolve($promiseOrValue);
35+
36+
/**
37+
* Creates a rejected promise for a reason if the reason is not a promise. If
38+
* the provided reason is a promise, then it is returned as-is.
39+
*
40+
* @param mixed $promiseOrValue
41+
*
42+
* @return mixed a rejected promise
43+
*/
44+
public static function createReject($promiseOrValue);
45+
46+
/**
47+
* Given an array of promises, return a promise that is fulfilled when all the
48+
* items in the array are fulfilled.
49+
*
50+
* @param mixed $promisesOrValues Promises or values.
51+
*
52+
* @return mixed a Promise
53+
*/
54+
public static function createAll($promisesOrValues);
55+
56+
57+
/**
58+
* Check if value is a promise
59+
*
60+
* @param mixed $value
61+
* @param bool $strict
62+
*
63+
* @return bool
64+
*/
65+
public static function isPromise($value, $strict = false);
66+
67+
/**
68+
* wait for Promise to complete
69+
* @param mixed $promise
70+
* @param bool $unwrap
71+
*
72+
* @return mixed
73+
*/
74+
public static function await($promise = null, $unwrap = false);
75+
}

‎tests/FactoryTest.php

+183
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the PromiseFactory package.
5+
*
6+
* (c) McGWeb <http://github.com/mcg-web>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace McGWeb\PromiseFactory\Tests;
13+
14+
use McGWeb\PromiseFactory\Factory\GuzzleHttpPromiseFactory;
15+
use McGWeb\PromiseFactory\Factory\ReactPromiseFactory;
16+
use McGWeb\PromiseFactory\PromiseFactoryInterface;
17+
18+
class FactoryTest extends \PHPUnit_Framework_TestCase
19+
{
20+
/**
21+
* @dataProvider factoryDataProvider
22+
* @param string $promiseClass
23+
* @param PromiseFactoryInterface $factory
24+
* @param string $context
25+
*/
26+
public function testCreate(PromiseFactoryInterface $factory, $context, $promiseClass)
27+
{
28+
$promise = $factory->create($resolve, $reject);
29+
30+
$this->assertInstanceOf($promiseClass, $promise, $context);
31+
$this->assertTrue(is_callable($resolve), $context);
32+
$this->assertTrue(is_callable($reject), $context);
33+
}
34+
35+
/**
36+
* @dataProvider factoryDataProvider
37+
* @param PromiseFactoryInterface $factory
38+
* @param $message
39+
*/
40+
public function testResolveCreatedPromise(PromiseFactoryInterface $factory, $message)
41+
{
42+
$promise = $factory->create($resolve, $reject);
43+
$expectResolvedValue = 'Resolve value';
44+
$resolve($expectResolvedValue);
45+
$resolvedValue = $factory->await($promise);
46+
47+
$this->assertEquals($expectResolvedValue, $resolvedValue, $message);
48+
}
49+
50+
/**
51+
* @dataProvider factoryDataProvider
52+
* @param PromiseFactoryInterface $factory
53+
* @param string $context
54+
*/
55+
public function testRejectCreatedPromise(PromiseFactoryInterface $factory, $context)
56+
{
57+
$promise = $factory->create($resolve, $reject);
58+
59+
$expectRejectionReason = new \Exception('Error!');
60+
$reject($expectRejectionReason);
61+
62+
$rejectionReason = $factory->await($promise, false);
63+
$this->assertEquals($expectRejectionReason, $rejectionReason, $context);
64+
}
65+
66+
/**
67+
* @dataProvider factoryDataProvider
68+
* @param PromiseFactoryInterface $factory
69+
* @param string $context
70+
* @param string $promiseClass
71+
*/
72+
public function testCreateAll(PromiseFactoryInterface $factory, $context, $promiseClass)
73+
{
74+
$values = ['A', 'B', 'C'];
75+
$promise = $factory->createAll($values);
76+
$this->assertInstanceOf($promiseClass, $promise, $context);
77+
78+
$resolvedValue = $factory->await($promise);
79+
80+
$this->assertEquals($values, $resolvedValue, $context);
81+
}
82+
83+
/**
84+
* @dataProvider factoryDataProvider
85+
* @param PromiseFactoryInterface $factory
86+
* @param string $context
87+
* @param string $promiseClass
88+
*/
89+
public function testCreateResolve(PromiseFactoryInterface $factory, $context, $promiseClass)
90+
{
91+
$value = 'resolved!';
92+
$promise = $factory->createResolve($value);
93+
$this->assertInstanceOf($promiseClass, $promise, $context);
94+
95+
$resolvedValue = $factory->await($promise);
96+
$this->assertEquals($value, $resolvedValue, $context);
97+
}
98+
99+
/**
100+
* @dataProvider factoryDataProvider
101+
* @param PromiseFactoryInterface $factory
102+
* @param string $context
103+
* @param string $promiseClass
104+
*/
105+
public function testCreatedReject(PromiseFactoryInterface $factory, $context, $promiseClass)
106+
{
107+
$expectRejectionReason = new \Exception('Error!');
108+
$promise = $factory->createReject($expectRejectionReason);
109+
$this->assertInstanceOf($promiseClass, $promise, $context);
110+
111+
$rejectionReason = $factory->await($promise, false);
112+
$this->assertEquals($expectRejectionReason, $rejectionReason, $context);
113+
}
114+
115+
/**
116+
* @dataProvider factoryDataProvider
117+
* @param PromiseFactoryInterface $factory
118+
* @param string $context
119+
*/
120+
public function testIsPromise(PromiseFactoryInterface $factory, $context)
121+
{
122+
$promise = $factory->create();
123+
124+
$this->assertTrue($factory->isPromise($promise, true), $context);
125+
$this->assertFalse($factory->isPromise([]), $context);
126+
$this->assertFalse($factory->isPromise(new \stdClass()), $context);
127+
}
128+
129+
/**
130+
* @dataProvider factoryDataProvider
131+
* @param PromiseFactoryInterface $factory
132+
* @param string $context
133+
*/
134+
public function testAwaitWithoutPromise(PromiseFactoryInterface $factory, $context)
135+
{
136+
$expected = 'expected value';
137+
$promise = $factory->createResolve($expected);
138+
$actual = null;
139+
140+
$promise->then(function ($value) use (&$actual) {
141+
$actual = $value;
142+
});
143+
144+
$factory->await();
145+
146+
$this->assertEquals($expected, $actual, $context);
147+
}
148+
149+
/**
150+
* @dataProvider factoryDataProvider
151+
* @param PromiseFactoryInterface $factory
152+
*
153+
* @expectedException \Exception
154+
* @expectedExceptionMessage error!
155+
*/
156+
public function testAwaitWithUnwrap(PromiseFactoryInterface $factory)
157+
{
158+
$expected = new \Exception('error!');
159+
$promise = $factory->createReject($expected);
160+
161+
$factory->await($promise, true);
162+
}
163+
164+
/**
165+
* @dataProvider factoryDataProvider
166+
* @param PromiseFactoryInterface $factory
167+
*
168+
* @expectedException \InvalidArgumentException
169+
* @expectedExceptionMessage ::await" method must be called with a Promise ("then" method).
170+
*/
171+
public function testAwaitWithInvalidPromise(PromiseFactoryInterface $factory)
172+
{
173+
$factory->await(new \stdClass(), true);
174+
}
175+
176+
public function factoryDataProvider()
177+
{
178+
return [
179+
[new GuzzleHttpPromiseFactory(), 'guzzle', '\\GuzzleHttp\\Promise\\PromiseInterface'],
180+
[new ReactPromiseFactory(), 'react', '\\React\\Promise\\PromiseInterface'],
181+
];
182+
}
183+
}

0 commit comments

Comments
 (0)
Please sign in to comment.