Complex classes like Bouncer 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 Bouncer, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
17 | class Bouncer |
||
18 | { |
||
19 | |||
20 | const NICE = 'nice'; |
||
21 | const OK = 'ok'; |
||
22 | const SUSPICIOUS = 'suspicious'; |
||
23 | const BAD = 'bad'; |
||
24 | |||
25 | const ROBOT = 'robot'; |
||
26 | const BROWSER = 'browser'; |
||
27 | const UNKNOWN = 'unknown'; |
||
28 | |||
29 | /** |
||
30 | * @var array |
||
31 | */ |
||
32 | public static $supportedOptions = array( |
||
33 | 'cache', |
||
34 | 'request', |
||
35 | 'logger', |
||
36 | 'profile', |
||
37 | 'cookieName', |
||
38 | 'cookiePath', |
||
39 | 'exitHandler', |
||
40 | 'responseCodeHandler' |
||
41 | ); |
||
42 | |||
43 | /** |
||
44 | * @var string|object |
||
45 | */ |
||
46 | protected $profile; |
||
47 | |||
48 | /** |
||
49 | * @var boolean |
||
50 | */ |
||
51 | protected $throwExceptions = false; |
||
52 | |||
53 | /** |
||
54 | * @var boolean |
||
55 | */ |
||
56 | protected $logErrors = true; |
||
57 | |||
58 | /** |
||
59 | * @var string |
||
60 | */ |
||
61 | protected $cookieName = 'bsid'; |
||
62 | |||
63 | /** |
||
64 | * @var string |
||
65 | */ |
||
66 | protected $cookiePath = '/'; |
||
67 | |||
68 | /** |
||
69 | * The exit callable to use when blocking a request |
||
70 | * |
||
71 | * @var callable |
||
72 | */ |
||
73 | protected $exitHandler; |
||
74 | |||
75 | /** |
||
76 | * The callable to use to set the HTTP Response Code |
||
77 | * |
||
78 | * @var callable |
||
79 | */ |
||
80 | protected $responseCodeHandler; |
||
81 | |||
82 | /** |
||
83 | * @var \Bouncer\Cache\CacheInterface |
||
84 | */ |
||
85 | protected $cache; |
||
86 | |||
87 | /** |
||
88 | * @var \Bouncer\Logger\LoggerInterface |
||
89 | */ |
||
90 | protected $logger; |
||
91 | |||
92 | /** |
||
93 | * @var Request |
||
94 | */ |
||
95 | protected $request; |
||
96 | |||
97 | /** |
||
98 | * @var array |
||
99 | */ |
||
100 | protected $response; |
||
101 | |||
102 | /** |
||
103 | * @var array |
||
104 | */ |
||
105 | protected $analyzers = array(); |
||
106 | |||
107 | /** |
||
108 | * @var Identity |
||
109 | */ |
||
110 | protected $identity; |
||
111 | |||
112 | /** |
||
113 | * Store internal metadata |
||
114 | * |
||
115 | * @var array |
||
116 | */ |
||
117 | protected $context; |
||
118 | |||
119 | /** |
||
120 | * @var boolean |
||
121 | */ |
||
122 | protected $started = false; |
||
123 | |||
124 | /** |
||
125 | * @var boolean |
||
126 | */ |
||
127 | protected $ended = false; |
||
128 | |||
129 | /** |
||
130 | * Constructor. |
||
131 | * |
||
132 | * @param array $options |
||
133 | */ |
||
134 | public function __construct(array $options = array()) |
||
135 | { |
||
136 | if (!empty($options)) { |
||
137 | $this->setOptions($options); |
||
138 | } |
||
139 | |||
140 | // Load Profile |
||
141 | if (!$this->profile) { |
||
142 | $this->profile = new \Bouncer\Profile\DefaultProfile; |
||
143 | } |
||
144 | |||
145 | call_user_func_array(array($this->profile, 'load'), array($this)); |
||
146 | } |
||
147 | |||
148 | /* |
||
149 | * Set the supported options |
||
150 | */ |
||
151 | public function setOptions(array $options = array()) |
||
152 | { |
||
153 | foreach (static::$supportedOptions as $key) { |
||
154 | if (isset($options[$key])) { |
||
155 | $this->$key = $options[$key]; |
||
156 | } |
||
157 | } |
||
158 | } |
||
159 | |||
160 | /** |
||
161 | * @throw Exception |
||
162 | */ |
||
163 | public function error($message) |
||
164 | { |
||
165 | if ($this->throwExceptions) { |
||
166 | throw new Exception($message); |
||
167 | } |
||
168 | if ($this->logErrors) { |
||
169 | error_log("Bouncer: {$message}"); |
||
170 | } |
||
171 | } |
||
172 | |||
173 | /** |
||
174 | * @return \Bouncer\Cache\CacheInterface |
||
175 | */ |
||
176 | public function getCache($reportError = false) |
||
177 | { |
||
178 | if (empty($this->cache)) { |
||
179 | if ($reportError) { |
||
180 | $this->error('No cache available.'); |
||
181 | } |
||
182 | return; |
||
183 | } |
||
184 | |||
185 | return $this->cache; |
||
186 | } |
||
187 | |||
188 | /** |
||
189 | * @return \Bouncer\Logger\LoggerInterface |
||
190 | */ |
||
191 | public function getLogger($reportError = false) |
||
192 | { |
||
193 | if (empty($this->logger)) { |
||
194 | if ($reportError) { |
||
195 | $this->error('No logger available.'); |
||
196 | } |
||
197 | return; |
||
198 | } |
||
199 | |||
200 | return $this->logger; |
||
201 | } |
||
202 | |||
203 | /** |
||
204 | * @return Request |
||
205 | */ |
||
206 | public function getRequest() |
||
207 | { |
||
208 | if (isset($this->request)) { |
||
209 | return $this->request; |
||
210 | } |
||
211 | |||
212 | $request = Request::createFromGlobals(); |
||
213 | $request->setTrustedProxies(array('127.0.0.1')); |
||
214 | $request->setTrustedHeaderName(Request::HEADER_FORWARDED, null); |
||
215 | |||
216 | return $this->request = $request; |
||
217 | } |
||
218 | |||
219 | /** |
||
220 | * @return array |
||
221 | */ |
||
222 | public function getResponse() |
||
223 | { |
||
224 | return $this->response; |
||
225 | } |
||
226 | |||
227 | /** |
||
228 | * @return string |
||
229 | */ |
||
230 | public function getUserAgent() |
||
231 | { |
||
232 | return $this->getRequest()->getUserAgent(); |
||
233 | } |
||
234 | |||
235 | /** |
||
236 | * @return string |
||
237 | */ |
||
238 | public function getAddr() |
||
239 | { |
||
240 | return $this->getRequest()->getAddr(); |
||
241 | } |
||
242 | |||
243 | /** |
||
244 | * @return Address |
||
245 | */ |
||
246 | public function getAddress() |
||
247 | { |
||
248 | $addr = $this->getRequest()->getAddr(); |
||
249 | |||
250 | $address = new Address($addr); |
||
251 | |||
252 | return $address; |
||
253 | } |
||
254 | |||
255 | /** |
||
256 | * @return array |
||
257 | */ |
||
258 | public function getHeaders() |
||
259 | { |
||
260 | $request = $this->getRequest(); |
||
261 | |||
262 | $headers = $request->getHeaders(); |
||
263 | |||
264 | return $headers; |
||
265 | } |
||
266 | |||
267 | /** |
||
268 | * @return Signature |
||
269 | */ |
||
270 | public function getSignature() |
||
271 | { |
||
272 | $headers = $this->getHeaders(); |
||
273 | |||
274 | $signature = new Signature(array('headers' => $headers)); |
||
275 | |||
276 | return $signature; |
||
277 | } |
||
278 | |||
279 | /** |
||
280 | * @return array |
||
281 | */ |
||
282 | public function getCookies() |
||
283 | { |
||
284 | $names = array($this->cookieName, '__utmz', '__utma'); |
||
285 | |||
286 | $request = $this->getRequest(); |
||
287 | |||
288 | return $request->getCookies($names); |
||
289 | } |
||
290 | |||
291 | /** |
||
292 | * Return the current session id (from Cookie) |
||
293 | * |
||
294 | * @return string|null |
||
295 | */ |
||
296 | public function getSessionId() |
||
302 | |||
303 | /** |
||
304 | * Return the protocol of the request: HTTP/1.0 or HTTP/1.1 |
||
305 | * |
||
306 | * @return string|null |
||
307 | */ |
||
308 | public function getProtocol() |
||
309 | { |
||
310 | $request = $this->getRequest(); |
||
311 | |||
312 | return $request->getProtocol(); |
||
313 | } |
||
314 | |||
315 | /** |
||
316 | * @return Identity |
||
317 | */ |
||
318 | public function getIdentity() |
||
356 | |||
357 | /** |
||
358 | * @return array |
||
359 | */ |
||
360 | public function getContext() |
||
368 | |||
369 | /* |
||
370 | * Init the context with time and pid. |
||
371 | */ |
||
372 | public function initContext() |
||
380 | |||
381 | /* |
||
382 | * @param string $key |
||
383 | * @param boolean|string|array $properties |
||
384 | */ |
||
385 | public function addContext($key, $properties) |
||
393 | |||
394 | /* |
||
395 | * Complete the context with session, exec_time and memory_usage. |
||
396 | */ |
||
397 | public function completeContext() |
||
418 | |||
419 | /* |
||
420 | * Complete the response with status code |
||
421 | */ |
||
422 | public function completeResponse() |
||
436 | /* |
||
437 | * Register an analyzer for a given type. |
||
438 | * |
||
439 | * @param string |
||
440 | * @param callable |
||
441 | * @param int |
||
442 | */ |
||
443 | public function registerAnalyzer($type, $callable, $priority = 100) |
||
447 | |||
448 | /* |
||
449 | * Process Analyzers for a given type. Return the modified array or object. |
||
450 | * |
||
451 | * @param string |
||
452 | * @param object |
||
453 | * |
||
454 | * @return object |
||
455 | */ |
||
456 | protected function processAnalyzers($type, $value) |
||
467 | |||
468 | /* |
||
469 | * Start Bouncer, init context and register end function |
||
470 | */ |
||
471 | public function start() |
||
484 | |||
485 | /* |
||
486 | * Set a cookie containing the session id |
||
487 | */ |
||
488 | public function initSession() |
||
501 | |||
502 | /* |
||
503 | * Throttle |
||
504 | * |
||
505 | * @param int $minimum in milliseconds |
||
506 | * @param int $maximum in milliseconds |
||
507 | * |
||
508 | */ |
||
509 | public function throttle($minimum = 1000, $maximum = 2500) |
||
520 | |||
521 | /* |
||
522 | * @deprecated deprecated since version 2.1.0 |
||
523 | */ |
||
524 | public function sleep($statuses = array(), $minimum = 1000, $maximum = 2500) |
||
532 | |||
533 | /* |
||
534 | * Block |
||
535 | * |
||
536 | * @param string $type |
||
537 | * @param array $extra |
||
538 | * |
||
539 | */ |
||
540 | public function block($type = null, $extra = null) |
||
565 | |||
566 | /* |
||
567 | * @deprecated deprecated since version 2.1.0 |
||
568 | */ |
||
569 | public function ban($statuses = array()) |
||
578 | |||
579 | /* |
||
580 | * @param string $type |
||
581 | * @param array $extra |
||
582 | */ |
||
583 | public function registerEvent($type, $extra = null) |
||
590 | |||
591 | /* |
||
592 | * Complete the connection then attempt to log. |
||
593 | */ |
||
594 | public function end() |
||
617 | |||
618 | /* |
||
619 | * Log the connection to the logging backend. |
||
620 | */ |
||
621 | public function log() |
||
637 | |||
638 | // Static |
||
639 | |||
640 | public static function hash($value) |
||
644 | |||
645 | } |
||
646 |
An exit expression should only be used in rare cases. For example, if you write a short command line script.
In most cases however, using an
exit
expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.