Test Setup Failed
Branch 0.x (13f0ad)
by Pavel
13:06 queued 01:31
created

LocatorChain   A

Complexity

Total Complexity 11

Size/Duplication

Total Lines 128
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 11
eloc 38
dl 0
loc 128
rs 10
c 0
b 0
f 0

4 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
A poll() 0 36 4
A locate() 0 15 4
A addLocator() 0 7 2
1
<?php
2
3
/*
4
 * This file is part of the Veslo project <https://github.com/symfony-doge/veslo>.
5
 *
6
 * (C) 2019 Pavel Petrov <[email protected]>.
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 *
11
 * @license https://opensource.org/licenses/GPL-3.0 GPL-3.0
12
 */
13
14
declare(strict_types=1);
15
16
namespace Veslo\AppBundle\Http\Proxy;
17
18
use Ds\PriorityQueue;
19
use Exception;
20
use Psr\Log\LoggerInterface;
21
use RuntimeException;
22
23
/**
24
 * Aggregates proxy locators and polls each of them one by one until proxy list is returned
25
 *
26
 * Note: although it is technically possible to use LocatorChain instance as a part of other locator, this class
27
 * is not directly designed to be nested one (so it is placed outside of locator's namespace and named unconventionally)
28
 */
29
class LocatorChain implements LocatorInterface
30
{
31
    /**
32
     * Dispatches a failed poll message for locators marked by "isImportant" flag
33
     *
34
     * @var LoggerInterface
35
     */
36
    private $logger;
37
38
    /**
39
     * Priority queue with proxy locators
40
     *
41
     * It is not really needed to use data structures from php-ds extension in this context due to low objects count,
42
     * it's mostly for learning purposes... also we have a little bonus - priority managing is delegated to the C-layer
43
     * instead of PHP code, so a compiler pass can be simplified.
44
     *
45
     * @var PriorityQueue<LocatorInterface>
46
     *
47
     * @see https://github.com/php-ds
48
     */
49
    private $proxyLocators;
50
51
    /**
52
     * Cached polling result for current process
53
     * (otherwise we should call a `PriorityQueue::copy()` and it can be redundant)
54
     *
55
     * @var string[]
56
     */
57
    private $_proxyList;
58
59
    /**
60
     * LocatorChain constructor.
61
     *
62
     * @param LoggerInterface $logger Dispatches a failed poll message for locators marked by "isImportant" flag
63
     */
64
    public function __construct(LoggerInterface $logger)
65
    {
66
        $this->logger = $logger;
67
68
        $this->proxyLocators = new PriorityQueue();
69
        $this->_proxyList    = null;
70
    }
71
72
    /**
73
     * Adds a proxy locator to the list for polling
74
     *
75
     * @param LocatorInterface $proxyLocator Provides a list with proxies available for http requests
76
     * @param int              $priority     Locator priority in polling loop
77
     * @param bool             $isImportant  Whenever a critical message should be raised if locator fails to locate a
78
     *                                       proxy list
79
     *
80
     * @return void
81
     */
82
    public function addLocator(LocatorInterface $proxyLocator, int $priority, bool $isImportant = false): void
83
    {
84
        if (null !== $this->_proxyList) {
85
            throw new RuntimeException('Unable to add a new locator after locate() execution.');
86
        }
87
88
        $this->proxyLocators->push([$proxyLocator, ['isImportant' => $isImportant]], $priority);
89
    }
90
91
    /**
92
     * {@inheritdoc}
93
     *
94
     * @see PriorityQueue::getIterator()
95
     */
96
    public function locate(): iterable
97
    {
98
        if (null !== $this->_proxyList) {
99
            return $this->_proxyList;
100
        }
101
102
        foreach ($this->proxyLocators as list($proxyLocator, $pollParameters)) {
103
            $proxyList = $this->poll($proxyLocator, $pollParameters);
104
105
            if (0 < count($proxyList)) {
106
                return $this->_proxyList = $proxyList;
107
            }
108
        }
109
110
        return $this->_proxyList = [];
111
    }
112
113
    /**
114
     * Polls locator for a proxy list
115
     *
116
     * @param LocatorInterface $proxyLocator Provides a list with proxies available for http requests
117
     * @param array            $parameters   Parameters for locator polling
118
     *
119
     * @return string[]
120
     */
121
    private function poll(LocatorInterface $proxyLocator, array $parameters): array
122
    {
123
        $locatorClass = get_class($proxyLocator);
124
        $isImportant  = $parameters['isImportant'];
125
        $pollContext  = ['locatorClass' => $locatorClass, 'isImportant' => $isImportant];
126
127
        $this->logger->debug('Polling locator for a proxy list.', $pollContext);
128
129
        $proxyList = [];
0 ignored issues
show
Unused Code introduced by
The assignment to $proxyList is dead and can be removed.
Loading history...
130
131
        try {
132
            $proxyList = $proxyLocator->locate();
133
        } catch (Exception $e) {
134
            $message          = $e->getMessage();
135
            $pollContextError = array_merge(['message' => $message], $pollContext);
136
137
            $this->logger->error(
138
                'An error has been occurred while polling locator for a proxy list.',
139
                $pollContextError
140
            );
141
        }
142
143
        $isProxyListFound = (0 < count($proxyList));
144
145
        if ($isProxyListFound) {
146
            $pollContextFound = array_merge(['proxies' => $proxyList], $pollContext);
147
            $this->logger->debug('Proxy list has been located.', $pollContextFound);
148
149
            return $proxyList;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $proxyList returns the type iterable which is incompatible with the type-hinted return array.
Loading history...
150
        }
151
152
        if ($isImportant) {
153
            $this->logger->critical("Proxy locator with 'isImportant' flag didn't provide a proxy list.", $pollContext);
154
        }
155
156
        return [];
157
    }
158
}
159