Complex classes like Component 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 Component, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 15 | class Component |
||
| 16 | { |
||
| 17 | const PREG_TYPES = 'jpe?g|gif|png|ico|js|css|pdf|ttf|otf|svg|eot|woff2?|swf|tar|t?gz|g?zip|csv|xls?x?|word|docx?|pptx?|psd|ogg|wav|mp3|mp4|mpeg?|mpg|mov|qt'; |
||
| 18 | public static $instance; // made public to facilitate testing |
||
| 19 | public static $not_found = array(); |
||
| 20 | public static $urls = array(); |
||
| 21 | private $cached = null; |
||
| 22 | private $db = null; |
||
| 23 | |||
| 24 | /** |
||
| 25 | * Prepares a Symfony Response for you to send. |
||
| 26 | * |
||
| 27 | * @param string $file Either a file location, or the type of file you are sending eg. html, txt, less, scss, json, xml, rdf, rss, atom, js, css |
||
| 28 | * @param array|string $options An array of options if ``$file`` is a location, or the string of data you want to send. The available options are: |
||
| 29 | * |
||
| 30 | * - '**name**' - changes a downloadable asset's file name |
||
| 31 | * - |
||
| 32 | * - |
||
| 33 | * |
||
| 34 | * @return object A Symfony\Component\HttpFoundation\Response |
||
| 35 | */ |
||
| 36 | 8 | public static function dispatch($file, $options = array()) |
|
| 110 | |||
| 111 | 6 | public static function cached($dir, array $glide = array()) |
|
| 225 | |||
| 226 | 2 | public static function urls($html) |
|
| 318 | |||
| 319 | 8 | public static function mime($type) |
|
| 320 | { |
||
| 321 | 8 | $mime = null; |
|
| 322 | 8 | $single = (is_array($type)) ? false : true; |
|
| 323 | 8 | if (is_array($type)) { |
|
| 324 | 1 | $type = array_shift($type); |
|
| 325 | 1 | } |
|
| 326 | 8 | switch (strtolower($type)) { |
|
| 327 | 8 | case 'html': |
|
| 328 | 3 | $mime = array('text/html', 'application/xhtml+xml', 'text/plain'); |
|
| 329 | 3 | break; |
|
| 330 | 8 | case 'txt': |
|
| 331 | 2 | $mime = array('text/plain'); |
|
| 332 | 2 | break; |
|
| 333 | 8 | case 'less': |
|
| 334 | 1 | $mime = array('text/x-less', 'text/css', 'text/plain', 'application/octet-stream'); |
|
| 335 | 1 | break; |
|
| 336 | 8 | case 'scss': |
|
| 337 | 1 | $mime = array('text/css', 'text/plain', 'application/octet-stream'); |
|
| 338 | 1 | break; |
|
| 339 | 8 | case 'json': |
|
| 340 | 1 | $mime = array('application/json', 'application/x-json', 'text/json', 'text/plain'); |
|
| 341 | 1 | break; |
|
| 342 | 8 | case 'xml': |
|
| 343 | 3 | $mime = array('application/xml', 'application/x-xml', 'text/xml', 'text/plain'); |
|
| 344 | 3 | break; |
|
| 345 | 6 | case 'rdf': |
|
| 346 | 1 | $mime = array('application/rdf+xml'); |
|
| 347 | 1 | break; |
|
| 348 | 6 | case 'rss': |
|
| 349 | 2 | $mime = array('application/rss+xml'); |
|
| 350 | 2 | break; |
|
| 351 | 5 | case 'atom': |
|
| 352 | 1 | $mime = array('application/atom+xml'); |
|
| 353 | 1 | break; |
|
| 354 | 5 | case 'jpeg': |
|
| 355 | 5 | case 'jpg': |
|
| 356 | 2 | $mime = array('image/jpeg', 'image/pjpeg'); |
|
| 357 | 2 | break; |
|
| 358 | 5 | case 'gif': |
|
| 359 | 1 | $mime = array('image/gif'); |
|
| 360 | 1 | break; |
|
| 361 | 5 | case 'png': |
|
| 362 | 1 | $mime = array('image/png', 'image/x-png'); |
|
| 363 | 1 | break; |
|
| 364 | 5 | case 'ico': |
|
| 365 | 1 | $mime = array('image/x-icon', 'image/vnd.microsoft.icon'); |
|
| 366 | 1 | break; |
|
| 367 | 5 | case 'js': |
|
| 368 | 2 | $mime = array('application/javascript', 'application/x-javascript', 'text/javascript', 'text/plain'); |
|
| 369 | 2 | break; |
|
| 370 | 4 | case 'css': |
|
| 371 | 2 | $mime = array('text/css', 'text/plain'); |
|
| 372 | 2 | break; |
|
| 373 | 3 | case 'pdf': |
|
| 374 | 1 | $mime = array('application/pdf', 'application/force-download', 'application/x-download', 'binary/octet-stream'); |
|
| 375 | 1 | break; |
|
| 376 | 3 | case 'ttf': |
|
| 377 | 1 | $mime = array('application/font-sfnt', 'application/font-ttf', 'application/x-font-ttf', 'font/ttf', 'font/truetype', 'application/octet-stream'); |
|
| 378 | 1 | break; |
|
| 379 | 3 | case 'otf': |
|
| 380 | 1 | $mime = array('application/font-sfnt', 'application/font-otf', 'application/x-font-otf', 'font/opentype', 'application/octet-stream'); |
|
| 381 | 1 | break; |
|
| 382 | 3 | case 'svg': |
|
| 383 | 1 | $mime = array('image/svg+xml', 'application/xml', 'text/xml'); |
|
| 384 | 1 | break; |
|
| 385 | 3 | case 'eot': |
|
| 386 | 1 | $mime = array('application/vnd.ms-fontobject', 'application/octet-stream'); |
|
| 387 | 1 | break; |
|
| 388 | 3 | case 'woff': |
|
| 389 | 1 | $mime = array('application/font-woff', 'application/x-woff', 'application/x-font-woff', 'font/x-woff', 'application/octet-stream'); |
|
| 390 | 1 | break; |
|
| 391 | 3 | case 'woff2': |
|
| 392 | 1 | $mime = array('application/font-woff2', 'font/woff2', 'application/octet-stream'); |
|
| 393 | 1 | break; |
|
| 394 | 3 | case 'swf': |
|
| 395 | 1 | $mime = array('application/x-shockwave-flash'); |
|
| 396 | 1 | break; |
|
| 397 | 3 | case 'tar': |
|
| 398 | 1 | $mime = array('application/x-tar'); |
|
| 399 | 1 | break; |
|
| 400 | 3 | case 'tgz': |
|
| 401 | 1 | $mime = array('application/x-tar', 'application/x-gzip-compressed'); |
|
| 402 | 1 | break; |
|
| 403 | 3 | case 'gz': |
|
| 404 | 3 | case 'gzip': |
|
| 405 | 1 | $mime = array('application/x-gzip'); |
|
| 406 | 1 | break; |
|
| 407 | 3 | case 'zip': |
|
| 408 | 1 | $mime = array('application/x-zip', 'application/zip', 'application/x-zip-compressed', 'application/s-compressed', 'multipart/x-zip'); |
|
| 409 | 1 | break; |
|
| 410 | 3 | case 'csv': |
|
| 411 | 3 | $mime = array('text/x-comma-separated-values', 'text/comma-separated-values', 'application/octet-stream', 'application/vnd.ms-excel', 'application/x-csv', 'text/x-csv', 'text/csv', 'application/csv', 'application/excel', 'application/vnd.msexcel', 'text/plain'); |
|
| 412 | 3 | break; |
|
| 413 | 2 | case 'xl': |
|
| 414 | 1 | $mime = array('application/excel'); |
|
| 415 | 1 | break; |
|
| 416 | 2 | case 'xls': |
|
| 417 | 1 | $mime = array('application/vnd.ms-excel', 'application/msexcel', 'application/x-msexcel', 'application/x-ms-excel', 'application/x-excel', 'application/x-dos_ms_excel', 'application/xls', 'application/x-xls', 'application/excel', 'application/download', 'application/vnd.ms-office', 'application/msword'); |
|
| 418 | 1 | break; |
|
| 419 | 2 | case 'xlsx': |
|
| 420 | 1 | $mime = array('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'application/zip', 'application/vnd.ms-excel', 'application/msword', 'application/x-zip'); |
|
| 421 | 1 | break; |
|
| 422 | 2 | case 'word': |
|
| 423 | 1 | $mime = array('application/msword', 'application/octet-stream'); |
|
| 424 | 1 | break; |
|
| 425 | 2 | case 'doc': |
|
| 426 | 1 | $mime = array('application/msword', 'application/vnd.ms-office'); |
|
| 427 | 1 | break; |
|
| 428 | 2 | case 'docx': |
|
| 429 | 1 | $mime = array('application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/zip', 'application/msword', 'application/x-zip'); |
|
| 430 | 1 | break; |
|
| 431 | 2 | case 'ppt': |
|
| 432 | 1 | $mime = array('application/powerpoint', 'application/vnd.ms-powerpoint', 'application/vnd.ms-office', 'application/msword'); |
|
| 433 | 1 | break; |
|
| 434 | 2 | case 'pptx': |
|
| 435 | 1 | $mime = array('application/vnd.openxmlformats-officedocument.presentationml.presentation', 'application/x-zip', 'application/zip'); |
|
| 436 | 1 | break; |
|
| 437 | 2 | case 'psd': |
|
| 438 | 1 | $mime = array('application/x-photoshop', 'image/vnd.adobe.photoshop'); |
|
| 439 | 1 | break; |
|
| 440 | 2 | case 'ogg': |
|
| 441 | 1 | $mime = array('audio/ogg'); |
|
| 442 | 1 | break; |
|
| 443 | 2 | case 'wav': |
|
| 444 | 1 | $mime = array('audio/x-wav', 'audio/wave', 'audio/wav'); |
|
| 445 | 1 | break; |
|
| 446 | 2 | case 'mp3': |
|
| 447 | 1 | $mime = array('audio/mpeg', 'audio/mpg', 'audio/mpeg3', 'audio/mp3'); |
|
| 448 | 1 | break; |
|
| 449 | 2 | case 'mp4': |
|
| 450 | 1 | $mime = array('video/mp4'); |
|
| 451 | 1 | break; |
|
| 452 | 2 | case 'mpe': |
|
| 453 | 2 | case 'mpeg': |
|
| 454 | 2 | case 'mpg': |
|
| 455 | 1 | $mime = array('video/mpeg'); |
|
| 456 | 1 | break; |
|
| 457 | 2 | case 'mov': |
|
| 458 | 2 | case 'qt': |
|
| 459 | 1 | $mime = array('video/quicktime'); |
|
| 460 | 1 | break; |
|
| 461 | 8 | } |
|
| 462 | |||
| 463 | 8 | return ($mime && $single) ? array_shift($mime) : $mime; |
|
| 464 | } |
||
| 465 | |||
| 466 | 1 | private function css($file, $row) |
|
| 511 | |||
| 512 | 2 | private function paths(&$cache) |
|
| 513 | { |
||
| 514 | 2 | $page = Page::html(); |
|
| 515 | 2 | $count = 0; |
|
| 516 | 2 | foreach ($cache as $dir => $files) { |
|
| 517 | 2 | $count += count($files); |
|
| 518 | 2 | } |
|
| 519 | 2 | $ids = $this->ids($count); |
|
| 520 | 2 | $insert = array(); |
|
| 521 | 2 | $update = array(); |
|
| 522 | 2 | $stmt = $this->db->prepare(array( |
|
| 523 | 2 | 'SELECT f.id AS file_id, f.updated, p.tiny, p.id AS path_id', |
|
| 524 | 2 | 'FROM files AS f INNER JOIN paths AS p ON f.id = p.file_id', |
|
| 525 | 2 | 'WHERE f.file = ? AND f.query = ?', |
|
| 526 | 2 | ), 'assoc'); |
|
| 527 | 2 | foreach ($cache as $dir => $files) { |
|
| 528 | 2 | foreach ($files as $path => $tiny) { |
|
| 529 | 2 | list($file, $query) = explode('?', $path.'?'); |
|
| 530 | 2 | $file = $page->dir[$dir].$file; |
|
| 531 | 2 | $updated = filemtime($file); |
|
| 532 | 2 | $this->db->execute($stmt, array($file, $query)); |
|
| 533 | 2 | if ($row = $this->db->fetch($stmt)) { |
|
| 534 | 2 | if ($row['updated'] == $updated) { |
|
| 535 | 2 | $tiny = $row['tiny']; |
|
| 536 | 2 | } else { |
|
| 537 | 1 | list($path_id, $tiny) = each($ids); |
|
| 538 | 1 | $update[$row['file_id']] = array($path_id, $updated); |
|
| 539 | } |
||
| 540 | 2 | } else { |
|
| 541 | 2 | list($path_id, $tiny) = each($ids); |
|
| 542 | 2 | $insert[] = array($path_id, $file, $query, $updated); |
|
| 543 | } |
||
| 544 | 2 | $cache[$dir][$path] = $tiny; |
|
| 545 | 2 | } |
|
| 546 | 2 | } |
|
| 547 | 2 | $this->db->close($stmt); |
|
| 548 | 2 | if (empty($insert) && empty($update)) { |
|
| 549 | 1 | return; |
|
| 550 | } |
||
| 551 | 2 | $this->db->exec('BEGIN IMMEDIATE'); |
|
| 552 | 2 | $paths = array(); |
|
| 553 | 2 | if (!empty($insert)) { |
|
| 554 | 2 | $stmt = $this->db->insert('files', array('path_id', 'file', 'query', 'updated')); |
|
| 555 | 2 | foreach ($insert as $array) { |
|
| 556 | 2 | $paths[$array[0]] = $this->db->insert($stmt, $array); |
|
| 557 | 2 | } |
|
| 558 | 2 | $this->db->close($stmt); |
|
| 559 | 2 | } |
|
| 560 | 2 | if (!empty($update)) { |
|
| 561 | 1 | $stmt = $this->db->update('files', 'id', array('path_id', 'updated')); |
|
| 562 | 1 | foreach ($update as $file_id => $array) { |
|
| 563 | 1 | $this->db->update($stmt, $file_id, $array); |
|
| 564 | 1 | $paths[$array[0]] = $file_id; |
|
| 565 | 1 | } |
|
| 566 | 1 | $this->db->close($stmt); |
|
| 567 | 1 | } |
|
| 568 | 2 | if (!empty($paths)) { |
|
| 569 | 2 | $stmt = $this->db->update('paths', 'id', array('file_id')); |
|
| 570 | 2 | foreach ($paths as $path_id => $file_id) { |
|
| 571 | 2 | $this->db->update($stmt, $path_id, array($file_id)); |
|
| 572 | 2 | } |
|
| 573 | 2 | $this->db->close($stmt); |
|
| 574 | 2 | } |
|
| 575 | 2 | $this->db->exec('COMMIT'); |
|
| 576 | |||
| 577 | 2 | return; |
|
| 578 | } |
||
| 579 | |||
| 580 | 2 | private function ids($count) |
|
| 581 | { |
||
| 582 | 2 | $ids = array(); |
|
| 583 | 2 | if ($stmt = $this->db->query(array( |
|
| 584 | 2 | 'SELECT id, tiny', |
|
| 585 | 2 | 'FROM paths', |
|
| 586 | 2 | 'WHERE file_id = ?', |
|
| 587 | 2 | 'ORDER BY id DESC LIMIT '.$count, |
|
| 588 | 2 | ), 0, 'row')) { |
|
| 589 | 2 | while (list($id, $tiny) = $this->db->fetch($stmt)) { |
|
| 590 | 2 | $ids[$id] = $tiny; |
|
| 591 | 2 | } |
|
| 592 | 2 | $this->db->close($stmt); |
|
| 593 | 2 | } |
|
| 594 | 2 | if (count($ids) == $count) { |
|
| 595 | 2 | return $ids; |
|
| 596 | } |
||
| 597 | 1 | $this->db->exec('BEGIN IMMEDIATE'); |
|
| 598 | 1 | $stmt = $this->db->insert('OR IGNORE INTO paths', array('tiny')); |
|
| 599 | 1 | $string = '123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; |
|
| 600 | 1 | for ($i = 0; $i < $count + 100; ++$i) { |
|
| 601 | 1 | $tiny_id = ''; // 60 (characters) ^ 5 (length) gives 777,600,000 possible combinations |
|
| 602 | 1 | while (strlen($tiny_id) < 5) { |
|
| 603 | 1 | $tiny_id .= $string[mt_rand(0, 60)]; |
|
| 604 | 1 | } |
|
| 605 | 1 | $this->db->insert($stmt, array($tiny_id)); |
|
| 606 | 1 | } |
|
| 607 | 1 | $this->db->close($stmt); |
|
| 608 | 1 | $this->db->exec('COMMIT'); |
|
| 609 | |||
| 610 | 1 | return $this->ids($count); |
|
| 611 | } |
||
| 612 | |||
| 613 | 6 | private function openDatabase() |
|
| 633 | |||
| 634 | 6 | private function closeDatabase() |
|
| 641 | |||
| 642 | 6 | private function __construct() |
|
| 645 | } |
||
| 646 |
If you define a variable conditionally, it can happen that it is not defined for all execution paths.
Let’s take a look at an example:
In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.
Available Fixes
Check for existence of the variable explicitly:
Define a default value for the variable:
Add a value for the missing path: