Completed
Push — master ( 848733...7881aa )
by Mike
22:08
created

preventCacheKeyHashCollision()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 17
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

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