Completed
Push — master ( 723696...0885cd )
by Antoine
02:44
created

Batch   A

Complexity

Total Complexity 31

Size/Duplication

Total Lines 246
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 6

Test Coverage

Coverage 99.02%

Importance

Changes 0
Metric Value
wmc 31
lcom 1
cbo 6
dl 0
loc 246
ccs 101
cts 102
cp 0.9902
rs 9.8
c 0
b 0
f 0

8 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A isCached() 0 4 2
A setCache() 0 6 1
A cache() 0 8 2
C geocode() 0 55 11
C reverse() 0 64 10
A serie() 0 14 2
A parallel() 0 19 2
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 League\Geotools\Cache\CacheInterface;
16
use League\Geotools\Coordinate\CoordinateInterface;
17
use League\Geotools\Exception\InvalidArgumentException;
18
use React\EventLoop\Factory as EventLoopFactory;
19
use React\Promise\Deferred;
20
21
/**
22
 * Batch class
23
 *
24
 * @author Antoine Corcy <[email protected]>
25
 */
26
class Batch implements BatchInterface
27
{
28
    /**
29
     * The Geocoder instance to use.
30
     *
31
     * @var Geocoder
32
     */
33
    protected $geocoder;
34
35
    /**
36
     * An array of closures.
37
     *
38
     * @var array
39
     */
40
    protected $tasks;
41
42
    /**
43
     * The cache instance to use.
44
     *
45
     * @var CacheInterface
46
     */
47
    protected $cache;
48
49
    /**
50
     * Set the Geocoder instance to use.
51
     *
52
     * @param Geocoder $geocoder The Geocoder instance to use.
53
     */
54 61
    public function __construct(Geocoder $geocoder)
55
    {
56 61
        $this->geocoder = $geocoder;
57 61
    }
58
59
    /**
60
     * Check against the cache instance if any.
61
     *
62
     * @param string $providerName The name of the provider.
63
     * @param string $query        The query string.
64
     *
65
     * @return boolean|BatchGeocoded The BatchGeocoded object from the query or the cache instance.
66
     */
67 34
    public function isCached($providerName, $query)
68
    {
69 34
        return isset($this->cache) ? $this->cache->isCached($providerName, $query) : false;
70
    }
71
72
    /**
73
     * Cache the BatchGeocoded object.
74
     *
75
     * @param BatchGeocoded $geocoded The BatchGeocoded object to cache.
76
     *
77
     * @return BatchGeocoded The BatchGeocoded object.
78
     */
79 9
    public function cache(BatchGeocoded $geocoded)
80
    {
81 9
        if (isset($this->cache)) {
82 1
            $this->cache->cache($geocoded);
83
        }
84
85 9
        return $geocoded;
86
    }
87
88
    /**
89
     * {@inheritDoc}
90
     */
91 28
    public function geocode($values)
92
    {
93 28
        $geocoder = $this->geocoder;
94 28
        $cache    = $this;
95
96 28
        foreach ($geocoder->getProviders() as $provider) {
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Geocoder\Geocoder as the method getProviders() does only exist in the following implementations of said interface: Geocoder\ProviderAggregator.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
97 28
            if (is_array($values) && count($values) > 0) {
98 9
                foreach ($values as $value) {
99
                    $this->tasks[] = function () use ($geocoder, $provider, $value, $cache) {
100 8
                        $deferred = new Deferred;
101
102
                        try {
103 8
                            if ($cached = $cache->isCached($provider->getName(), $value)) {
104 4
                                $deferred->resolve($cached);
105
                            } else {
106 4
                                $batchResult = new BatchResult($provider->getName(), $value);
107 4
                                $address = $geocoder->using($provider->getName())->geocode($value)->first();
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Geocoder\Geocoder as the method using() does only exist in the following implementations of said interface: Geocoder\ProviderAggregator.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
108 6
                                $deferred->resolve($cache->cache($batchResult->createFromAddress($address)));
109
                            }
110 2
                        } catch (\Exception $e) {
111 2
                            $batchGeocoded = new BatchResult($provider->getName(), $value, $e->getMessage());
112 2
                            $deferred->reject($batchGeocoded->newInstance());
113
                        }
114
115 8
                        return $deferred->promise();
116 9
                    };
117
                }
118 19
            } elseif (is_string($values) && '' !== trim($values)) {
119
                $this->tasks[] = function () use ($geocoder, $provider, $values, $cache) {
120 8
                    $deferred = new Deferred;
121
122
                    try {
123 8
                        if ($cached = $cache->isCached($provider->getName(), $values)) {
124 4
                            $deferred->resolve($cached);
125
                        } else {
126 4
                            $batchResult = new BatchResult($provider->getName(), $values);
127 4
                            $address = $geocoder->using($provider->getName())->geocode($values)->first();
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Geocoder\Geocoder as the method using() does only exist in the following implementations of said interface: Geocoder\ProviderAggregator.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
128 6
                            $deferred->resolve($cache->cache($batchResult->createFromAddress($address)));
129
                        }
130 2
                    } catch (\Exception $e) {
131 2
                        $batchGeocoded = new BatchResult($provider->getName(), $values, $e->getMessage());
132 2
                        $deferred->reject($batchGeocoded->newInstance());
133
                    }
134
135 8
                    return $deferred->promise();
136 12
                };
137
            } else {
138 7
                throw new InvalidArgumentException(
139 28
                    'The argument should be a string or an array of strings to geocode.'
140
                );
141
            }
142
        }
143
144 21
        return $this;
145
    }
146
147
    /**
148
     * {@inheritDoc}
149
     */
150 26
    public function reverse($coordinates)
151
    {
152 26
        $geocoder = $this->geocoder;
153 26
        $cache    = $this;
154
155 26
        foreach ($geocoder->getProviders() as $provider) {
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Geocoder\Geocoder as the method getProviders() does only exist in the following implementations of said interface: Geocoder\ProviderAggregator.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
156 26
            if (is_array($coordinates) && count($coordinates) > 0) {
157 9
                foreach ($coordinates as $coordinate) {
158
                    $this->tasks[] = function () use ($geocoder, $provider, $coordinate, $cache) {
159 8
                        $deferred = new Deferred();
160
161 8
                        $valueCoordinates = sprintf('%s, %s', $coordinate->getLatitude(), $coordinate->getLongitude());
162
                        try {
163 8
                            if ($cached = $cache->isCached($provider->getName(), $valueCoordinates)) {
164 4
                                $deferred->resolve($cached);
165
                            } else {
166 4
                                $batchResult = new BatchResult($provider->getName(), $valueCoordinates);
167 4
                                $address = $geocoder->using($provider->getName())->reverse(
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Geocoder\Geocoder as the method using() does only exist in the following implementations of said interface: Geocoder\ProviderAggregator.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
168 4
                                        $coordinate->getLatitude(),
169 4
                                        $coordinate->getLongitude()
170 2
                                    )->first();
171
172 6
                                $deferred->resolve($cache->cache($batchResult->createFromAddress($address)));
173
                            }
174 2
                        } catch (\Exception $e) {
175 2
                            $batchGeocoded = new BatchResult($provider->getName(), $valueCoordinates, $e->getMessage());
176 2
                            $deferred->reject($batchGeocoded->newInstance());
177
                        }
178
179 8
                        return $deferred->promise();
180 9
                    };
181
                }
182
            } elseif ($coordinates instanceOf CoordinateInterface) {
183
                $this->tasks[] = function () use ($geocoder, $provider, $coordinates, $cache) {
184 8
                    $deferred = new Deferred();
185
186 8
                    $valueCoordinates = sprintf('%s, %s', $coordinates->getLatitude(), $coordinates->getLongitude());
187
                    try {
188 8
                        if ($cached = $cache->isCached($provider->getName(), $valueCoordinates)) {
189 4
                            $deferred->resolve($cached);
190
                        } else {
191 4
                            $batchResult = new BatchResult($provider->getName(), $valueCoordinates);
192 4
                            $address = $geocoder->using($provider->getName())->reverse(
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Geocoder\Geocoder as the method using() does only exist in the following implementations of said interface: Geocoder\ProviderAggregator.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
193 4
                                    $coordinates->getLatitude(),
194 4
                                    $coordinates->getLongitude()
195 2
                                )->first();
196 6
                            $deferred->resolve($cache->cache($batchResult->createFromAddress($address)));
197
                        }
198 2
                    } catch (\Exception $e) {
199 2
                        $batchGeocoded = new BatchResult($provider->getName(), $valueCoordinates, $e->getMessage());
200 2
                        $deferred->reject($batchGeocoded->newInstance());
201
                    }
202
203 8
                    return $deferred->promise();
204 10
                };
205
            } else {
206 7
                throw new InvalidArgumentException(
207 26
                    'The argument should be a Coordinate instance or an array of Coordinate instances to reverse.'
208
                );
209
            }
210
        }
211
212 19
        return $this;
213
    }
214
215
    /**
216
     * {@inheritDoc}
217
     *
218
     * $this cannot be used in anonymous function in PHP 5.3.x
219
     * @see http://php.net/manual/en/functions.anonymous.php
220
     */
221 17
    public function serie()
222
    {
223 17
        $computedInSerie = array();
224
225 17
        foreach ($this->tasks as $task) {
226
            $task()->then(function($result) use (&$computedInSerie) {
227 13
                $computedInSerie[] = $result;
228
            }, function ($emptyResult) use (&$computedInSerie) {
229 4
                $computedInSerie[] = $emptyResult;
230 17
            });
231
        }
232
233 16
        return $computedInSerie;
234
    }
235
236
    /**
237
     * {@inheritDoc}
238
     *
239
     * $this cannot be used in anonymous function in PHP 5.3.x
240
     * @see http://php.net/manual/en/functions.anonymous.php
241
     */
242 17
    public function parallel()
243
    {
244 17
        $loop = EventLoopFactory::create();
245 17
        $computedInParallel = array();
246
247 17
        foreach ($this->tasks as $task) {
248
            $loop->nextTick(function () use ($task, &$computedInParallel) {
249
                $task()->then(function($result) use (&$computedInParallel) {
250 12
                    $computedInParallel[] = $result;
251 16
                }, function ($emptyResult) use (&$computedInParallel) {
252 4
                    $computedInParallel[] = $emptyResult;
253 16
                });
254 17
            });
255
        }
256
257 17
        $loop->run();
258
259 16
        return $computedInParallel;
260
    }
261
262
    /**
263
     * {@inheritDoc}
264
     */
265 19
    public function setCache(CacheInterface $cache)
266
    {
267 19
        $this->cache = $cache;
268
269 19
        return $this;
270
    }
271
}
272