Complex classes like Redis 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 Redis, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
56 | class Redis extends Adapter implements Loader { |
||
57 | |||
58 | const DATABASE_DEFAULT = 0;//default database to use |
||
59 | const KEY_PREFIX_IZZUM = 'izzum:';//default key prefix |
||
60 | |||
61 | /** |
||
62 | * default key for configuration of machines |
||
63 | */ |
||
64 | const KEY_CONFIGURATION = 'configuration'; |
||
65 | |||
66 | /** |
||
67 | * configuration:<machine-name> |
||
68 | */ |
||
69 | const KEY_CONFIGURATION_SPECIFIC = '%s:%s'; |
||
70 | /** |
||
71 | * set of entities in a machine: <machine> |
||
72 | */ |
||
73 | const KEY_ENTITYIDS = 'entities:%s:ids'; |
||
74 | /** |
||
75 | * set of entities per state: <machine>, <state> |
||
76 | */ |
||
77 | const KEY_CURRENT_STATES = 'entities:%s:states:%s'; |
||
78 | |||
79 | /** |
||
80 | * state of an entity: <machine>, <id> |
||
81 | */ |
||
82 | const KEY_ENTITY_STATE = 'entities:%s:state:%s'; |
||
83 | |||
84 | |||
85 | /** |
||
86 | * this key stores the count of all transitions and is used as the id that references the canonical form. |
||
87 | * transitions are added by incrementing this counter and using that id as key to the entries of the canonical forms |
||
88 | * of the transitions (stored as a redis hash) |
||
89 | */ |
||
90 | const KEY_COUNTERS_TRANSITIONS_ALL = 'counters:transitions:all'; |
||
91 | |||
92 | /** |
||
93 | * the keys that stores the canonical form of the transitions, storing the transition data. |
||
94 | * all other transitions will reference the id/counter of the canonical form |
||
95 | */ |
||
96 | const KEY_TRANSITIONS_CANONICAL = 'transitions:canonical:%s';//hash: $counter (KEY_COUNTERS_TRANSITIONS_ALL) |
||
|
|||
97 | |||
98 | /** |
||
99 | * the next keys store id's in sorted sets, scored by timestamp. the id's reference the canonical form. |
||
100 | * the keys provide different views on the data that can easily be retrieved by using: |
||
101 | * - sort: the sort command allows you to reference multiple transitions as referenced in a list or set, in one command |
||
102 | * - zcount: count the elements in a sorted set. this gives you an easy count on the number of transitions per |
||
103 | * machine, state, entity etc. since those are all stored. |
||
104 | */ |
||
105 | const KEY_TRANSITIONS_ALL = 'transitions:all:normal';//sorted set |
||
106 | const KEY_TRANSITIONS_MACHINES = 'transitions:machines:normal:%s';//sorted set: $machine |
||
107 | const KEY_TRANSITIONS_STATES = 'transitions:states:normal:%s:%s';//sorted set: $machine, $state |
||
108 | const KEY_TRANSITIONS_ENTITIES = 'transitions:entities:normal:%s:%s';//sorted set: $machine, $entity_id |
||
109 | const KEY_TRANSITIONS_ALL_FAILED = 'transitions:all:failed';//sorted set |
||
110 | const KEY_TRANSITIONS_MACHINES_FAILED = 'transitions:machines:failed:%s';//sorted set:$machine |
||
111 | const KEY_TRANSITIONS_STATES_FAILED = 'transitions:states:failed:%s:%s';//sorted set: $machine, $state |
||
112 | const KEY_TRANSITIONS_ENTITIES_FAILED = 'transitions:entities:failed:%s:%s';//sorted set: $machine, $entity_id |
||
113 | |||
114 | |||
115 | |||
116 | private $host; |
||
117 | private $port; |
||
118 | private $timeout; |
||
119 | private $reserved; |
||
120 | private $retry; |
||
121 | private $socket; |
||
122 | private $password; |
||
123 | private $database; |
||
124 | private $prefix; |
||
125 | private $configuration_key; |
||
126 | |||
127 | /** |
||
128 | * connected and optionally authenticated redis connection. |
||
129 | * @var \Redis |
||
130 | */ |
||
131 | private $redis; |
||
132 | |||
133 | |||
134 | /** |
||
135 | * The constructor accepts default connection parameters. |
||
136 | * |
||
137 | * You can also use an existing \Redis instance used by your application. |
||
138 | * Just construct without parameters and call 'setConnection($instance)' before doing anything else. |
||
139 | * |
||
140 | * You can also use a unix domain socket. Just construct without parameters |
||
141 | * and call 'setUnixDomainSocket' before doing anything else. |
||
142 | * |
||
143 | * @param string $host optional |
||
144 | * @param int $port optional |
||
145 | * @param float $timeout value in seconds. default is 0 meaning unlimited |
||
146 | * @param string $reserved should be NULL if $retry is specified |
||
147 | * @param int $retry value in milliseconds |
||
148 | */ |
||
149 | 3 | public function __construct($host = '127.0.0.1', $port = 6379, $timeout = 0, $reserved = null, $retry = null) |
|
164 | |||
165 | public function setUnixDomainSocket($socket) |
||
175 | |||
176 | /** |
||
177 | * set password to authenticate to the redis server |
||
178 | * @param string $password |
||
179 | */ |
||
180 | public function setPassword($password) { |
||
183 | |||
184 | /** |
||
185 | * set the redis database. in case there is an active connection, it switches the database. |
||
186 | * @param int $database a redis database is an integer starting from 0 (the default) |
||
187 | */ |
||
188 | 3 | public function setDatabase($database) { |
|
194 | |||
195 | /** |
||
196 | * set the redis connection explicitely, useful if you want to share the |
||
197 | * redis instance when it is created outside this class. |
||
198 | * @param \Redis $redis a connected (and authenticated) redis instance |
||
199 | */ |
||
200 | public function setConnection(\Redis $redis) |
||
204 | |||
205 | /** |
||
206 | * set the key prefix to be used for all redis keys |
||
207 | * @param string $prefix |
||
208 | */ |
||
209 | 3 | final public function setPrefix($prefix) { |
|
215 | |||
216 | /** |
||
217 | * set the configuration key to be used for storing a json string of machine configurations. |
||
218 | * @param string $key |
||
219 | */ |
||
220 | 3 | final public function setConfigurationKey($key) { |
|
223 | |||
224 | /** |
||
225 | * get the configuration key used for storing a json string of machine configurations. |
||
226 | * @return string $key |
||
227 | */ |
||
228 | 3 | final public function getConfigurationKey() { |
|
231 | |||
232 | /** |
||
233 | * get the prefix for all keys used |
||
234 | * @return string |
||
235 | */ |
||
236 | 3 | final public function getPrefix() |
|
240 | |||
241 | /** |
||
242 | * Gets a lazy loaded \Redis instance that is connected and optionally authenticated. |
||
243 | * |
||
244 | * @throws Exception |
||
245 | * @return \Redis |
||
246 | */ |
||
247 | 3 | public function getRedis() { |
|
292 | |||
293 | /** |
||
294 | * A hook to use in a subclass. |
||
295 | * you can do you initial setup here if you like. |
||
296 | */ |
||
297 | 3 | protected function onConnect() { |
|
300 | |||
301 | /** |
||
302 | * {@inheritDoc} |
||
303 | */ |
||
304 | 2 | public function processGetState(Identifier $identifier) { |
|
322 | |||
323 | |||
324 | /** |
||
325 | * {@inheritDoc} |
||
326 | */ |
||
327 | 2 | public function isPersisted(Identifier $identifier) { |
|
339 | |||
340 | |||
341 | /** |
||
342 | * {@inheritDoc} |
||
343 | */ |
||
344 | 2 | public function insertState(Identifier $identifier, $state, $message = null) |
|
367 | |||
368 | |||
369 | |||
370 | /** |
||
371 | * {@inheritDoc} |
||
372 | */ |
||
373 | 2 | public function updateState(Identifier $identifier, $state, $message = null) |
|
404 | |||
405 | /** |
||
406 | * {@inheritDoc} |
||
407 | */ |
||
408 | 2 | public function addHistory(Identifier $identifier, $state, $message = null, $is_exception = false) |
|
489 | |||
490 | /** |
||
491 | * {@inheritDoc} |
||
492 | */ |
||
493 | 2 | public function getEntityIds($machine, $state = null) { |
|
512 | |||
513 | /** |
||
514 | * {@inheritDoc} |
||
515 | * Load the statemachine with data from a JSON string. |
||
516 | * the JSON string is stored at the redis key '<prefix:>configuration' by default. |
||
517 | * you can alter the configuration key by using Redis::setPrefix() and Redis::setConfigurationKey() |
||
518 | * |
||
519 | * First, the key '<prefix>:configuration:<machine-name>' is checked for existence. |
||
520 | * If it exists, take the configuration from that key, else take the configuration form |
||
521 | * the '<prefix>:configuration' key. |
||
522 | * |
||
523 | * This method can be overriden in a subclass to use another loader when |
||
524 | * the data is stored in redis in YAML or XML form for example. |
||
525 | * You could use the ReaderWriterDelegator to use another source to load the configuration from. |
||
526 | */ |
||
527 | 3 | public function load(StateMachine $statemachine) { |
|
539 | |||
540 | |||
541 | /** |
||
542 | * do some cleanup |
||
543 | */ |
||
544 | public function __destruct() |
||
555 | |||
556 | /** |
||
557 | * very very dumb proxy to redis connection. should only be used for testing. |
||
558 | * calls methods on a https://github.com/phpredis/phpredis instance. |
||
559 | * |
||
560 | * some method calls will only accept an array as the second argument (like hmset) |
||
561 | * |
||
562 | * This makes it useful to test the redis commands or just use this class as an interface to redis. |
||
563 | * |
||
564 | * @param string $name name of the method to route to the active redis connection |
||
565 | * @param mixed $arguments |
||
566 | * @return mixed |
||
567 | */ |
||
568 | 3 | public function __call($name, $arguments) |
|
573 | |||
574 | 2 | public function toString() |
|
578 | |||
579 | 2 | public function __toString() |
|
583 | |||
584 | } |
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.
The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.
This check looks for comments that seem to be mostly valid code and reports them.