ThrottleMiddleware   A
last analyzed

Complexity

Total Complexity 11

Size/Duplication

Total Lines 103
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 6

Importance

Changes 0
Metric Value
wmc 11
lcom 2
cbo 6
dl 0
loc 103
rs 10
c 0
b 0
f 0

5 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
A registerConfiguration() 0 4 1
A __invoke() 0 12 3
B processConfiguration() 0 30 4
A sleep() 0 11 2
1
<?php
2
3
namespace BenTools\GuzzleHttp\Middleware;
4
5
use BenTools\GuzzleHttp\Middleware\Storage\Adapter\ArrayAdapter;
6
use BenTools\GuzzleHttp\Middleware\Storage\Counter;
7
use BenTools\GuzzleHttp\Middleware\Storage\ThrottleStorageInterface;
8
use Psr\Http\Message\RequestInterface;
9
use Psr\Log\LoggerInterface;
10
use Psr\Log\LogLevel;
11
use Psr\Log\NullLogger;
12
13
class ThrottleMiddleware
14
{
15
    /**
16
     * @var ThrottleConfiguration[]
17
     */
18
    private $configurations = [];
19
20
    /**
21
     * @var ThrottleStorageInterface
22
     */
23
    private $storage;
24
25
    /**
26
     * @var LoggerInterface
27
     */
28
    private $logger;
29
30
    /**
31
     * @var string
32
     */
33
    private $logLevel;
34
35
    /**
36
     * ThrottleMiddleware constructor.
37
     * @param ThrottleStorageInterface $storage
38
     * @param LoggerInterface|null     $logger
39
     * @param string                   $logLevel
40
     */
41
    public function __construct(ThrottleStorageInterface $storage = null, LoggerInterface $logger = null, string $logLevel = LogLevel::INFO)
42
    {
43
        $this->storage = $storage ?? new ArrayAdapter();
44
        $this->logger = $logger ?? new NullLogger();
45
        $this->logLevel = $logLevel;
46
    }
47
48
    /**
49
     * @param ThrottleConfiguration $configuration
50
     */
51
    public function registerConfiguration(ThrottleConfiguration $configuration)
52
    {
53
        $this->configurations[$configuration->getStorageKey()] = $configuration;
54
    }
55
56
57
    public function __invoke(callable $handler)
58
    {
59
        return function (RequestInterface $request, array $options) use ($handler) {
60
            foreach ($this->configurations as $configuration) {
61
                if ($configuration->matchRequest($request)) {
62
                    $this->processConfiguration($configuration);
63
                    break;
64
                }
65
            }
66
            return $handler($request, $options);
67
        };
68
    }
69
70
    private function processConfiguration(ThrottleConfiguration $configuration)
71
    {
72
        try {
73
            $counter = $this->storage->getCounter($configuration->getStorageKey());
74
        } catch (\TypeError $e) {
75
            $counter = new Counter($configuration->getDuration());
76
        }
77
78
        if (!$counter->isExpired()) {
79
            if ($counter->count() >= $configuration->getMaxRequests()) {
80
                $this->logger->log(
81
                    $this->logLevel,
82
                    sprintf(
83
                        '%d out of %d requests reach. Sleeping %s seconds before trying again...',
84
                        $counter->count(),
85
                        $configuration->getMaxRequests(),
86
                        $counter->getRemainingTime()
87
                    )
88
                );
89
                $this->sleep($counter->getRemainingTime());
90
                $this->processConfiguration($configuration);
91
                return;
92
            }
93
        } else {
94
            $counter->reset();
95
        }
96
97
        $counter->increment();
98
        $this->storage->saveCounter($configuration->getStorageKey(), $counter, $configuration->getDuration());
99
    }
100
101
    /**
102
     * @param float $value
103
     */
104
    private function sleep(float $value)
105
    {
106
        $values = explode('.', (string) $value);
107
        $seconds = array_shift($values);
108
        $milliseconds = array_shift($values);
109
        \sleep($seconds);
110
        if (null !== $milliseconds) {
111
            $milliseconds = ((float) sprintf('0.%s', $milliseconds)) * 1000;
112
            usleep($milliseconds * 1000);
113
        }
114
    }
115
}
116