Passed
Pull Request — master (#13)
by
unknown
01:39
created

GuzzleMiddleware::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
eloc 3
c 1
b 0
f 1
dl 0
loc 5
rs 10
cc 1
nc 1
nop 1
1
<?php declare(strict_types=1);
2
3
namespace LeoCarmo\CircuitBreaker;
4
5
use Psr\Http\Message\RequestInterface;
6
use Psr\Http\Message\ResponseInterface;
7
8
class GuzzleMiddleware
9
{
10
    private const DEFAULT_SUCCESS_FAMILY = 200;
11
12
    private const DEFAULT_IGNORE_FAMILY = 300;
13
14
    protected CircuitBreaker $circuitBreaker;
15
16
    protected array $customSuccessCodes = [];
17
18
    protected array $customIgnoreCodes = [];
19
20
    protected array $customSuccessFamily = [];
21
22
    protected array $customIgnoreFamily = [];
23
24
    public function __construct(CircuitBreaker $circuitBreaker)
25
    {
26
        $this->circuitBreaker = $circuitBreaker;
27
        $this->setCustomSuccessFamily(self::DEFAULT_SUCCESS_FAMILY);
28
        $this->setCustomIgnoreFamily(self::DEFAULT_IGNORE_FAMILY);
29
    }
30
31
    public function setCustomSuccessCodes(array $codes): void
32
    {
33
        $this->customSuccessCodes = $codes;
34
    }
35
36
    private function validateCodeStatusFamily(int $family): void
37
    {
38
        $num_length = strlen((string) $family);
39
        $initial_number = substr((string) $family, 0, 1);
40
        if (($num_length !== 3) && ($initial_number > 0 && $initial_number < 6)) {
41
            throw new \InvalidArgumentException('This code status family is not valid.');
42
        }
43
    }
44
45
    public function setCustomSuccessFamily(int $family): void
46
    {
47
        $this->validateCodeStatusFamily($family);
48
        $initial_number = substr((string) $family, 0, 1);
49
        $this->customSuccessFamily[] = $initial_number;
50
    }
51
52
    public function setCustomIgnoreFamily(int $family): void
53
    {
54
        $this->validateCodeStatusFamily($family);
55
        $initial_number = substr((string) $family, 0, 1);
56
        $this->customIgnoreFamily[] = $initial_number;
57
    }
58
59
    public function setCustomIgnoreCodes(array $codes): void
60
    {
61
        $this->customIgnoreCodes = $codes;
62
    }
63
64
    public function __invoke(callable $handler): \Closure
65
    {
66
        return function (RequestInterface $request, array $options) use ($handler) {
67
68
            if (! $this->circuitBreaker->isAvailable()) {
69
                throw new CircuitBreakerException(
70
                    sprintf('"%s" is not available', $this->circuitBreaker->getService())
71
                );
72
            }
73
74
            $promise = $handler($request, $options);
75
76
            return $promise->then(
77
                function (ResponseInterface $response) {
78
                    $this->executeCircuitBreakerOnResponse($response);
79
80
                    return $response;
81
                },
82
                function (\Throwable $exception) {
83
                    $this->circuitBreaker->failure();
84
                    throw $exception;
85
                }
86
            );
87
        };
88
    }
89
90
    protected function executeCircuitBreakerOnResponse(ResponseInterface $response): void
91
    {
92
        $statusCode = $response->getStatusCode();
93
94
        if ($this->isIgnoredStatus($statusCode)) {
95
            return;
96
        }
97
98
        if (! $this->isStatusCodeRangeValid($statusCode)) {
99
            $this->circuitBreaker->failure();
100
            return;
101
        }
102
103
        if ($this->isStatusCodeSuccess($statusCode)) {
104
            $this->circuitBreaker->success();
105
            return;
106
        }
107
108
        $this->circuitBreaker->failure();
109
    }
110
111
    protected function isStatusCodeFamilyToIgnore(int $statusCode): bool
112
    {
113
        $initial_number = substr((string) $statusCode, 0, 1);
114
        return (in_array($initial_number, $this->customIgnoreFamily));
115
    }
116
117
    protected  function isIgnoredStatus(int $statusCode): bool
118
    {
119
        return $this->isStatusCodeFamilyToIgnore($statusCode) || in_array($statusCode, $this->customIgnoreCodes);
120
    }
121
122
    protected function isStatusCodeRangeValid(int $statusCode): bool
123
    {
124
        return ($statusCode >= 100 && $statusCode < 600);
125
    }
126
127
    protected function isStatusCodeFamilyToSuccess(int $statusCode): bool
128
    {
129
        $initial_number = substr((string) $statusCode, 0, 1);
130
        return (in_array($initial_number, $this->customSuccessFamily));
131
    }
132
133
    protected function isStatusCodeSuccess(int $statusCode): bool
134
    {
135
        return ($this->isStatusCodeFamilyToSuccess($statusCode) || in_array($statusCode, $this->customSuccessCodes));
136
    }
137
}