Complex classes like Errors 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 Errors, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
27 | class Errors extends \AbstractModel |
||
28 | { |
||
29 | /** @var Errors Sole private Errors instance */ |
||
30 | private static $_errors = null; |
||
31 | |||
32 | /** @var string[] The types of categories we have */ |
||
33 | private $errorTypes = array( |
||
34 | 'general', |
||
35 | 'critical', |
||
36 | 'database', |
||
37 | 'undefined_vars', |
||
38 | 'user', |
||
39 | 'template', |
||
40 | 'debug', |
||
41 | ); |
||
42 | |||
43 | /** |
||
44 | * In case of maintenance of very early errors, the database may not be available, |
||
45 | * this __construct will feed AbstractModel with a value just to stop it |
||
46 | * from trying to initialize the database connection. |
||
47 | * |
||
48 | * @param $db Database|null |
||
49 | */ |
||
50 | public function __construct($db = null) |
||
54 | |||
55 | /** |
||
56 | * Halts execution, optionally displays an error message |
||
57 | * |
||
58 | * @param string|integer $error |
||
59 | */ |
||
60 | protected function terminate($error = '') |
||
64 | |||
65 | /** |
||
66 | * @param string $errorType |
||
67 | */ |
||
68 | public function addErrorTypes($errorType) |
||
72 | |||
73 | /** |
||
74 | * @return string[] |
||
75 | */ |
||
76 | protected function getErrorTypes() |
||
77 | { |
||
78 | static $tried_hook = false; |
||
79 | |||
80 | // Perhaps integration wants to add specific error types for the log |
||
81 | $errorTypes = array(); |
||
82 | if (empty($tried_hook)) |
||
83 | { |
||
84 | // This prevents us from infinite looping if the hook or call produces an error. |
||
85 | $tried_hook = true; |
||
86 | call_integration_hook('integrate_error_types', array(&$errorTypes)); |
||
87 | $this->errorTypes += $errorTypes; |
||
88 | } |
||
89 | |||
90 | return $this->errorTypes; |
||
91 | } |
||
92 | |||
93 | /** |
||
94 | * @return string |
||
95 | */ |
||
96 | private function parseQueryString() |
||
97 | { |
||
98 | global $scripturl; |
||
99 | |||
100 | $query_string = empty($_SERVER['QUERY_STRING']) ? (empty($_SERVER['REQUEST_URL']) ? '' : str_replace($scripturl, '', $_SERVER['REQUEST_URL'])) : $_SERVER['QUERY_STRING']; |
||
101 | |||
102 | // Don't log the session hash in the url twice, it's a waste. |
||
103 | $query_string = htmlspecialchars((ELK === 'SSI' ? '' : '?') . preg_replace(array('~;sesc=[^&;]+~', '~' . session_name() . '=' . session_id() . '[&;]~'), array(';sesc', ''), $query_string), ENT_COMPAT, 'UTF-8'); |
||
104 | |||
105 | // Just so we know what board error messages are from. |
||
106 | if (isset($_POST['board']) && !isset($_GET['board'])) |
||
107 | $query_string .= ($query_string == '' ? 'board=' : ';board=') . $_POST['board']; |
||
108 | |||
109 | return $query_string; |
||
110 | } |
||
111 | |||
112 | /** |
||
113 | * Insert an error entry in to the log_errors table |
||
114 | * |
||
115 | * @param string $query_string |
||
116 | * @param string $error_message |
||
117 | * @param string|boolean $error_type |
||
118 | * @param string $file |
||
119 | * @param int $line |
||
120 | */ |
||
121 | private function insertLog($query_string, $error_message, $error_type, $file, $line) |
||
122 | { |
||
123 | global $user_info, $last_error; |
||
124 | |||
125 | $this->_db = database(); |
||
126 | |||
127 | // Just in case there's no id_member or IP set yet. |
||
128 | if (empty($user_info['id'])) |
||
129 | $user_info['id'] = 0; |
||
130 | if (empty($user_info['ip'])) |
||
131 | $user_info['ip'] = ''; |
||
132 | |||
133 | // Don't log the same error countless times, as we can get in a cycle of depression... |
||
134 | $error_info = array($user_info['id'], time(), $user_info['ip'], $query_string, $error_message, isset($_SESSION['session_value']) ? (string) $_SESSION['session_value'] : 'no_session_data', $error_type, $file, $line); |
||
135 | if (empty($last_error) || $last_error != $error_info) |
||
136 | { |
||
137 | // Insert the error into the database. |
||
138 | $this->_db->insert('', |
||
139 | '{db_prefix}log_errors', |
||
140 | array('id_member' => 'int', 'log_time' => 'int', 'ip' => 'string-16', 'url' => 'string-65534', 'message' => 'string-65534', 'session' => 'string', 'error_type' => 'string', 'file' => 'string-255', 'line' => 'int'), |
||
141 | $error_info, |
||
142 | array('id_error') |
||
143 | ); |
||
144 | $last_error = $error_info; |
||
145 | } |
||
146 | } |
||
147 | |||
148 | /** |
||
149 | * Log an error to the error log if the error logging is enabled. |
||
150 | * |
||
151 | * - filename and line should be __FILE__ and __LINE__, respectively. |
||
152 | * |
||
153 | * Example use: |
||
154 | * - die(Errors::instance()->log_error($msg)); |
||
155 | * |
||
156 | * @param string $error_message |
||
157 | * @param string|boolean $error_type = 'general' |
||
158 | * @param string $file = '' |
||
159 | * @param int $line = 0 |
||
160 | * |
||
161 | * @return string |
||
162 | */ |
||
163 | public function log_error($error_message, $error_type = 'general', $file = '', $line = 00) |
||
164 | { |
||
165 | // Check if error logging is actually on. |
||
166 | if (empty($this->_modSettings['enableErrorLogging'])) |
||
167 | return $error_message; |
||
168 | |||
169 | // Basically, htmlspecialchars it minus &. (for entities!) |
||
170 | $error_message = strtr($error_message, array('<' => '<', '>' => '>', '"' => '"')); |
||
171 | $error_message = strtr($error_message, array('<br />' => '<br />', '<b>' => '<strong>', '</b>' => '</strong>', "\n" => '<br />')); |
||
172 | |||
173 | // Add a file and line to the error message? |
||
174 | // Don't use the actual txt entries for file and line but instead use %1$s for file and %2$s for line |
||
175 | // Windows-style slashes don't play well, lets convert them to the unix style. |
||
176 | $file = str_replace('\\', '/', $file); |
||
177 | $line = (int) $line; |
||
178 | |||
179 | // Find the best query string we can... |
||
180 | $query_string = $this->parseQueryString(); |
||
181 | |||
182 | // Make sure the category that was specified is a valid one |
||
183 | $error_type = in_array($error_type, $this->getErrorTypes()) && $error_type !== true ? $error_type : 'general'; |
||
184 | |||
185 | // Insert the error into the database. |
||
186 | $this->insertLog($query_string, $error_message, $error_type, $file, $line); |
||
187 | |||
188 | // Return the message to make things simpler. |
||
189 | return $error_message; |
||
190 | } |
||
191 | |||
192 | /** |
||
193 | * Similar to log_error, it accepts a language index as the error. |
||
194 | * |
||
195 | * What it does: |
||
196 | * |
||
197 | * - Takes care of loading the forum default language |
||
198 | * - Logs the error (forwarding to log_error) |
||
199 | * |
||
200 | * @param string $error |
||
201 | * @param string $error_type = 'general' |
||
202 | * @param string|mixed[] $sprintf = array() |
||
203 | * @param string $file = '' |
||
204 | * @param int $line = 0 |
||
205 | * |
||
206 | * @return string |
||
207 | */ |
||
208 | public function log_lang_error($error, $error_type = 'general', $sprintf = array(), $file = '', $line = 0) |
||
226 | |||
227 | /** |
||
228 | * An irrecoverable error. |
||
229 | * |
||
230 | * What it does: |
||
231 | * |
||
232 | * - This function stops execution and displays an error message. |
||
233 | * - It logs the error message if $log is specified. |
||
234 | * |
||
235 | * @param string $error |
||
236 | * @param string|boolean $log defaults to 'general' false will skip logging, true will use general |
||
237 | * |
||
238 | * @throws \Elk_Exception |
||
239 | */ |
||
240 | public function fatal_error($error = '', $log = 'general') |
||
244 | |||
245 | /** |
||
246 | * Shows a fatal error with a message stored in the language file. |
||
247 | * |
||
248 | * What it does: |
||
249 | * |
||
250 | * - This function stops execution and displays an error message by key. |
||
251 | * - uses the string with the error_message_key key. |
||
252 | * - logs the error in the forum's default language while displaying the error |
||
253 | * message in the user's language. |
||
254 | * - uses Errors language file and applies the $sprintf information if specified. |
||
255 | * - the information is logged if log is specified. |
||
256 | * |
||
257 | * @param string $error |
||
258 | * @param string|boolean $log defaults to 'general' false will skip logging, true will use general |
||
259 | * @param string[] $sprintf defaults to empty array() |
||
260 | * |
||
261 | * @throws \Elk_Exception |
||
262 | */ |
||
263 | public function fatal_lang_error($error, $log = 'general', $sprintf = array()) |
||
267 | |||
268 | /** |
||
269 | * It is called by Errors::fatal_error() and Errors::fatal_lang_error(). |
||
270 | * |
||
271 | * @uses Errors template, fatal_error sub template |
||
272 | * @param string $error_message |
||
273 | * @param string $error_code string or int code |
||
274 | * @throws \Elk_Exception |
||
275 | */ |
||
276 | final protected function _setup_fatal_ErrorContext($error_message, $error_code) |
||
330 | |||
331 | /** |
||
332 | * Show a message for the (full block) maintenance mode. |
||
333 | * |
||
334 | * What it does: |
||
335 | * |
||
336 | * - It shows a complete page independent of language files or themes. |
||
337 | * - It is used only if $maintenance = 2 in Settings.php. |
||
338 | * - It stops further execution of the script. |
||
339 | */ |
||
340 | public function display_maintenance_message() |
||
361 | |||
362 | /** |
||
363 | * Show an error message for the connection problems. |
||
364 | * |
||
365 | * What it does: |
||
366 | * |
||
367 | * - It shows a complete page independent of language files or themes. |
||
368 | * - It is used only if there's no way to connect to the database. |
||
369 | * - It stops further execution of the script. |
||
370 | */ |
||
371 | public function display_db_error() |
||
420 | |||
421 | /** |
||
422 | * Show an error message for load average blocking problems. |
||
423 | * |
||
424 | * What it does: |
||
425 | * |
||
426 | * - It shows a complete page independent of language files or themes. |
||
427 | * - It is used only if the load averages are too high to continue execution. |
||
428 | * - It stops further execution of the script. |
||
429 | */ |
||
430 | public function display_loadavg_error() |
||
449 | |||
450 | /** |
||
451 | * Small utility function for fatal error pages, sets the headers. |
||
452 | * |
||
453 | * - Used by display_db_error(), display_loadavg_error(), |
||
454 | * display_maintenance_message() |
||
455 | */ |
||
456 | private function _set_fatal_error_headers() |
||
468 | |||
469 | /** |
||
470 | * Retrieve the sole instance of this class. |
||
471 | * |
||
472 | * @return Errors |
||
473 | */ |
||
474 | public static function instance() |
||
490 | } |
||
491 |
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.