Skip to content

Commit c9ee027

Browse files
authored
F/retry function wrapper (#47)
* disable apigen * implement RetryFunctionWrapper
1 parent 4e1769a commit c9ee027

File tree

4 files changed

+143
-5
lines changed

4 files changed

+143
-5
lines changed

.travis.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ script:
1717
- vendor/phpunit/phpunit/phpunit --coverage-clover build/logs/clover.xml
1818
after_script:
1919
- vendor/bin/test-reporter
20-
after_success:
21-
- bash .travis_after_success.sh
20+
#after_success:
21+
#- bash .travis_after_success.sh
2222
addons:
2323
code_climate:
2424
repo_token: "$CODECLIMATE_TOKEN"

composer.json

-3
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,6 @@
1313
"mockery/mockery": "1.1.*",
1414
"codeclimate/php-test-reporter": "dev-master",
1515
"mikey179/vfsStream": "1.6.*",
16-
"apigen/apigen": "dev-master",
17-
"phpdocumentor/reflection-docblock": "4.1.*",
18-
"roave/better-reflection": "dev-master#c87d856",
1916
"guzzlehttp/guzzle": "~6.0",
2017
"nannehuiges/jsend": "2.1.*",
2118
"monolog/monolog": "1.*",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
<?php
2+
3+
4+
namespace SAREhub\Commons\Misc;
5+
6+
7+
class RetryFunctionWrapper
8+
{
9+
/**
10+
* @var callable
11+
*/
12+
private $callback;
13+
14+
/**
15+
* @var string
16+
*/
17+
private $expectedErrorClasses;
18+
19+
/**
20+
* @var int
21+
*/
22+
private $maxRetries;
23+
24+
/**
25+
* @var float
26+
*/
27+
private $initialWait;
28+
29+
/**
30+
* @var int
31+
*/
32+
private $exponent;
33+
34+
/**
35+
* @var int
36+
*/
37+
private $leftRetries;
38+
39+
/**
40+
* @var float
41+
*/
42+
private $currentWait;
43+
44+
public function __construct(callable $callback, array $expectedErrorClasses = [], int $maxRetries = 3, float $initialWait = 1.0, int $exponent = 2)
45+
{
46+
$this->callback = $callback;
47+
$this->expectedErrorClasses = $expectedErrorClasses;
48+
$this->maxRetries = $this->leftRetries = $maxRetries;
49+
$this->initialWait = $this->currentWait = $initialWait;
50+
$this->exponent = $exponent;
51+
}
52+
53+
/**
54+
* @throws \Exception
55+
*/
56+
public function executeInOnePass()
57+
{
58+
while ($this->leftRetries > 0) {
59+
$result = $this->executeNextTry();
60+
if ($result["success"]) {
61+
return $result["data"];
62+
}
63+
}
64+
}
65+
66+
/**
67+
* @return array ["success" => bool, "data" => mixed]
68+
* @throws \Exception
69+
*/
70+
public function executeNextTry(): array
71+
{
72+
try {
73+
--$this->leftRetries;
74+
$callback = $this->callback;
75+
$data = $callback();
76+
$this->currentWait = $this->initialWait;
77+
$this->leftRetries = $this->maxRetries;
78+
return [
79+
"success" => true,
80+
"data" => $data
81+
];
82+
} catch (\Exception $e) {
83+
$errors = class_parents($e);
84+
array_push($errors, get_class($e));
85+
if (!array_intersect($errors, $this->expectedErrorClasses) || $this->leftRetries === 0) {
86+
throw $e;
87+
}
88+
usleep($this->currentWait * 1E6);
89+
$this->currentWait = $this->initialWait * $this->exponent;
90+
return [
91+
"success" => false,
92+
"data" => null
93+
];
94+
}
95+
}
96+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?php
2+
3+
namespace SAREhub\Commons\Misc;
4+
5+
6+
use Mockery\Adapter\Phpunit\MockeryPHPUnitIntegration;
7+
use PHPUnit\Framework\TestCase;
8+
use SAREhub\Commons\Test\CallableMock;
9+
10+
class RetryFunctionWrapperTest extends TestCase
11+
{
12+
use MockeryPHPUnitIntegration;
13+
14+
public function testExecuteInOnePass()
15+
{
16+
$callback = CallableMock::create();
17+
18+
$callback->expects("__invoke")->andReturn(true);
19+
$wrapper = new RetryFunctionWrapper($callback);
20+
$this->assertTrue($wrapper->executeInOnePass());
21+
}
22+
23+
public function testExecuteInOnePassWhenExpectedExceptionOccur()
24+
{
25+
$callback = CallableMock::create();
26+
27+
$callback->expects("__invoke")->andThrow(new \InvalidArgumentException());
28+
$callback->expects("__invoke")->andReturn(true);
29+
$wrapper = new RetryFunctionWrapper($callback, [\InvalidArgumentException::class]);
30+
$this->assertTrue($wrapper->executeInOnePass());
31+
}
32+
33+
public function testExecuteInOnePassWhenUsedAllAttempts()
34+
{
35+
$callback = CallableMock::create();
36+
37+
$callback->expects("__invoke")->andThrow(new \InvalidArgumentException());
38+
$callback->expects("__invoke")->andThrow(new \InvalidArgumentException());
39+
$callback->expects("__invoke")->never();
40+
$wrapper = new RetryFunctionWrapper($callback, [\InvalidArgumentException::class], 2);
41+
42+
$this->expectException(\InvalidArgumentException::class);
43+
$wrapper->executeInOnePass();
44+
}
45+
}

0 commit comments

Comments
 (0)