Complex classes like AbstractStorage 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 AbstractStorage, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
14 | abstract class AbstractStorage implements CacheInterface { |
||
15 | const StorageTemporary = 0; |
||
16 | const StoragePersistent = 1; |
||
17 | const RegionDomain = 0;// Только в контексте этого HostName |
||
18 | const RegionServer = 1;// В контексте ВСЕГО дедика |
||
19 | const RegionFolder = 2;// В контексте папки домена (для разных хостов, запущенных из одной папки) |
||
20 | |||
21 | /** |
||
22 | * @var string Префикс к названиям полей ключей для бесконфликтного использования в глобалсе |
||
23 | */ |
||
24 | protected $_prefix; |
||
25 | |||
26 | /** |
||
27 | * @var KeyValueSettings|object Настройки, переданные в конструктор и конструктором исправленные |
||
28 | */ |
||
29 | protected $settings; |
||
30 | |||
31 | /** |
||
32 | * @var MutexInterface[]|null[] |
||
33 | * Мьютекс, созданный через MutexInterface, настройки задаются конкретной реализацией |
||
34 | */ |
||
35 | protected $_locks = []; |
||
36 | |||
37 | /** |
||
38 | * @var string|null |
||
39 | */ |
||
40 | protected $_last_used_lock_key = null; |
||
41 | |||
42 | /** |
||
43 | * @param KeyValueSettings|object $settings |
||
44 | */ |
||
45 | abstract function __construct($settings); |
||
46 | |||
47 | /** |
||
48 | * Дёргаем значение из kv-хранилища |
||
49 | * |
||
50 | * @param string $key Название ключа |
||
51 | * @param string|mixed $default_value Дефолтное значение, если настоящее недоступно |
||
52 | * |
||
53 | * @return string|mixed |
||
54 | */ |
||
55 | 75 | function get_value($key, $default_value = '') { |
|
56 | 75 | $data = $this->get_value_full($key); |
|
57 | |||
58 | 75 | return is_null($data) ? $default_value : $data->value; |
|
59 | } |
||
60 | |||
61 | /** |
||
62 | * Формируем datum типа объект, который мы будем вносить в наше kv-хранилище |
||
63 | * |
||
64 | * @param string|integer $key Ключ |
||
65 | * @param mixed $value Новое значение |
||
66 | * @param double $ttl Кол-во секунд, после которых значение будет считаться просроченным |
||
67 | * |
||
68 | * @return object|KeyValueDatum |
||
69 | */ |
||
70 | 140 | protected function form_datum_value($key, $value, $ttl) { |
|
71 | 140 | $backtrace = debug_backtrace(); |
|
72 | $data = (object) [ |
||
73 | 140 | 'key' => $key, |
|
74 | 140 | 'time_create' => microtime(true), |
|
75 | 140 | 'time_expires' => microtime(true) + $ttl, |
|
76 | 140 | 'host' => isset($_SERVER, $_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : null, |
|
77 | 140 | 'value' => $value, |
|
78 | 140 | 'init_file' => isset($backtrace[1], $backtrace[1]['file']) ? $backtrace[1]['file'] : null, |
|
79 | 140 | 'init_line' => isset($backtrace[1], $backtrace[1]['line']) ? $backtrace[1]['line'] : null, |
|
80 | 140 | 'pid' => function_exists('posix_getpid') ? posix_getpid() : null,// @todo windows |
|
81 | 28 | ]; |
|
82 | 140 | unset($backtrace); |
|
83 | |||
84 | 140 | return $data; |
|
85 | } |
||
86 | |||
87 | /** |
||
88 | * Пишем новое значение для key в нашем kv-хранилище |
||
89 | * |
||
90 | * @param string $key Название ключа |
||
91 | * @param mixed $value Новое значение |
||
92 | * @param double|integer $ttl Кол-во секунд, после которых значение будет считаться просроченным |
||
93 | * |
||
94 | * @throws KeyValueException |
||
95 | */ |
||
96 | abstract function set_value($key, $value, $ttl = 315576000); |
||
97 | |||
98 | /** |
||
99 | * @param string $key |
||
100 | * |
||
101 | * @return object|null |
||
102 | */ |
||
103 | abstract protected function get_value_full_clear($key); |
||
104 | |||
105 | /** |
||
106 | * Получение полных данных по вхождению в kv-хранилище |
||
107 | * |
||
108 | * @param string $key Название ключа |
||
109 | * |
||
110 | * @return object|KeyValueDatum|null |
||
111 | */ |
||
112 | 130 | function get_value_full($key) { |
|
113 | 130 | $ts1 = microtime(true); |
|
114 | 130 | $data = $this->get_value_full_clear($key); |
|
115 | 130 | self::add_profiling(microtime(true) - $ts1, static::class, 'get_value_full_clear'); |
|
116 | 130 | if (is_null($data)) { |
|
117 | 125 | return null; |
|
118 | } |
||
119 | |||
120 | // Проверяем expires |
||
121 | 115 | if (!isset($data->time_expires) or ($data->time_expires < microtime(true))) { |
|
122 | 23 | return null; |
|
123 | } |
||
124 | |||
125 | 107 | return $data; |
|
126 | } |
||
127 | |||
128 | /** |
||
129 | * Удаляем вхождение kv-хранилища |
||
130 | * |
||
131 | * @param string $key Название ключа |
||
132 | */ |
||
133 | abstract function delete_value($key); |
||
134 | |||
135 | /** |
||
136 | * Стандартная стратегия выбора префиксов |
||
137 | */ |
||
138 | 525 | function standard_prefix_strategy() { |
|
150 | |||
151 | /** |
||
152 | * @param integer $region_type |
||
153 | * |
||
154 | * @return string |
||
155 | * @throws KeyValueException |
||
156 | */ |
||
157 | 525 | static function get_default_prefix_from_environment($region_type = self::RegionDomain) { |
|
172 | |||
173 | /** |
||
174 | * @param string $key |
||
175 | * |
||
176 | * @return string |
||
177 | * @throws KeyValueException |
||
178 | */ |
||
179 | 495 | static function get_environment($key) { |
|
189 | |||
190 | /** |
||
191 | * @return string |
||
192 | */ |
||
193 | 115 | function get_prefix() { |
|
196 | |||
197 | /** |
||
198 | * Время просрочки значения, если есть |
||
199 | * |
||
200 | * @param string $key |
||
201 | * |
||
202 | * @return double|null |
||
203 | */ |
||
204 | 15 | function get_expires_time($key) { |
|
212 | |||
213 | /** |
||
214 | * Обновляем срок жизни записи |
||
215 | * |
||
216 | * @param string $key Название ключа |
||
217 | * @param double $ttl Кол-во секунд, после которых значение будет считаться просроченным |
||
218 | * @throws KeyValueException |
||
219 | */ |
||
220 | 15 | function set_expires_time($key, $ttl) { |
|
227 | |||
228 | /** |
||
229 | * Время задания значения |
||
230 | * |
||
231 | * @param string $key |
||
232 | * |
||
233 | * @return double|null |
||
234 | */ |
||
235 | 45 | function get_change_time($key) { |
|
243 | |||
244 | /** |
||
245 | * Время задания значения |
||
246 | * |
||
247 | * @param string $key |
||
248 | * |
||
249 | * @return boolean |
||
250 | */ |
||
251 | 85 | function is_exist($key) { |
|
254 | |||
255 | /** |
||
256 | * @var double[][] |
||
257 | */ |
||
258 | private static $_profiling = []; |
||
259 | |||
260 | /** |
||
261 | * @param double $time |
||
262 | * @param string $class |
||
263 | * @param string $action |
||
264 | */ |
||
265 | 170 | protected static function add_profiling($time, $class, $action) { |
|
274 | |||
275 | /** |
||
276 | * Весь профайлинг |
||
277 | * |
||
278 | * @return double[]|double[][] |
||
279 | */ |
||
280 | 5 | static function get_profiling() { |
|
303 | |||
304 | /** |
||
305 | * Создаём мьютекс, соответствующий ключу и кладём его в _locks |
||
306 | * |
||
307 | * @param string $key |
||
308 | */ |
||
309 | 5 | protected function create_lock($key) { |
|
317 | |||
318 | /** |
||
319 | * Лочим мьютекс, соответствующий ключу |
||
320 | * |
||
321 | * @param string $key |
||
322 | */ |
||
323 | 155 | protected function get_lock($key) { |
|
330 | |||
331 | /** |
||
332 | * Снимаем мьютекс |
||
333 | */ |
||
334 | 155 | protected function release_lock() { |
|
339 | |||
340 | /** |
||
341 | * Удаляем все созданные мьютексы, не удаляя сам объект Storage |
||
342 | * |
||
343 | * @param integer|null $minimal_count_for_delete |
||
344 | */ |
||
345 | 25 | function clear_mutex_list($minimal_count_for_delete = null) { |
|
350 | |||
351 | /** |
||
352 | * PSR-16 wrapper |
||
353 | */ |
||
354 | |||
355 | /** |
||
356 | * @param string $key |
||
357 | * |
||
358 | * @throws \Psr\SimpleCache\InvalidArgumentException |
||
359 | */ |
||
360 | 335 | protected function is_key_valid($key) { |
|
368 | |||
369 | /** |
||
370 | * @param string $key |
||
371 | * @param null $default |
||
372 | * |
||
373 | * @return mixed |
||
374 | * @throws \Psr\SimpleCache\InvalidArgumentException |
||
375 | */ |
||
376 | 160 | function get($key, $default = null) { |
|
381 | |||
382 | /** |
||
383 | * @param string $key |
||
384 | * |
||
385 | * @return boolean |
||
386 | * @throws \Psr\SimpleCache\InvalidArgumentException |
||
387 | */ |
||
388 | 85 | function delete($key) { |
|
394 | |||
395 | /** |
||
396 | * @param string $key |
||
397 | * @param mixed $value |
||
398 | * @param null $ttl |
||
399 | * |
||
400 | * @return boolean |
||
401 | * @throws \Psr\SimpleCache\InvalidArgumentException |
||
402 | */ |
||
403 | 135 | function set($key, $value, $ttl = null) { |
|
413 | |||
414 | /** |
||
415 | * @codeCoverageIgnore |
||
416 | */ |
||
417 | function clear() { |
||
420 | |||
421 | /** |
||
422 | * @param \iterable $keys |
||
423 | * @param null $default |
||
424 | * |
||
425 | * @return array |
||
426 | * @throws \Psr\SimpleCache\InvalidArgumentException |
||
427 | */ |
||
428 | 30 | function getMultiple($keys, $default = null) { |
|
436 | |||
437 | /** |
||
438 | * @param \iterable $keys |
||
439 | * @param null $ttl |
||
440 | * |
||
441 | * @return boolean |
||
442 | * @throws \Psr\SimpleCache\InvalidArgumentException |
||
443 | */ |
||
444 | 30 | function setMultiple($keys, $ttl = null) { |
|
454 | |||
455 | /** |
||
456 | * @param iterable $keys |
||
457 | * |
||
458 | * @return boolean |
||
459 | * @throws \Psr\SimpleCache\InvalidArgumentException |
||
460 | */ |
||
461 | 90 | function deleteMultiple($keys) { |
|
473 | |||
474 | /** |
||
475 | * @param string $key |
||
476 | * |
||
477 | * @throws \Psr\SimpleCache\InvalidArgumentException |
||
478 | * @return boolean |
||
479 | */ |
||
480 | 145 | function has($key) { |
|
485 | } |
||
486 | |||
487 | ?> |
If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.
Let’s take a look at an example:
Our function
my_function
expects aPost
object, and outputs the author of the post. The base classPost
returns a simple string and outputting a simple string will work just fine. However, the child classBlogPost
which is a sub-type ofPost
instead decided to return anobject
, and is therefore violating the SOLID principles. If aBlogPost
were passed tomy_function
, PHP would not complain, but ultimately fail when executing thestrtoupper
call in its body.