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:
1 | <?php |
||
19 | class LogManager implements LoggerInterface |
||
20 | { |
||
21 | /** |
||
22 | * @var \Kaylyu\Alipay\Kernel\ServiceContainer |
||
23 | */ |
||
24 | protected $app; |
||
25 | |||
26 | /** |
||
27 | * The array of resolved channels. |
||
28 | * |
||
29 | * @var array |
||
30 | */ |
||
31 | protected $channels = []; |
||
32 | |||
33 | /** |
||
34 | * The registered custom driver creators. |
||
35 | * |
||
36 | * @var array |
||
37 | */ |
||
38 | protected $customCreators = []; |
||
39 | |||
40 | /** |
||
41 | * The Log levels. |
||
42 | * |
||
43 | * @var array |
||
44 | */ |
||
45 | protected $levels = [ |
||
46 | 'debug' => Monolog::DEBUG, |
||
47 | 'info' => Monolog::INFO, |
||
48 | 'notice' => Monolog::NOTICE, |
||
49 | 'warning' => Monolog::WARNING, |
||
50 | 'error' => Monolog::ERROR, |
||
51 | 'critical' => Monolog::CRITICAL, |
||
52 | 'alert' => Monolog::ALERT, |
||
53 | 'emergency' => Monolog::EMERGENCY, |
||
54 | ]; |
||
55 | |||
56 | /** |
||
57 | * LogManager constructor. |
||
58 | * |
||
59 | * @param \Kaylyu\Alipay\Kernel\ServiceContainer |
||
60 | */ |
||
61 | public function __construct(ServiceContainer $app) |
||
65 | |||
66 | /** |
||
67 | * Create a new, on-demand aggregate logger instance. |
||
68 | * |
||
69 | * @param array $channels |
||
70 | * @param string|null $channel |
||
71 | * |
||
72 | * @return \Psr\Log\LoggerInterface |
||
73 | */ |
||
74 | public function stack(array $channels, $channel = null) |
||
78 | |||
79 | /** |
||
80 | * Get a log channel instance. |
||
81 | * |
||
82 | * @param string|null $channel |
||
83 | * |
||
84 | * @return mixed |
||
85 | */ |
||
86 | public function channel($channel = null) |
||
90 | |||
91 | /** |
||
92 | * Get a log driver instance. |
||
93 | * |
||
94 | * @param string|null $driver |
||
95 | * |
||
96 | * @return mixed |
||
97 | */ |
||
98 | public function driver($driver = null) |
||
102 | |||
103 | /** |
||
104 | * Attempt to get the log from the local cache. |
||
105 | * |
||
106 | * @param string $name |
||
107 | * |
||
108 | * @return \Psr\Log\LoggerInterface |
||
109 | */ |
||
110 | protected function get($name) |
||
124 | |||
125 | /** |
||
126 | * Resolve the given log instance by name. |
||
127 | * |
||
128 | * @param string $name |
||
129 | * |
||
130 | * @return \Psr\Log\LoggerInterface |
||
131 | * |
||
132 | * @throws \InvalidArgumentException |
||
133 | */ |
||
134 | protected function resolve($name) |
||
154 | |||
155 | /** |
||
156 | * Create an emergency log handler to avoid white screens of death. |
||
157 | * |
||
158 | * @return \Monolog\Logger |
||
159 | */ |
||
160 | protected function createEmergencyLogger() |
||
166 | |||
167 | /** |
||
168 | * Call a custom driver creator. |
||
169 | * |
||
170 | * @param array $config |
||
171 | * |
||
172 | * @return mixed |
||
173 | */ |
||
174 | protected function callCustomCreator(array $config) |
||
178 | |||
179 | /** |
||
180 | * Create an aggregate log driver instance. |
||
181 | * |
||
182 | * @param array $config |
||
183 | * |
||
184 | * @return \Monolog\Logger |
||
185 | */ |
||
186 | protected function createStackDriver(array $config) |
||
196 | |||
197 | /** |
||
198 | * Create an instance of the single file log driver. |
||
199 | * |
||
200 | * @param array $config |
||
201 | * |
||
202 | * @return \Psr\Log\LoggerInterface |
||
203 | */ |
||
204 | protected function createSingleDriver(array $config) |
||
212 | |||
213 | /** |
||
214 | * Create an instance of the daily file log driver. |
||
215 | * |
||
216 | * @param array $config |
||
217 | * |
||
218 | * @return \Psr\Log\LoggerInterface |
||
219 | */ |
||
220 | View Code Duplication | protected function createDailyDriver(array $config) |
|
228 | |||
229 | /** |
||
230 | * Create an instance of the Slack log driver. |
||
231 | * |
||
232 | * @param array $config |
||
233 | * |
||
234 | * @return \Psr\Log\LoggerInterface |
||
235 | */ |
||
236 | protected function createSlackDriver(array $config) |
||
251 | |||
252 | /** |
||
253 | * Create an instance of the syslog log driver. |
||
254 | * |
||
255 | * @param array $config |
||
256 | * |
||
257 | * @return \Psr\Log\LoggerInterface |
||
258 | */ |
||
259 | View Code Duplication | protected function createSyslogDriver(array $config) |
|
267 | |||
268 | /** |
||
269 | * Create an instance of the "error log" log driver. |
||
270 | * |
||
271 | * @param array $config |
||
272 | * |
||
273 | * @return \Psr\Log\LoggerInterface |
||
274 | */ |
||
275 | View Code Duplication | protected function createErrorlogDriver(array $config) |
|
283 | |||
284 | /** |
||
285 | * Prepare the handlers for usage by Monolog. |
||
286 | * |
||
287 | * @param array $handlers |
||
288 | * |
||
289 | * @return array |
||
290 | */ |
||
291 | protected function prepareHandlers(array $handlers) |
||
299 | |||
300 | /** |
||
301 | * Prepare the handler for usage by Monolog. |
||
302 | * |
||
303 | * @param \Monolog\Handler\HandlerInterface $handler |
||
304 | * |
||
305 | * @return \Monolog\Handler\HandlerInterface |
||
306 | */ |
||
307 | protected function prepareHandler(HandlerInterface $handler) |
||
311 | |||
312 | /** |
||
313 | * Get a Monolog formatter instance. |
||
314 | * |
||
315 | * @return \Monolog\Formatter\FormatterInterface |
||
316 | */ |
||
317 | protected function formatter() |
||
324 | |||
325 | /** |
||
326 | * Extract the log channel from the given configuration. |
||
327 | * |
||
328 | * @param array $config |
||
329 | * |
||
330 | * @return string |
||
331 | */ |
||
332 | protected function parseChannel(array $config) |
||
336 | |||
337 | /** |
||
338 | * Parse the string level into a Monolog constant. |
||
339 | * |
||
340 | * @param array $config |
||
341 | * |
||
342 | * @return int |
||
343 | * |
||
344 | * @throws \InvalidArgumentException |
||
345 | */ |
||
346 | protected function level(array $config) |
||
356 | |||
357 | /** |
||
358 | * Get the default log driver name. |
||
359 | * |
||
360 | * @return string |
||
361 | */ |
||
362 | public function getDefaultDriver() |
||
366 | |||
367 | /** |
||
368 | * Set the default log driver name. |
||
369 | * |
||
370 | * @param string $name |
||
371 | */ |
||
372 | public function setDefaultDriver($name) |
||
376 | |||
377 | /** |
||
378 | * Register a custom driver creator Closure. |
||
379 | * |
||
380 | * @param string $driver |
||
381 | * @param \Closure $callback |
||
382 | * |
||
383 | * @return $this |
||
384 | */ |
||
385 | public function extend($driver, \Closure $callback) |
||
391 | |||
392 | /** |
||
393 | * System is unusable. |
||
394 | * |
||
395 | * @param string $message |
||
396 | * @param array $context |
||
397 | * |
||
398 | * @return mixed |
||
399 | */ |
||
400 | public function emergency($message, array $context = []) |
||
404 | |||
405 | /** |
||
406 | * Action must be taken immediately. |
||
407 | * |
||
408 | * Example: Entire website down, database unavailable, etc. This should |
||
409 | * trigger the SMS alerts and wake you up. |
||
410 | * |
||
411 | * @param string $message |
||
412 | * @param array $context |
||
413 | * |
||
414 | * @return mixed |
||
415 | */ |
||
416 | public function alert($message, array $context = []) |
||
420 | |||
421 | /** |
||
422 | * Critical conditions. |
||
423 | * |
||
424 | * Example: Application component unavailable, unexpected exception. |
||
425 | * |
||
426 | * @param string $message |
||
427 | * @param array $context |
||
428 | * |
||
429 | * @return mixed |
||
430 | */ |
||
431 | public function critical($message, array $context = []) |
||
435 | |||
436 | /** |
||
437 | * Runtime errors that do not require immediate action but should typically |
||
438 | * be logged and monitored. |
||
439 | * |
||
440 | * @param string $message |
||
441 | * @param array $context |
||
442 | * |
||
443 | * @return mixed |
||
444 | */ |
||
445 | public function error($message, array $context = []) |
||
449 | |||
450 | /** |
||
451 | * Exceptional occurrences that are not errors. |
||
452 | * |
||
453 | * Example: Use of deprecated APIs, poor use of an API, undesirable things |
||
454 | * that are not necessarily wrong. |
||
455 | * |
||
456 | * @param string $message |
||
457 | * @param array $context |
||
458 | * |
||
459 | * @return mixed |
||
460 | */ |
||
461 | public function warning($message, array $context = []) |
||
465 | |||
466 | /** |
||
467 | * Normal but significant events. |
||
468 | * |
||
469 | * @param string $message |
||
470 | * @param array $context |
||
471 | * |
||
472 | * @return mixed |
||
473 | */ |
||
474 | public function notice($message, array $context = []) |
||
478 | |||
479 | /** |
||
480 | * Interesting events. |
||
481 | * |
||
482 | * Example: User logs in, SQL logs. |
||
483 | * |
||
484 | * @param string $message |
||
485 | * @param array $context |
||
486 | * |
||
487 | * @return mixed |
||
488 | */ |
||
489 | public function info($message, array $context = []) |
||
493 | |||
494 | /** |
||
495 | * Detailed debug information. |
||
496 | * |
||
497 | * @param string $message |
||
498 | * @param array $context |
||
499 | * |
||
500 | * @return mixed |
||
501 | */ |
||
502 | public function debug($message, array $context = []) |
||
506 | |||
507 | /** |
||
508 | * Logs with an arbitrary level. |
||
509 | * |
||
510 | * @param mixed $level |
||
511 | * @param string $message |
||
512 | * @param array $context |
||
513 | * |
||
514 | * @return mixed |
||
515 | */ |
||
516 | public function log($level, $message, array $context = []) |
||
520 | |||
521 | /** |
||
522 | * Dynamically call the default driver instance. |
||
523 | * |
||
524 | * @param string $method |
||
525 | * @param array $parameters |
||
526 | * |
||
527 | * @return mixed |
||
528 | */ |
||
529 | public function __call($method, $parameters) |
||
533 | } |
||
534 |
Let’s take a look at an example:
In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.
Available Fixes
Change the type-hint for the parameter:
Add an additional type-check:
Add the method to the interface: