Completed
Push — symfony-4 ( cac9bc...073781 )
by Tobias
86:28 queued 71:27
created

Batch::geocode()   C

Complexity

Conditions 11
Paths 4

Size

Total Lines 55
Code Lines 37

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 32
CRAP Score 11

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 55
ccs 32
cts 32
cp 1
rs 6.6153
cc 11
eloc 37
nc 4
nop 1
crap 11

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/*
4
 * This file is part of the Geotools library.
5
 *
6
 * (c) Antoine Corcy <[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
12
namespace League\Geotools\Batch;
13
14
use Geocoder\Geocoder;
15
use Geocoder\ProviderAggregator;
16
use League\Geotools\Coordinate\CoordinateInterface;
17
use League\Geotools\Exception\InvalidArgumentException;
18
use Psr\Cache\CacheItemPoolInterface;
19
use React\EventLoop\Factory as EventLoopFactory;
20
use React\Promise\Deferred;
21
22
/**
23
 * Batch class
24
 *
25
 * @author Antoine Corcy <[email protected]>
26
 */
27
class Batch implements BatchInterface
28
{
29
    /**
30
     * The Geocoder instance to use.
31
     *
32
     * @var ProviderAggregator
33
     */
34
    protected $geocoder;
35
36
    /**
37
     * An array of closures.
38
     *
39
     * @var array
40
     */
41
    protected $tasks;
42
43
    /**
44
     * The cache instance to use.
45
     *
46
     * @var CacheItemPoolInterface
47
     */
48
    protected $cache;
49
50
    /**
51
     * Set the Geocoder instance to use.
52
     *
53
     * @param ProviderAggregator $geocoder The Geocoder instance to use.
54
     */
55 61
    public function __construct(ProviderAggregator $geocoder)
56
    {
57 61
        $this->geocoder = $geocoder;
58 61
    }
59
60
    /**
61
     * Check against the cache instance if any.
62
     *
63
     * @param string $providerName The name of the provider.
64
     * @param string $query        The query string.
65
     *
66
     * @return boolean|BatchGeocoded The BatchGeocoded object from the query or the cache instance.
67
     */
68 34
    public function isCached($providerName, $query)
69
    {
70 34
        if (null === $this->cache) {
71
            return false;
72
        }
73
74
        $item = $this->cache->getItem($this->getCacheKey($providerName, $query));
75
76
        if ($item->isHit()) {
77
            return $item->get();
78
        }
79
80 9
        return false;
81
    }
82 9
83 1
84
    /**
85
     * Cache the BatchGeocoded object.
86 9
     *
87
     * @param BatchGeocoded $geocoded The BatchGeocoded object to cache.
88
     *
89
     * @return BatchGeocoded The BatchGeocoded object.
90
     */
91
    public function cache(BatchGeocoded $geocoded)
92 28
    {
93
        if (isset($this->cache)) {
94 28
            $key = $this->getCacheKey($geocoded->getProviderName(), $geocoded->getQuery());
95 28
            $item = $this->cache->getItem($key);
96
            $item->set($geocoded);
97 28
            $this->cache->save($item);
98 28
        }
99 9
100 9
        return $geocoded;
101 8
    }
102
103
    /**
104 8
     * {@inheritDoc}
105 4
     */
106
    public function geocode($values)
107 4
    {
108 4
        $geocoder = $this->geocoder;
109 6
        $cache    = $this;
110
111 2
        foreach ($geocoder->getProviders() as $provider) {
112 2
            if (is_array($values) && count($values) > 0) {
113 2
                foreach ($values as $value) {
114
                    $this->tasks[] = function () use ($geocoder, $provider, $value, $cache) {
115
                        $deferred = new Deferred;
116 8
117 9
                        try {
118
                            if ($cached = $cache->isCached($provider->getName(), $value)) {
119 19
                                $deferred->resolve($cached);
120 12
                            } else {
121 8
                                $batchResult = new BatchResult($provider->getName(), $value);
122
                                $address = $geocoder->using($provider->getName())->geocode($value)->first();
123
                                $deferred->resolve($cache->cache($batchResult->createFromAddress($address)));
124 8
                            }
125 4
                        } catch (\Exception $e) {
126
                            $batchGeocoded = new BatchResult($provider->getName(), $value, $e->getMessage());
127 4
                            $deferred->reject($batchGeocoded->newInstance());
128 4
                        }
129 6
130
                        return $deferred->promise();
131 2
                    };
132 2
                }
133 2
            } elseif (is_string($values) && '' !== trim($values)) {
134
                $this->tasks[] = function () use ($geocoder, $provider, $values, $cache) {
135
                    $deferred = new Deferred;
136 8
137 12
                    try {
138
                        if ($cached = $cache->isCached($provider->getName(), $values)) {
139 7
                            $deferred->resolve($cached);
140 28
                        } else {
141
                            $batchResult = new BatchResult($provider->getName(), $values);
142
                            $address = $geocoder->using($provider->getName())->geocode($values)->first();
143
                            $deferred->resolve($cache->cache($batchResult->createFromAddress($address)));
144
                        }
145 21
                    } catch (\Exception $e) {
146
                        $batchGeocoded = new BatchResult($provider->getName(), $values, $e->getMessage());
147
                        $deferred->reject($batchGeocoded->newInstance());
148
                    }
149
150
                    return $deferred->promise();
151 26
                };
152
            } else {
153 26
                throw new InvalidArgumentException(
154 26
                    'The argument should be a string or an array of strings to geocode.'
155
                );
156 26
            }
157 26
        }
158 9
159 9
        return $this;
160 8
    }
161
162 8
    /**
163
     * {@inheritDoc}
164 8
     */
165 4
    public function reverse($coordinates)
166
    {
167 4
        $geocoder = $this->geocoder;
168 4
        $cache    = $this;
169 4
170 4
        foreach ($geocoder->getProviders() as $provider) {
171 2
            if (is_array($coordinates) && count($coordinates) > 0) {
172
                foreach ($coordinates as $coordinate) {
173 6
                    $this->tasks[] = function () use ($geocoder, $provider, $coordinate, $cache) {
174
                        $deferred = new Deferred();
175 2
176 2
                        $valueCoordinates = sprintf('%s, %s', $coordinate->getLatitude(), $coordinate->getLongitude());
177 2
                        try {
178
                            if ($cached = $cache->isCached($provider->getName(), $valueCoordinates)) {
179
                                $deferred->resolve($cached);
180 8
                            } else {
181 9
                                $batchResult = new BatchResult($provider->getName(), $valueCoordinates);
182
                                $address = $geocoder->using($provider->getName())->reverse(
183 17
                                        $coordinate->getLatitude(),
184 10
                                        $coordinate->getLongitude()
185 8
                                    )->first();
186
187 8
                                $deferred->resolve($cache->cache($batchResult->createFromAddress($address)));
188
                            }
189 8
                        } catch (\Exception $e) {
190 4
                            $batchGeocoded = new BatchResult($provider->getName(), $valueCoordinates, $e->getMessage());
191
                            $deferred->reject($batchGeocoded->newInstance());
192 4
                        }
193 4
194 4
                        return $deferred->promise();
195 4
                    };
196 2
                }
197 6
            } elseif ($coordinates instanceOf CoordinateInterface) {
198
                $this->tasks[] = function () use ($geocoder, $provider, $coordinates, $cache) {
199 2
                    $deferred = new Deferred();
200 2
201 2
                    $valueCoordinates = sprintf('%s, %s', $coordinates->getLatitude(), $coordinates->getLongitude());
202
                    try {
203
                        if ($cached = $cache->isCached($provider->getName(), $valueCoordinates)) {
204 8
                            $deferred->resolve($cached);
205 10
                        } else {
206
                            $batchResult = new BatchResult($provider->getName(), $valueCoordinates);
207 7
                            $address = $geocoder->using($provider->getName())->reverse(
208 26
                                    $coordinates->getLatitude(),
209
                                    $coordinates->getLongitude()
210
                                )->first();
211
                            $deferred->resolve($cache->cache($batchResult->createFromAddress($address)));
212
                        }
213 19
                    } catch (\Exception $e) {
214
                        $batchGeocoded = new BatchResult($provider->getName(), $valueCoordinates, $e->getMessage());
215
                        $deferred->reject($batchGeocoded->newInstance());
216
                    }
217
218
                    return $deferred->promise();
219
                };
220
            } else {
221
                throw new InvalidArgumentException(
222 17
                    'The argument should be a Coordinate instance or an array of Coordinate instances to reverse.'
223
                );
224 17
            }
225
        }
226 17
227 13
        return $this;
228 13
    }
229
230 4
    /**
231 17
     * {@inheritDoc}
232
     *
233
     * $this cannot be used in anonymous function in PHP 5.3.x
234 16
     * @see http://php.net/manual/en/functions.anonymous.php
235
     */
236
    public function serie()
237
    {
238
        $computedInSerie = array();
239
240
        foreach ($this->tasks as $task) {
241
            $task()->then(function($result) use (&$computedInSerie) {
242
                $computedInSerie[] = $result;
243 17
            }, function ($emptyResult) use (&$computedInSerie) {
244
                $computedInSerie[] = $emptyResult;
245 17
            });
246 17
        }
247
248 17
        return $computedInSerie;
249
    }
250 12
251 12
    /**
252
     * {@inheritDoc}
253 4
     *
254 16
     * $this cannot be used in anonymous function in PHP 5.3.x
255 17
     * @see http://php.net/manual/en/functions.anonymous.php
256
     */
257
    public function parallel()
258 17
    {
259
        $loop = EventLoopFactory::create();
260 16
        $computedInParallel = array();
261
262
        foreach ($this->tasks as $task) {
263
            $loop->nextTick(function () use ($task, &$computedInParallel) {
264
                $task()->then(function($result) use (&$computedInParallel) {
265
                    $computedInParallel[] = $result;
266 19
                }, function ($emptyResult) use (&$computedInParallel) {
267
                    $computedInParallel[] = $emptyResult;
268 19
                });
269
            });
270 19
        }
271
272
        $loop->run();
273
274
        return $computedInParallel;
275
    }
276
277
    /**
278
     * {@inheritDoc}
279
     */
280
    public function setCache(CacheItemPoolInterface $cache)
281
    {
282
        $this->cache = $cache;
283
284
        return $this;
285
    }
286
287
    private function getCacheKey(string $providerName, string $query): string
288
    {
289
        return sha1($providerName.'-'.$query);
290
    }
291
}
292