Code

< 40 %
40-60 %
> 60 %
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the Geocoder package.
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 *
10
 * @license    MIT License
11
 */
12
13
namespace Geocoder\Plugin;
14
15
use Geocoder\Collection;
16
use Geocoder\Exception\Exception;
17
use Geocoder\Exception\LogicException;
18
use Geocoder\Plugin\Exception\LoopException;
19
use Geocoder\Plugin\Promise\GeocoderFulfilledPromise;
20
use Geocoder\Plugin\Promise\GeocoderRejectedPromise;
21
use Geocoder\Provider\Provider;
22
use Geocoder\Query\GeocodeQuery;
23
use Geocoder\Query\Query;
24
use Geocoder\Query\ReverseQuery;
25
26
/**
27
 * @author Joel Wurtz <[email protected]>
28
 * @author Tobias Nyholm <[email protected]>
29
 */
30
class PluginProvider implements Provider
31
{
32
    /**
33
     * @var Provider
34
     */
35
    private $provider;
36
37
    /**
38
     * @var Plugin[]
39
     */
40
    private $plugins;
41
42
    /**
43
     * A list of options.
44
     *
45
     * @var array
46
     */
47
    private $options;
48
49
    /**
50
     * @param Plugin[]                          $plugins
51
     * @param array{max_restarts?: int<0, max>} $options
52
     */
53 6
    public function __construct(Provider $provider, array $plugins = [], array $options = [])
54
    {
55 6
        $this->provider = $provider;
56 6
        $this->plugins = $plugins;
57 6
        $this->options = $this->configure($options);
58
    }
59
60 5
    public function geocodeQuery(GeocodeQuery $query): Collection
61
    {
62 5
        $pluginChain = $this->createPluginChain($this->plugins, function (GeocodeQuery $query) {
63
            try {
64 4
                return new GeocoderFulfilledPromise($this->provider->geocodeQuery($query));
65 1
            } catch (Exception $exception) {
66 1
                return new GeocoderRejectedPromise($exception);
67
            }
68 5
        });
69
70 5
        return $pluginChain($query)->wait();
71
    }
72
73 2
    public function reverseQuery(ReverseQuery $query): Collection
74
    {
75 2
        $pluginChain = $this->createPluginChain($this->plugins, function (ReverseQuery $query) {
76
            try {
77 2
                return new GeocoderFulfilledPromise($this->provider->reverseQuery($query));
78
            } catch (Exception $exception) {
79
                return new GeocoderRejectedPromise($exception);
80
            }
81 2
        });
82
83 2
        return $pluginChain($query)->wait();
84
    }
85
86
    public function getName(): string
87
    {
88
        return $this->provider->getName();
89
    }
90
91
    /**
92
     * Configure the plugin provider.
93
     */
94 6
    private function configure(array $options = []): array
95
    {
96 6
        $defaults = [
97 6
            'max_restarts' => 10,
98 6
        ];
99
100 6
        $config = array_merge($defaults, $options);
101
102
        // Make sure no invalid values are provided
103 6
        if (count($config) !== count($defaults)) {
104
            throw new LogicException(sprintf('Valid options to the PluginProviders are: %s', implode(', ', array_values($defaults))));
105
        }
106
107 6
        return $config;
108
    }
109
110
    /**
111
     * Create the plugin chain.
112
     *
113
     * @param Plugin[] $pluginList     A list of plugins
114
     * @param callable $clientCallable Callable making the HTTP call
115
     *
116
     * @return callable
117
     */
118 6
    private function createPluginChain(array $pluginList, callable $clientCallable)
119
    {
120 6
        $firstCallable = $lastCallable = $clientCallable;
121
122 6
        while ($plugin = array_pop($pluginList)) {
123 5
            $lastCallable = function (Query $query) use ($plugin, $lastCallable, &$firstCallable) {
124 5
                return $plugin->handleQuery($query, $lastCallable, $firstCallable);
125 5
            };
126
127 5
            $firstCallable = $lastCallable;
128
        }
129
130 6
        $firstCalls = 0;
131 6
        $firstCallable = function (Query $query) use ($lastCallable, &$firstCalls) {
132 6
            if ($firstCalls > $this->options['max_restarts']) {
133 1
                throw LoopException::create('Too many restarts in plugin provider', $query);
134
            }
135
136 6
            ++$firstCalls;
137
138 6
            return $lastCallable($query);
139 6
        };
140
141 6
        return $firstCallable;
142
    }
143
}
144