Complex classes like BrowscapUpdater often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use BrowscapUpdater, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
22 | final class BrowscapUpdater |
||
23 | { |
||
24 | /** |
||
25 | * The cache instance |
||
26 | * |
||
27 | * @var \BrowscapPHP\Cache\BrowscapCacheInterface|null |
||
28 | */ |
||
29 | private $cache; |
||
30 | |||
31 | /** |
||
32 | * @var @var \Psr\Log\LoggerInterface|null |
||
33 | */ |
||
34 | private $logger; |
||
35 | |||
36 | /** |
||
37 | * @var \GuzzleHttp\ClientInterface|null |
||
38 | */ |
||
39 | private $client; |
||
40 | |||
41 | /** |
||
42 | * Curl connect timeout in seconds |
||
43 | * |
||
44 | * @var int |
||
45 | */ |
||
46 | private $connectTimeout = 5; |
||
47 | |||
48 | /** |
||
49 | * Gets a cache instance |
||
50 | * |
||
51 | * @return \BrowscapPHP\Cache\BrowscapCacheInterface |
||
52 | */ |
||
53 | 14 | public function getCache() : BrowscapCacheInterface |
|
67 | |||
68 | /** |
||
69 | * Sets a cache instance |
||
70 | * |
||
71 | * @param \BrowscapPHP\Cache\BrowscapCacheInterface|\WurflCache\Adapter\AdapterInterface $cache |
||
72 | * @throws \BrowscapPHP\Exception |
||
73 | * @return self |
||
74 | */ |
||
75 | 14 | public function setCache($cache) : self |
|
76 | { |
||
77 | 14 | if ($cache instanceof BrowscapCacheInterface) { |
|
78 | 10 | $this->cache = $cache; |
|
79 | 4 | } elseif ($cache instanceof AdapterInterface) { |
|
80 | 3 | $this->cache = new BrowscapCache($cache); |
|
81 | } else { |
||
82 | 1 | throw new Exception( |
|
83 | 'the cache has to be an instance of \BrowscapPHP\Cache\BrowscapCacheInterface or ' |
||
84 | 1 | . 'an instanceof of \WurflCache\Adapter\AdapterInterface', |
|
85 | 1 | Exception::CACHE_INCOMPATIBLE |
|
86 | ); |
||
87 | } |
||
88 | |||
89 | 13 | return $this; |
|
90 | } |
||
91 | |||
92 | /** |
||
93 | * Sets a logger instance |
||
94 | * |
||
95 | * @param \Psr\Log\LoggerInterface $logger |
||
96 | * |
||
97 | * @return self |
||
98 | */ |
||
99 | 10 | public function setLogger(LoggerInterface $logger) : self |
|
105 | |||
106 | /** |
||
107 | * returns a logger instance |
||
108 | * |
||
109 | * @return \Psr\Log\LoggerInterface |
||
110 | */ |
||
111 | 11 | public function getLogger() : LoggerInterface |
|
119 | |||
120 | /** |
||
121 | * Sets the Connection Timeout |
||
122 | * |
||
123 | * @param int $connectTimeout |
||
124 | */ |
||
125 | 1 | public function setConnectTimeout(int $connectTimeout) : void |
|
129 | |||
130 | 10 | public function getClient() : ClientInterface |
|
138 | |||
139 | 10 | public function setClient(ClientInterface $client) |
|
143 | |||
144 | /** |
||
145 | * reads and parses an ini file and writes the results into the cache |
||
146 | * |
||
147 | * @param string $iniFile |
||
148 | * @throws \BrowscapPHP\Exception |
||
149 | */ |
||
150 | 3 | public function convertFile(string $iniFile) : void |
|
168 | |||
169 | /** |
||
170 | * reads and parses an ini string and writes the results into the cache |
||
171 | * |
||
172 | * @param string $iniString |
||
173 | */ |
||
174 | 2 | public function convertString(string $iniString) : void |
|
181 | |||
182 | /** |
||
183 | * fetches a remote file and stores it into a local folder |
||
184 | * |
||
185 | * @param string $file The name of the file where to store the remote content |
||
186 | * @param string $remoteFile The code for the remote file to load |
||
187 | * |
||
188 | * @throws \BrowscapPHP\Exception\FetcherException |
||
189 | * @throws \BrowscapPHP\Helper\Exception |
||
190 | * @throws \GuzzleHttp\Exception\GuzzleException |
||
191 | */ |
||
192 | 3 | public function fetch(string $file, string $remoteFile = IniLoader::PHP_INI) : void |
|
239 | |||
240 | /** |
||
241 | * fetches a remote file, parses it and writes the result into the cache |
||
242 | * |
||
243 | * if the local stored information are in the same version as the remote data no actions are |
||
244 | * taken |
||
245 | * |
||
246 | * @param string $remoteFile The code for the remote file to load |
||
247 | * |
||
248 | * @throws \BrowscapPHP\Exception\FileNotFoundException |
||
249 | * @throws \BrowscapPHP\Helper\Exception |
||
250 | * @throws \BrowscapPHP\Exception\FetcherException |
||
251 | * @throws \GuzzleHttp\Exception\GuzzleException |
||
252 | */ |
||
253 | 2 | public function update(string $remoteFile = IniLoader::PHP_INI) : void |
|
292 | |||
293 | /** |
||
294 | * checks if an update on a remote location for the local file or the cache |
||
295 | * |
||
296 | * @throws \BrowscapPHP\Helper\Exception |
||
297 | * @throws \BrowscapPHP\Exception\FetcherException |
||
298 | * @return int|null The actual cached version if a newer version is available, null otherwise |
||
299 | * @throws \GuzzleHttp\Exception\GuzzleException |
||
300 | */ |
||
301 | 9 | public function checkUpdate() : ?int |
|
302 | { |
||
303 | 9 | $success = null; |
|
304 | 9 | $cachedVersion = $this->getCache()->getItem('browscap.version', false, $success); |
|
305 | |||
306 | 9 | if (! $cachedVersion) { |
|
307 | // could not load version from cache |
||
308 | 5 | $this->getLogger()->info('there is no cached version available, please update from remote'); |
|
309 | |||
310 | 5 | return 0; |
|
311 | } |
||
312 | |||
313 | 4 | $uri = (new IniLoader())->getRemoteVersionUrl(); |
|
314 | |||
315 | /** @var \Psr\Http\Message\ResponseInterface $response */ |
||
316 | 4 | $response = $this->getClient()->request('get', $uri, ['connect_timeout' => $this->connectTimeout]); |
|
317 | |||
318 | 4 | if ($response->getStatusCode() !== 200) { |
|
319 | 1 | throw new FetcherException( |
|
320 | 1 | 'an error occured while fetching version data from URI ' . $uri . ': StatusCode was ' |
|
321 | 1 | . $response->getStatusCode() |
|
322 | ); |
||
323 | } |
||
324 | |||
325 | try { |
||
326 | 3 | $remoteVersion = $response->getBody()->getContents(); |
|
327 | 1 | } catch (\Exception $e) { |
|
328 | 1 | throw new FetcherException( |
|
329 | 1 | 'an error occured while fetching version data from URI ' . $uri . ': StatusCode was ' |
|
330 | 1 | . $response->getStatusCode(), |
|
331 | 1 | 0, |
|
332 | 1 | $e |
|
333 | ); |
||
334 | } |
||
335 | |||
336 | 2 | if (! $remoteVersion) { |
|
337 | // could not load remote version |
||
338 | $this->getLogger()->info('could not load version from remote location'); |
||
339 | |||
340 | return 0; |
||
341 | } |
||
342 | |||
343 | 2 | if ($cachedVersion && $remoteVersion && $remoteVersion <= $cachedVersion) { |
|
344 | // no newer version available |
||
345 | 1 | $this->getLogger()->info('there is no newer version available'); |
|
346 | |||
347 | 1 | return null; |
|
348 | } |
||
349 | |||
350 | 1 | $this->getLogger()->info( |
|
351 | 1 | 'a newer version is available, local version: ' . $cachedVersion . ', remote version: ' . $remoteVersion |
|
352 | ); |
||
353 | |||
354 | 1 | return (int) $cachedVersion; |
|
355 | } |
||
356 | |||
357 | 5 | private function sanitizeContent(string $content) : string |
|
365 | |||
366 | /** |
||
367 | * reads and parses an ini string and writes the results into the cache |
||
368 | * |
||
369 | * @param \BrowscapPHP\Helper\Converter $converter |
||
370 | * @param string $content |
||
371 | * @param int|null $cachedVersion |
||
372 | */ |
||
373 | 3 | private function storeContent(Converter $converter, string $content, ?int $cachedVersion) |
|
384 | } |
||
385 |
In PHP, under loose comparison (like
==
, or!=
, orswitch
conditions), values of different types might be equal.For
integer
values, zero is a special case, in particular the following results might be unexpected: