Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
Complex classes like SettingBase 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 SettingBase, and based on these observations, apply Extract Interface, too.
1 | <?php namespace Cornford\Setter; |
||
9 | abstract class SettingBase { |
||
10 | |||
11 | const LOCATION_DATABASE = 'database'; |
||
12 | const LOCATION_CACHE = 'cache'; |
||
13 | |||
14 | const CACHE_ENABLED = true; |
||
15 | const CACHE_TAG = 'setter::'; |
||
16 | const CACHE_EXPIRY = true; |
||
17 | |||
18 | /** |
||
19 | * Database |
||
20 | * |
||
21 | * @var \Illuminate\Database\DatabaseManager |
||
22 | */ |
||
23 | protected $databaseInstance; |
||
24 | |||
25 | /** |
||
26 | * Config |
||
27 | * |
||
28 | * @var \Illuminate\Config\Repository |
||
29 | */ |
||
30 | protected $config; |
||
31 | |||
32 | /** |
||
33 | * Cache |
||
34 | * |
||
35 | * @var \Illuminate\Cache\Repository |
||
36 | */ |
||
37 | protected $cache; |
||
38 | |||
39 | /** |
||
40 | * Caching Enabled? |
||
41 | * |
||
42 | * @var boolean |
||
43 | */ |
||
44 | protected $cacheEnabled = true; |
||
45 | |||
46 | /** |
||
47 | * Cache Tag |
||
48 | * |
||
49 | * @var string |
||
50 | */ |
||
51 | protected $cacheTag; |
||
52 | |||
53 | /** |
||
54 | * Cache |
||
55 | * |
||
56 | * @var integer|datetime|boolean |
||
57 | */ |
||
58 | protected $cacheExpiry; |
||
59 | |||
60 | /** |
||
61 | * Un-cached? |
||
62 | * |
||
63 | * @var boolean |
||
64 | */ |
||
65 | protected $uncached = false; |
||
66 | |||
67 | /** |
||
68 | * Construct Setter |
||
69 | * |
||
70 | * @param Query $database |
||
71 | * @param Repository $config |
||
72 | * @param Cache $cache |
||
73 | * @param array $options |
||
74 | * |
||
75 | * @throws SettingArgumentException |
||
76 | */ |
||
77 | public function __construct(Query $database, Repository $config, Cache $cache, array $options = []) |
||
99 | |||
100 | /** |
||
101 | * Set caching enabled status. |
||
102 | * |
||
103 | * @param boolean $value |
||
104 | * |
||
105 | * @throws SettingArgumentException |
||
106 | * |
||
107 | * @return void |
||
108 | */ |
||
109 | protected function setCacheEnabled($value) |
||
117 | |||
118 | /** |
||
119 | * Get the caching enabled status. |
||
120 | * |
||
121 | * @return boolean |
||
122 | */ |
||
123 | protected function getCacheEnabled() |
||
127 | |||
128 | /** |
||
129 | * Cache enabled? |
||
130 | * |
||
131 | * @return boolean |
||
132 | */ |
||
133 | public function cacheEnabled() |
||
134 | { |
||
135 | return ($this->getCacheEnabled() === self::CACHE_ENABLED); |
||
136 | } |
||
137 | |||
138 | /** |
||
139 | * Set the cache tag |
||
140 | * |
||
141 | * @param string $value |
||
142 | * |
||
143 | * @throws SettingArgumentException |
||
144 | * |
||
145 | * @return void |
||
146 | */ |
||
147 | public function setCacheTag($value) |
||
155 | |||
156 | /** |
||
157 | * Get the cache tag |
||
158 | * |
||
159 | * @return string |
||
160 | */ |
||
161 | public function getCacheTag() |
||
165 | |||
166 | /** |
||
167 | * Set the cache expiry |
||
168 | * |
||
169 | * @param boolean|integer|DateTime $value |
||
170 | * |
||
171 | * @throws SettingArgumentException |
||
172 | * |
||
173 | * @return void |
||
174 | */ |
||
175 | View Code Duplication | protected function setCacheExpiry($value) |
|
183 | |||
184 | /** |
||
185 | * Get the cache tag |
||
186 | * |
||
187 | * @return string |
||
188 | */ |
||
189 | protected function getCacheExpiry() |
||
193 | |||
194 | /** |
||
195 | * Set the uncached status. |
||
196 | * |
||
197 | * @param boolean $value |
||
198 | * |
||
199 | * @return void |
||
200 | */ |
||
201 | protected function setUncached($value) |
||
205 | |||
206 | /** |
||
207 | * Get the uncached status. |
||
208 | * |
||
209 | * @return boolean |
||
210 | */ |
||
211 | protected function getUncached() |
||
215 | |||
216 | /** |
||
217 | * Return a key with an attached cache tag |
||
218 | * |
||
219 | * @param string $key |
||
220 | * |
||
221 | * @return string |
||
222 | */ |
||
223 | protected function attachCacheTag($key) |
||
227 | |||
228 | /** |
||
229 | * Check a setting exists in cache |
||
230 | * |
||
231 | * @param string $key |
||
232 | * |
||
233 | * @return boolean |
||
234 | */ |
||
235 | public function cacheHas($key) |
||
239 | |||
240 | /** |
||
241 | * Forget a cached setting by key |
||
242 | * |
||
243 | * @param string $key |
||
244 | * |
||
245 | * @return boolean |
||
246 | */ |
||
247 | public function cacheForget($key) |
||
254 | |||
255 | /** |
||
256 | * Clear all cached settings |
||
257 | * |
||
258 | * @return boolean |
||
259 | */ |
||
260 | public function cacheClear() |
||
267 | |||
268 | /** |
||
269 | * Check a setting exists in config |
||
270 | * |
||
271 | * @param string $key |
||
272 | * |
||
273 | * @return boolean |
||
274 | */ |
||
275 | public function configHas($key) |
||
279 | |||
280 | /** |
||
281 | * Arrange results into an associative array |
||
282 | * |
||
283 | * @param array $results |
||
284 | * @param string $key |
||
285 | * |
||
286 | * @return array |
||
287 | */ |
||
288 | protected function arrangeResults($results, $key = null) |
||
289 | { |
||
290 | $return = array(); |
||
291 | |||
292 | foreach ($results as $path => $value) { |
||
293 | $parts = strpos($path, '.') > 0 ? explode('.', trim(preg_replace('/^' . $key . '/', '', $path), '.')) : array($path); |
||
294 | $target =& $return; |
||
295 | |||
296 | foreach ($parts as $part) { |
||
297 | $target =& $target[$part]; |
||
298 | } |
||
299 | |||
300 | $target = $this->decodeJson($value); |
||
301 | } |
||
302 | |||
303 | return $return; |
||
304 | } |
||
305 | |||
306 | /** |
||
307 | * Return result values |
||
308 | * |
||
309 | * @param array $results |
||
310 | * @param string $key |
||
311 | * |
||
312 | * @return string|array |
||
313 | */ |
||
314 | protected function returnResults($results = array(), $key) |
||
315 | { |
||
316 | $items = $this->arrangeResults($results, $key); |
||
317 | $return = $this->combineResults($items, $key); |
||
318 | |||
319 | if ((!is_array($this->returnConfig($key)) || count($this->returnConfig($key)) == 0) && |
||
320 | (array_key_exists($key, $return) || array_key_exists('', $return)) |
||
321 | && count($return) == 1 |
||
322 | ) { |
||
323 | $return = reset($return); |
||
324 | } |
||
325 | |||
326 | if ($this->cacheEnabled()) { |
||
327 | $this->cache->forget($this->attachCacheTag($key)); |
||
328 | $this->cache->add($this->attachCacheTag($key), $return, $this->getCacheExpiry()); |
||
329 | } |
||
330 | |||
331 | return $this->decodeJson($return); |
||
332 | } |
||
333 | |||
334 | /** |
||
335 | * Combine result values from the database and configuration |
||
336 | * |
||
337 | * @param array $results |
||
338 | * @param string $key |
||
339 | * |
||
340 | * @return array |
||
341 | */ |
||
342 | protected function combineResults(array $results = array(), $key) |
||
343 | { |
||
344 | $config = $this->returnConfig($key); |
||
345 | |||
346 | if (is_array($config)) { |
||
347 | return array_replace_recursive($config, ((array_key_exists($key, $results) || array_key_exists('', $results)) ? reset($results) : $results)); |
||
348 | } |
||
349 | |||
350 | return $results; |
||
351 | } |
||
352 | |||
353 | /** |
||
354 | * Re-cache item and its parents |
||
355 | * |
||
356 | * @param string $value |
||
357 | * @param string $key |
||
358 | * |
||
359 | * @return void |
||
360 | */ |
||
361 | protected function recacheItem($value, $key) |
||
381 | |||
382 | /** |
||
383 | * Return cache values |
||
384 | * |
||
385 | * @param string $key |
||
386 | * |
||
387 | * @return string|array |
||
388 | */ |
||
389 | protected function returnCache($key) |
||
395 | |||
396 | /** |
||
397 | * Return config values |
||
398 | * |
||
399 | * @param string $key |
||
400 | * |
||
401 | * @return string|array |
||
402 | */ |
||
403 | protected function returnConfig($key) |
||
409 | |||
410 | /** |
||
411 | * Is the string Json encoded. |
||
412 | * |
||
413 | * @param string $string |
||
414 | * @return boolean |
||
415 | */ |
||
416 | protected function isJson($string) |
||
426 | |||
427 | /** |
||
428 | * Decode a Json item. |
||
429 | * |
||
430 | * @param mixed $value |
||
431 | * |
||
432 | * @return mixed |
||
433 | */ |
||
434 | protected function decodeJson($value) |
||
442 | |||
443 | } |
||
444 |
An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.
If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.