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 Session 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 Session, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
88 | class Session |
||
89 | { |
||
90 | use Configurable; |
||
91 | |||
92 | /** |
||
93 | * Set session timeout in seconds. |
||
94 | * |
||
95 | * @var int |
||
96 | * @config |
||
97 | */ |
||
98 | private static $timeout = 0; |
||
99 | |||
100 | /** |
||
101 | * @config |
||
102 | * @var array |
||
103 | */ |
||
104 | private static $session_ips = array(); |
||
105 | |||
106 | /** |
||
107 | * @config |
||
108 | * @var string |
||
109 | */ |
||
110 | private static $cookie_domain; |
||
111 | |||
112 | /** |
||
113 | * @config |
||
114 | * @var string |
||
115 | */ |
||
116 | private static $cookie_path; |
||
117 | |||
118 | /** |
||
119 | * @config |
||
120 | * @var string |
||
121 | */ |
||
122 | private static $session_store_path; |
||
123 | |||
124 | /** |
||
125 | * @config |
||
126 | * @var boolean |
||
127 | */ |
||
128 | private static $cookie_secure = false; |
||
129 | |||
130 | /** |
||
131 | * Session data. |
||
132 | * Will be null if session has not been started |
||
133 | * |
||
134 | * @var array|null |
||
135 | */ |
||
136 | protected $data = null; |
||
137 | |||
138 | /** |
||
139 | * @var array |
||
140 | */ |
||
141 | protected $changedData = array(); |
||
142 | |||
143 | /** |
||
144 | * Get user agent for this request |
||
145 | * |
||
146 | * @return string |
||
147 | */ |
||
148 | protected function userAgent() |
||
156 | |||
157 | /** |
||
158 | * Start PHP session, then create a new Session object with the given start data. |
||
159 | * |
||
160 | * @param array|null|Session $data Can be an array of data (such as $_SESSION) or another Session object to clone. |
||
161 | * If null, this session is treated as unstarted. |
||
162 | */ |
||
163 | public function __construct($data) |
||
171 | |||
172 | /** |
||
173 | * Init this session instance before usage |
||
174 | */ |
||
175 | public function init(HTTPRequest $request) |
||
176 | { |
||
177 | if (!$this->isStarted()) { |
||
178 | $this->start($request); |
||
179 | } |
||
180 | |||
181 | // Funny business detected! |
||
182 | if (isset($this->data['HTTP_USER_AGENT'])) { |
||
183 | if ($this->data['HTTP_USER_AGENT'] !== $this->userAgent()) { |
||
184 | $this->clearAll(); |
||
185 | $this->destroy(); |
||
186 | $this->start($request); |
||
187 | } |
||
188 | } |
||
189 | } |
||
190 | |||
191 | /** |
||
192 | * Destroy existing session and restart |
||
193 | */ |
||
194 | public function restart(HTTPRequest $request) |
||
195 | { |
||
196 | $this->destroy(); |
||
197 | $this->init($request); |
||
198 | } |
||
199 | |||
200 | /** |
||
201 | * Determine if this session has started |
||
202 | * |
||
203 | * @return bool |
||
204 | */ |
||
205 | public function isStarted() |
||
209 | |||
210 | /** |
||
211 | * Begin session |
||
212 | * |
||
213 | * @param $request The request for which to start a session |
||
214 | */ |
||
215 | public function start(HTTPRequest $request) |
||
272 | |||
273 | /** |
||
274 | * Destroy this session |
||
275 | * |
||
276 | * @param bool $removeCookie |
||
277 | */ |
||
278 | public function destroy($removeCookie = true) |
||
294 | |||
295 | /** |
||
296 | * Set session value |
||
297 | * |
||
298 | * @param string $name |
||
299 | * @param mixed $val |
||
300 | * @return $this |
||
301 | */ |
||
302 | public function set($name, $val) |
||
338 | |||
339 | /** |
||
340 | * Merge value with array |
||
341 | * |
||
342 | * @param string $name |
||
343 | * @param mixed $val |
||
344 | */ |
||
345 | public function addToArray($name, $val) |
||
365 | |||
366 | /** |
||
367 | * Get session value |
||
368 | * |
||
369 | * @param string $name |
||
370 | * @return mixed |
||
371 | */ |
||
372 | public function get($name) |
||
403 | |||
404 | /** |
||
405 | * Clear session value |
||
406 | * |
||
407 | * @param string $name |
||
408 | * @return $this |
||
409 | */ |
||
410 | public function clear($name) |
||
441 | |||
442 | /** |
||
443 | * Clear all values |
||
444 | */ |
||
445 | public function clearAll() |
||
457 | |||
458 | /** |
||
459 | * Get all values |
||
460 | * |
||
461 | * @return array|null |
||
462 | */ |
||
463 | public function getAll() |
||
467 | |||
468 | /** |
||
469 | * Set user agent key |
||
470 | */ |
||
471 | public function finalize() |
||
475 | |||
476 | /** |
||
477 | * Save data to session |
||
478 | * Only save the changes, so that anyone manipulating $_SESSION directly doesn't get burned. |
||
479 | */ |
||
480 | public function save(HTTPRequest $request) |
||
492 | |||
493 | /** |
||
494 | * Recursively apply the changes represented in $data to $dest. |
||
495 | * Used to update $_SESSION |
||
496 | * |
||
497 | * @param array $data |
||
498 | * @param array $dest |
||
499 | */ |
||
500 | protected function recursivelyApply($data, &$dest) |
||
513 | |||
514 | /** |
||
515 | * Return the changed data, for debugging purposes. |
||
516 | * |
||
517 | * @return array |
||
518 | */ |
||
519 | public function changedData() |
||
523 | } |
||
524 |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.