Completed
Pull Request — master (#182)
by Atymic
20:38
created

ProviderAndDumperAggregator::getProviders()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
crap 1
1
<?php namespace Geocoder\Laravel;
2
3
/**
4
 * This file is part of the Geocoder Laravel package.
5
 * For the full copyright and license information, please view the LICENSE
6
 * file that was distributed with this source code.
7
 *
8
 * @author Mike Bronner <[email protected]>
9
 * @license    MIT License
10
 */
11
12
use Geocoder\Dumper\GeoJson;
13
use Geocoder\Dumper\Gpx;
14
use Geocoder\Dumper\Kml;
15
use Geocoder\Dumper\Wkb;
16
use Geocoder\Dumper\Wkt;
17
use Geocoder\Laravel\Contracts\ResolveFromContainer;
18
use Geocoder\Laravel\Exceptions\InvalidDumperException;
19
use Geocoder\ProviderAggregator;
20
use Geocoder\Query\GeocodeQuery;
21
use Geocoder\Query\ReverseQuery;
22
use Illuminate\Log\Logger;
23
use Illuminate\Support\Collection;
24
use Illuminate\Support\Str;
25
use Psr\Log\LoggerAwareTrait;
26
use ReflectionClass;
27
28
/**
29
 * @SuppressWarnings(PHPMD.TooManyPublicMethods)
30
 */
31
class ProviderAndDumperAggregator
32
{
33
    protected $aggregator;
34 24
    protected $limit;
35
    protected $results;
36 24
    protected $isCaching = true;
37 24
38 24
    public function __construct()
39
    {
40
        $this->aggregator = new ProviderAggregator();
41
        $this->results = collect();
42
    }
43 1
44
    /**
45 1
     * @deprecated Use `get()` instead.
46
     */
47
    public function all() : array
48 17
    {
49
        return $this->results->all();
50 17
    }
51
52
    public function get() : Collection
53 2
    {
54
        return $this->results;
55 2
    }
56 2
57
    public function toJson() : string
58
    {
59
        return $this
60
            ->dump("geojson")
61
            ->first();
62
    }
63 2
64 1
    public function doNotCache() : self
65 1
    {
66 1
        $this->isCaching = false;
67
68 1
        return $this;
69
    }
70
71 1
    public function dump(string $dumper) : Collection
72 1
    {
73 1
        $dumperClasses = collect([
74
            'geojson' => GeoJson::class,
75 1
            'gpx' => Gpx::class,
76 1
            'kml' => Kml::class,
77 1
            'wkb' => Wkb::class,
78
            'wkt' => Wkt::class,
79
        ]);
80 1
81
        if (!$dumperClasses->has($dumper)) {
82 1
            $errorMessage = implode('', [
83 1
                "The dumper specified ('{$dumper}') is invalid. Valid dumpers ",
84 1
                "are: geojson, gpx, kml, wkb, wkt.",
85 1
            ]);
86 1
87 1
            throw new InvalidDumperException($errorMessage);
88 1
        }
89
90
        $dumperClass = $dumperClasses->get($dumper);
91 1
        $dumper = new $dumperClass;
92
        $results = collect($this->results->all());
93 1
94
        return $results->map(function ($result) use ($dumper) {
95
            return $dumper->dump($result);
96 1
        });
97
    }
98 1
99 1 View Code Duplication
    public function geocode(string $value) : self
100 1
    {
101 1
        $cacheKey = (new Str)->slug(strtolower(urlencode($value)));
102 1
        $this->results = $this->cacheRequest($cacheKey, [$value], "geocode");
103 1
104 1
        return $this;
105
    }
106
107 1
    public function geocodeQuery(GeocodeQuery $query) : self
108
    {
109 1
        $cacheKey = serialize($query);
110
        $this->results = $this->cacheRequest($cacheKey, [$query], "geocodeQuery");
111
112 1
        return $this;
113
    }
114 1
115
    public function getLimit() : int
116
    {
117 16
        return $this->limit;
118
    }
119 16
120 16
    public function getName() : string
121 16
    {
122 16
        return $this->aggregator->getName();
123 16
    }
124 16
125 16
    public function limit(int $limit) : self
126
    {
127
        $this->aggregator = new ProviderAggregator(null, $limit);
128 16
        $this->registerProvidersFromConfig(collect(config('geocoder.providers')));
129
        $this->limit = $limit;
130 16
131
        return $this;
132
    }
133 1
134
    public function getProvider()
135 1
    {
136 1
        $reflectedClass = new ReflectionClass(ProviderAggregator::class);
137 1
        $reflectedProperty = $reflectedClass->getProperty('provider');
138 1
        $reflectedProperty->setAccessible(true);
139 1
140 1
        return $reflectedProperty->getValue($this->aggregator)
141 1
            ?? $this->getProviders()->first();
142
    }
143
144 1
    public function getProviders() : Collection
145
    {
146 1
        return collect($this->aggregator->getProviders());
147
    }
148
149 1
    public function registerProvider($provider) : self
150
    {
151 1
        $this->aggregator->registerProvider($provider);
152 1
153 1
        return $this;
154
    }
155 1
156
    public function registerProviders(array $providers = []) : self
157
    {
158 1
        $this->aggregator->registerProviders($providers);
159
160 1
        return $this;
161
    }
162
163 1
    public function registerProvidersFromConfig(Collection $providers) : self
164
    {
165 1
        $this->registerProviders($this->getProvidersFromConfiguration($providers));
166
167 1
        return $this;
168
    }
169
170 24 View Code Duplication
    public function reverse(float $latitude, float $longitude) : self
171
    {
172 24
        $cacheKey = (new Str)->slug(strtolower(urlencode("{$latitude}-{$longitude}")));
173
        $this->results = $this->cacheRequest($cacheKey, [$latitude, $longitude], "reverse");
174 24
175
        return $this;
176
    }
177 5
178
    public function reverseQuery(ReverseQuery $query) : self
179 5
    {
180
        $cacheKey = serialize($query);
181 5
        $this->results = $this->cacheRequest($cacheKey, [$query], "reverseQuery");
182
183
        return $this;
184 2
    }
185
186 2
    public function using(string $name) : self
187
    {
188
        $this->aggregator = $this->aggregator->using($name);
189
190
        return $this;
191
    }
192 1
193
    protected function cacheRequest(string $cacheKey, array $queryElements, string $queryType)
194 1
    {
195
        if (! $this->isCaching) {
196
            $this->isCaching = true;
197 24
198
            return collect($this->aggregator->{$queryType}(...$queryElements));
199 24
        }
200
201 24
        $hashedCacheKey = sha1($this->getProvider()->getName() . "-" . $cacheKey);
202
        $duration = config("geocoder.cache.duration", 0);
203
        $store = config('geocoder.cache.store');
204
205
        $result = app("cache")
206 24
            ->store($store)
207 24
            ->remember($hashedCacheKey, $duration, function () use ($cacheKey, $queryElements, $queryType) {
208 24
                return [
209
                    "key" => $cacheKey,
210 24
                    "value" => collect($this->aggregator->{$queryType}(...$queryElements)),
211 24
                ];
212
            });
213
214 24
        $result = $this->preventCacheKeyHashCollision(
215 24
            $result,
216
            $hashedCacheKey,
217 24
            $cacheKey,
218
            $queryElements,
219
            $queryType
220 24
        );
221
222 24
        $this->removeEmptyCacheEntry($result, $hashedCacheKey);
223 24
224 24
        return $result;
225
    }
226
227
    protected function getAdapterClass(string $provider) : string
228 24
    {
229
        $specificAdapters = collect([
230 24
            'Geocoder\Provider\GeoIP2\GeoIP2' => 'Geocoder\Provider\GeoIP2\GeoIP2Adapter',
231 24
            'Geocoder\Provider\MaxMindBinary\MaxMindBinary' => '',
232
        ]);
233
234 24
        if ($specificAdapters->has($provider)) {
235
            return $specificAdapters->get($provider);
236
        }
237 24
238
        return config('geocoder.adapter');
239 24
    }
240 24
241
    protected function getReader()
242
    {
243
        $reader = config('geocoder.reader');
244 24
245
        if (is_array(config('geocoder.reader'))) {
246
            $readerClass = array_key_first(config('geocoder.reader'));
247
            $readerArguments = config('geocoder.reader')[$readerClass];
248 24
            $reflection = new ReflectionClass($readerClass);
249
            $reader = $reflection->newInstanceArgs($readerArguments);
250
        }
251 19
252
        return $reader;
253 19
    }
254
255 19
    protected function getArguments(array $arguments, string $provider) : array
256 1
    {
257
        if ($provider === 'Geocoder\Provider\Chain\Chain') {
258 19
            return $this->getProvidersFromConfiguration(
259
                collect(config('geocoder.providers.Geocoder\Provider\Chain\Chain'))
260
            );
261
        }
262
263
        $adapter = $this->getAdapterClass($provider);
264
265
        if ($adapter) {
266
            if ($this->requiresReader($provider)) {
267
                $adapter = new $adapter($this->getReader());
268
            } else {
269
                $adapter = new $adapter;
270
            }
271
272
            array_unshift($arguments, $adapter);
273
        }
274
275
        return $arguments;
276
    }
277
278
    protected function getProvidersFromConfiguration(Collection $providers) : array
279
    {
280
        $providers = $providers->map(function ($arguments, $provider) {
281
            if (is_string($arguments) && $arguments === ResolveFromContainer::class) {
282
                return app($provider);
283
            }
284
285
            $arguments = $this->getArguments($arguments, $provider);
286
            $reflection = new ReflectionClass($provider);
287
288
            if ($provider === "Geocoder\Provider\Chain\Chain") {
289
                $chainProvider = $reflection->newInstance($arguments);
290
291
                if (class_exists(Logger::class)
292
                    && in_array(LoggerAwareTrait::class, class_uses($chainProvider))
293
                    && app(Logger::class) !== null
294
                ) {
295
                    $chainProvider->setLogger(app(Logger::class));
296
                }
297
298
                return $chainProvider;
299
            }
300
301
            return $reflection->newInstanceArgs($arguments);
302
        });
303
304
        return $providers->toArray();
305
    }
306
307
    protected function preventCacheKeyHashCollision(
308
        array $result,
309
        string $hashedCacheKey,
310
        string $cacheKey,
311
        array $queryElements,
312
        string $queryType
313
    ) {
314
        if ($result["key"] === $cacheKey) {
315
            return $result["value"];
316
        }
317
318
        app("cache")
319
            ->store(config('geocoder.cache.store'))
320
            ->forget($hashedCacheKey);
321
322
        return $this->cacheRequest($cacheKey, $queryElements, $queryType);
323
    }
324
325
    protected function removeEmptyCacheEntry(Collection $result, string $cacheKey)
326
    {
327
        if ($result && $result->isEmpty()) {
328
            app('cache')->forget($cacheKey);
329
        }
330
    }
331
332
    protected function requiresReader(string $class) : bool
333
    {
334
        $specificAdapters = collect([
335
            'Geocoder\Provider\GeoIP2\GeoIP2',
336
        ]);
337
338
        return $specificAdapters->contains($class);
339
    }
340
}
341