Completed
Push — master ( 0c7e3d...41f22b )
by Mike
21:24 queued 12s
created

ProviderAndDumperAggregator::doNotCache()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

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