Passed
Branch 0.x (5dcc76)
by Pavel
03:30
created

UriLocator::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 6
dl 0
loc 10
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 3
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\Locator;
17
18
use Exception;
19
use Psr\Log\LoggerInterface;
20
use Symfony\Component\OptionsResolver\OptionsResolver;
21
use Symfony\Component\Serializer\Encoder\DecoderInterface;
22
use Veslo\AppBundle\Cache\CacherInterface;
23
use Veslo\AppBundle\Exception\Http\Proxy\Locator\BadProxyListUriException;
24
use Veslo\AppBundle\Http\Proxy\LocatorInterface;
25
26
/**
27
 * Uses URI string to fetch a proxy list from external source
28
 *
29
 * Note: you should manage to use an algorithm that doesn't depend on this locator itself
30
 * to avoid a cycling references while resolving a proxy list.
31
 */
32
class UriLocator implements LocatorInterface
33
{
34
    /**
35
     * Logger as it is
36
     *
37
     * @var LoggerInterface
38
     */
39
    private $logger;
40
41
    /**
42
     * Decodes a string into PHP data
43
     *
44
     * @var DecoderInterface
45
     */
46
    private $contentsDecoder;
47
48
    /**
49
     * Saves a proxy list in the cache and invalidates it by demand
50
     *
51
     * @var CacherInterface|null
52
     */
53
    private $proxyCacher;
54
55
    /**
56
     * Options for URI proxy locator
57
     *
58
     * Example:
59
     * ```
60
     * [
61
     *     'uri' => 'https://proxy-provider.ltd/my_proxy_list',
62
     *     'format' => 'json',
63
     *     'decoder_context' => []
64
     * ]
65
     * ```
66
     *
67
     * @var array
68
     */
69
    private $options;
70
71
    /**
72
     * UriLocator constructor.
73
     *
74
     * @param LoggerInterface  $logger          Logger as it is
75
     * @param DecoderInterface $contentsDecoder Decodes a string into PHP data
76
     * @param array            $options         Options for URI proxy locator
77
     */
78
    public function __construct(LoggerInterface $logger, DecoderInterface $contentsDecoder, array $options)
79
    {
80
        $this->logger          = $logger;
81
        $this->contentsDecoder = $contentsDecoder;
82
        $this->proxyCacher     = null;
83
84
        $optionsResolver = new OptionsResolver();
85
        $this->configureOptions($optionsResolver);
86
87
        $this->options = $optionsResolver->resolve($options);
88
    }
89
90
    /**
91
     * {@inheritdoc}
92
     *
93
     * @throws BadProxyListUriException
94
     */
95
    public function locate(): iterable
96
    {
97
        $proxyListUri = $this->options['uri'];
98
        $uriContents  = false;
0 ignored issues
show
Unused Code introduced by
The assignment to $uriContents is dead and can be removed.
Loading history...
99
100
        try {
101
            $uriContents = file_get_contents($proxyListUri);
102
        } catch (Exception $e) {
103
            $message = $e->getMessage();
104
105
            $this->logger->error(
106
                'An error has been occurred while getting contents by URI.',
107
                [
108
                    'message' => $message,
109
                    'uri'     => $proxyListUri,
110
                ]
111
            );
112
        }
113
114
        if (false === $uriContents) {
115
            throw BadProxyListUriException::withProxyListUri($proxyListUri);
116
        }
117
118
        $uriContentsFormat = $this->options['format'];
119
        $decoderContext    = $this->options['decoder_context'];
120
        $proxyList         = $this->contentsDecoder->decode($uriContents, $uriContentsFormat, $decoderContext);
121
122
        // TODO: would be safer to use a converter here which will explicitly convert decoded data to an array.
123
        // Such thing will depend on data provider and their APIs, for now this logic is pretty enough.
124
125
        if ($this->proxyCacher instanceof CacherInterface) {
126
            $this->proxyCacher->save($proxyList);
127
        }
128
129
        return $proxyList;
130
    }
131
132
    /**
133
     * Sets a proxy list cacher
134
     *
135
     * @param CacherInterface $proxyCacher Saves a proxy list in the cache and invalidates it by demand
136
     *
137
     * @return void
138
     */
139
    public function setCacher(CacherInterface $proxyCacher): void
140
    {
141
        $this->proxyCacher = $proxyCacher;
142
    }
143
144
    /**
145
     * Configures URI proxy locator options
146
     *
147
     * @param OptionsResolver $optionsResolver Validates options and merges them with default values
148
     *
149
     * @return void
150
     */
151
    protected function configureOptions(OptionsResolver $optionsResolver): void
152
    {
153
        $optionsResolver->setDefaults(
154
            [
155
                'uri'             => null,
156
                'format'          => null,
157
                'decoder_context' => [],
158
            ]
159
        );
160
161
        $optionsResolver->setAllowedTypes('uri', ['string']);
162
        $optionsResolver->setAllowedTypes('decoder_context', ['array']);
163
        $optionsResolver->setRequired(['uri', 'format']);
164
    }
165
}
166