Complex classes like FlipPage 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 FlipPage, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 323 | class FlipPage extends WebPage |
||
| 324 | { |
||
| 325 | /** The currently logged in user or null if no user is logged in */ |
||
| 326 | public $user; |
||
| 327 | /** An array of links to put in the header */ |
||
| 328 | public $links; |
||
| 329 | /** An array of notifications to draw on the page */ |
||
| 330 | public $notifications; |
||
| 331 | /** Should we draw the header? */ |
||
| 332 | public $header; |
||
| 333 | /** The login page URL */ |
||
| 334 | public $loginUrl; |
||
| 335 | /** The logout page URL */ |
||
| 336 | public $logoutUrl; |
||
| 337 | /** Should we use minified JS/CSS? */ |
||
| 338 | protected $minified = null; |
||
| 339 | /** Should we use local JS/CSS or Content Delivery Networks? */ |
||
| 340 | protected $cdn = null; |
||
| 341 | /** Draw the analytics scripts */ |
||
| 342 | protected $analytics = true; |
||
| 343 | |||
| 344 | /** |
||
| 345 | * Create a webpage with JQuery, Bootstrap, etc |
||
| 346 | * |
||
| 347 | * @param string $title The webpage title |
||
| 348 | * @param boolean $header Draw the header bar? |
||
| 349 | * |
||
| 350 | * @SuppressWarnings("StaticAccess") |
||
| 351 | */ |
||
| 352 | function __construct($title, $header = true) |
||
|
|
|||
| 353 | { |
||
| 354 | parent::__construct($title); |
||
| 355 | $this->setupVars(); |
||
| 356 | $this->add_js(JS_JQUERY, false); |
||
| 357 | $this->add_js(JS_FLIPSIDE, false); |
||
| 358 | $this->addBootstrap(); |
||
| 359 | $this->header = $header; |
||
| 360 | $this->links = array(); |
||
| 361 | $this->notifications = array(); |
||
| 362 | $this->loginUrl = 'login.php'; |
||
| 363 | $this->logoutUrl = 'logout.php'; |
||
| 364 | if(isset(FlipsideSettings::$global)) |
||
| 365 | { |
||
| 366 | if(isset(FlipsideSettings::$global['login_url'])) |
||
| 367 | { |
||
| 368 | $this->loginUrl = FlipsideSettings::$global['login_url']; |
||
| 369 | } |
||
| 370 | if(isset(FlipsideSettings::$global['logout_url'])) |
||
| 371 | { |
||
| 372 | $this->logoutUrl = FlipsideSettings::$global['logout_url']; |
||
| 373 | } |
||
| 374 | } |
||
| 375 | $this->user = FlipSession::getUser(); |
||
| 376 | $this->addAllLinks(); |
||
| 377 | } |
||
| 378 | |||
| 379 | /** |
||
| 380 | * Get the external site links for this page |
||
| 381 | * |
||
| 382 | */ |
||
| 383 | protected function getSites() |
||
| 391 | |||
| 392 | /** |
||
| 393 | * Add the links to be used in the header |
||
| 394 | * |
||
| 395 | * @SuppressWarnings("Superglobals") |
||
| 396 | * |
||
| 397 | * @todo Consider pulling the about menu from the settings file or a DB |
||
| 398 | */ |
||
| 399 | protected function addAllLinks() |
||
| 400 | { |
||
| 401 | if($this->user === false || $this->user === null) |
||
| 402 | { |
||
| 403 | if(isset($_SERVER['REQUEST_URI']) && strstr($_SERVER['REQUEST_URI'], 'logout.php') === false) |
||
| 404 | { |
||
| 405 | $this->addLink('Login', $this->loginUrl); |
||
| 406 | } |
||
| 407 | } |
||
| 408 | else |
||
| 409 | { |
||
| 410 | $this->add_links(); |
||
| 411 | $this->addLink('Logout', $this->logoutUrl); |
||
| 412 | } |
||
| 413 | $about_menu = array( |
||
| 414 | 'Burning Flipside'=>'https://www.burningflipside.com/about/event', |
||
| 415 | 'AAR, LLC'=>'https://www.burningflipside.com/organization/aar', |
||
| 416 | 'Privacy Policy'=>'https://www.burningflipside.com/about/privacy' |
||
| 417 | ); |
||
| 418 | $this->addLink('About', 'http://www.burningflipside.com/about', $about_menu); |
||
| 419 | } |
||
| 420 | |||
| 421 | /** |
||
| 422 | * Setup minified and cdn class varibles from defaults or the settings file |
||
| 423 | */ |
||
| 424 | private function setupVars() |
||
| 425 | { |
||
| 426 | if($this->minified !== null && $this->cdn !== null) return; |
||
| 427 | $this->minified = 'min'; |
||
| 428 | $this->cdn = 'cdn'; |
||
| 429 | if(isset(FlipsideSettings::$global)) |
||
| 430 | { |
||
| 431 | if(isset(FlipsideSettings::$global['use_minified']) && !FlipsideSettings::$global['use_minified']) |
||
| 432 | { |
||
| 433 | $this->minified = 'no'; |
||
| 434 | } |
||
| 435 | if(isset(FlipsideSettings::$global['use_cdn']) && !FlipsideSettings::$global['use_cdn']) |
||
| 436 | { |
||
| 437 | $this->cdn = 'no'; |
||
| 438 | } |
||
| 439 | } |
||
| 440 | } |
||
| 441 | |||
| 442 | /** |
||
| 443 | * Add a JavaScript file from its src URI |
||
| 444 | * |
||
| 445 | * @param string $src The webpath to the JavaScript file |
||
| 446 | * @param boolean $async Can the JavaScript be loaded asynchronously? |
||
| 447 | * |
||
| 448 | * @deprecated 2.0.0 Please use addJSByURI() instead |
||
| 449 | */ |
||
| 450 | function add_js_from_src($src, $async = true) |
||
| 451 | { |
||
| 452 | $this->addJSByURI($src, $async); |
||
| 453 | } |
||
| 454 | |||
| 455 | /** |
||
| 456 | * Add a JavaScript file from its src URI |
||
| 457 | * |
||
| 458 | * @param string $uri The webpath to the JavaScript file |
||
| 459 | * @param boolean $async Can the JavaScript be loaded asynchronously? |
||
| 460 | */ |
||
| 461 | public function addJSByURI($uri, $async = true) |
||
| 462 | { |
||
| 463 | $attributes = array('src'=>$uri, 'type'=>'text/javascript'); |
||
| 464 | if($async === true) |
||
| 465 | { |
||
| 466 | $attributes['async'] = true; |
||
| 467 | } |
||
| 468 | $jsTag = $this->createOpenTag('script', $attributes); |
||
| 469 | $closeTag = $this->createCloseTag('script'); |
||
| 470 | $this->addHeadTag($jsTag); |
||
| 471 | $this->addHeadTag($closeTag); |
||
| 472 | } |
||
| 473 | |||
| 474 | /** |
||
| 475 | * Add a Cascading Style Sheet file from its src URI |
||
| 476 | * |
||
| 477 | * @param string $src The webpath to the Cascading Style Sheet file |
||
| 478 | * @param boolean $import Can the CSS be loaded asynchronously? |
||
| 479 | * |
||
| 480 | * @deprecated 2.0.0 Please use addCSSByURI() instead |
||
| 481 | */ |
||
| 482 | function add_css_from_src($src, $import = false) |
||
| 483 | { |
||
| 484 | $this->addCSSByURI($src, $import); |
||
| 485 | } |
||
| 486 | |||
| 487 | /** |
||
| 488 | * Add a Cascading Style Sheet file from its src URI |
||
| 489 | * |
||
| 490 | * @param string $src The webpath to the Cascading Style Sheet file |
||
| 491 | * @param boolean $async Can the CSS be loaded asynchronously? |
||
| 492 | */ |
||
| 493 | public function addCSSByURI($uri, $async = false) |
||
| 494 | { |
||
| 495 | $attributes = array('rel'=>'stylesheet', 'href'=>$uri, 'type'=>'text/css'); |
||
| 496 | if($async === true && $this->importSupport === true) |
||
| 497 | { |
||
| 498 | $attributes['rel'] = 'import'; |
||
| 499 | } |
||
| 500 | $cssTag = $this->createOpenTag('link', $attributes, true); |
||
| 501 | $this->addHeadTag($cssTag); |
||
| 502 | } |
||
| 503 | |||
| 504 | /** |
||
| 505 | * Add a JavaScript file from a set of files known to the framework |
||
| 506 | * |
||
| 507 | * @param string $type the ID of the JS file |
||
| 508 | * @param boolean $async Can the JS file be loaded asynchronously? |
||
| 509 | * |
||
| 510 | * @deprecated 2.0.0 Please use addWellKnownJS() instead |
||
| 511 | */ |
||
| 512 | function addJS($type, $async = true) |
||
| 513 | { |
||
| 514 | $this->addWellKnownJS($type, $async); |
||
| 515 | } |
||
| 516 | |||
| 517 | function add_js($type, $async = true) |
||
| 518 | { |
||
| 519 | $this->addWellKnownJS($type, $async); |
||
| 520 | } |
||
| 521 | |||
| 522 | /** |
||
| 523 | * Add a JavaScript file from a set of files known to the framework |
||
| 524 | * |
||
| 525 | * @param string $jsFileID the ID of the JS file |
||
| 526 | * @param boolean $async Can the JS file be loaded asynchronously? |
||
| 527 | */ |
||
| 528 | public function addWellKnownJS($jsFileID, $async = true) |
||
| 529 | { |
||
| 530 | global $jsArray; |
||
| 531 | $this->setupVars(); |
||
| 532 | $src = $jsArray[$jsFileID][$this->cdn][$this->minified]; |
||
| 533 | $this->addJSByURI($src, $async); |
||
| 534 | } |
||
| 535 | |||
| 536 | /** |
||
| 537 | * Add a CSS file from a set of files known to the framework |
||
| 538 | * |
||
| 539 | * @param string $type the ID of the CSS file |
||
| 540 | * @param boolean $import Can the CSS file be loaded asynchronously? |
||
| 541 | * |
||
| 542 | * @deprecated 2.0.0 Please use addWellKnownCSS() instead |
||
| 543 | */ |
||
| 544 | function add_css($type, $import = false) |
||
| 545 | { |
||
| 546 | $this->addWellKnownCSS($type, $import); |
||
| 547 | } |
||
| 548 | |||
| 549 | /** |
||
| 550 | * Add a CSS file from a set of files known to the framework |
||
| 551 | * |
||
| 552 | * @param string $cssFileID the ID of the CSS file |
||
| 553 | * @param boolean $async Can the CSS file be loaded asynchronously? |
||
| 554 | */ |
||
| 555 | public function addWellKnownCSS($cssFileID, $async = true) |
||
| 556 | { |
||
| 557 | global $cssArray; |
||
| 558 | $this->setupVars(); |
||
| 559 | $src = $cssArray[$cssFileID][$this->cdn][$this->minified]; |
||
| 560 | $this->addCSSByURI($src, $async); |
||
| 561 | } |
||
| 562 | |||
| 563 | /** |
||
| 564 | * Add files needed by the Bootstrap framework |
||
| 565 | */ |
||
| 566 | private function addBootstrap() |
||
| 567 | { |
||
| 568 | $this->add_js(JS_BOOTSTRAP, false); |
||
| 569 | $this->addWellKnownCSS(CSS_BOOTSTRAP); |
||
| 570 | $this->addWellKnownCSS(CSS_FONTAWESOME); |
||
| 571 | } |
||
| 572 | |||
| 573 | protected function getSiteLinksForHeader() |
||
| 584 | |||
| 585 | protected function getHrefForDropdown(&$link) |
||
| 595 | |||
| 596 | protected function getDropdown($link, $name) |
||
| 597 | { |
||
| 598 | $ret = '<li class="dropdown">'; |
||
| 599 | $href = $this->getHrefForDropdown($link); |
||
| 600 | $ret .= '<a href="'.$href.'" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">'.$name.' <span class="caret"></span></a>'; |
||
| 601 | $ret .= '<ul class="dropdown-menu">'; |
||
| 602 | $subNames = array_keys($link); |
||
| 603 | foreach($subNames as $subName) |
||
| 604 | { |
||
| 605 | $ret .= $this->getLinkByName($subName, $link); |
||
| 606 | } |
||
| 607 | $ret .= '</ul></li>'; |
||
| 608 | return $ret; |
||
| 609 | } |
||
| 610 | |||
| 611 | protected function getLinkByName($name, $links) |
||
| 623 | |||
| 624 | protected function getLinksMenus() |
||
| 634 | |||
| 635 | /** |
||
| 636 | * Draw the header for the page |
||
| 637 | */ |
||
| 638 | protected function addHeader() |
||
| 639 | { |
||
| 640 | $sites = $this->getSiteLinksForHeader(); |
||
| 641 | $links = $this->getLinksMenus(); |
||
| 642 | $header = '<nav class="navbar navbar-default navbar-fixed-top"> |
||
| 643 | <div class="container-fluid"> |
||
| 644 | <!-- Brand and toggle get grouped for better mobile display --> |
||
| 645 | <div class="navbar-header"> |
||
| 646 | <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar-collapse" aria-expanded="false"> |
||
| 647 | <span class="sr-only">Toggle navigation</span> |
||
| 648 | <span class="icon-bar"></span> |
||
| 649 | <span class="icon-bar"></span> |
||
| 650 | <span class="icon-bar"></span> |
||
| 651 | </button> |
||
| 652 | <a class="navbar-brand" href="#"> |
||
| 653 | <picture> |
||
| 654 | <source srcset="/img/common/logo.svg" style="width: 30px; height:30px"/> |
||
| 655 | <img alt="Burning Flipside" src="/img/common/logo.png" style="width: 30px; height:30px"/> |
||
| 656 | </picture> |
||
| 657 | </a> |
||
| 658 | </div> |
||
| 659 | <!-- Collect the nav links, forms, and other content for toggling --> |
||
| 660 | <div class="collapse navbar-collapse" id="navbar-collapse"> |
||
| 661 | <ul id="site_nav" class="nav navbar-nav"> |
||
| 662 | '.$sites.' |
||
| 663 | </ul> |
||
| 664 | <ul class="nav navbar-nav navbar-right"> |
||
| 665 | '.$links.' |
||
| 666 | </ul> |
||
| 667 | </div> |
||
| 668 | </div> |
||
| 669 | </nav>'; |
||
| 670 | $this->body = $header.$this->body; |
||
| 671 | $this->body_tags .= 'style="padding-top: 60px;"'; |
||
| 672 | } |
||
| 673 | |||
| 674 | /** Notification that is green for success */ |
||
| 675 | const NOTIFICATION_SUCCESS = 'alert-success'; |
||
| 676 | /** Notification that is blue for infomrational messages */ |
||
| 677 | const NOTIFICATION_INFO = 'alert-info'; |
||
| 678 | /** Notification that is yellow for warning */ |
||
| 679 | const NOTIFICATION_WARNING = 'alert-warning'; |
||
| 680 | /** Notification that is red for error */ |
||
| 681 | const NOTIFICATION_FAILED = 'alert-danger'; |
||
| 682 | |||
| 683 | /** |
||
| 684 | * Add a notification to the page |
||
| 685 | * |
||
| 686 | * @param string $msg The message to show in the notifcation |
||
| 687 | * @param string $sev The severity of the notifcation |
||
| 688 | * @param boolean $dismissible Can the user dismiss the notificaton? |
||
| 689 | * |
||
| 690 | * @deprecated 2.0.0 Use the addNotification function instead |
||
| 691 | */ |
||
| 692 | function add_notification($msg, $sev = self::NOTIFICATION_INFO, $dismissible = 1) |
||
| 693 | { |
||
| 694 | $notice = array('msg'=>$msg, 'sev'=>$sev, 'dismissible'=>$dismissible); |
||
| 695 | array_push($this->notifications, $notice); |
||
| 696 | } |
||
| 697 | |||
| 698 | /** |
||
| 699 | * Add a notification to the page |
||
| 700 | * |
||
| 701 | * @param string $message The message to show in the notifcation |
||
| 702 | * @param string $sevity The severity of the notifcation |
||
| 703 | * @param boolean $dismissible Can the user dismiss the notificaton? |
||
| 704 | */ |
||
| 705 | public function addNotification($message, $severity = self::NOTIFICATION_INFO, $dismissible = true) |
||
| 706 | { |
||
| 707 | array_push($this->notifications, array('msg'=>$message, 'sev'=>$severity, 'dismissible'=>$dismissible)); |
||
| 708 | } |
||
| 709 | |||
| 710 | /** |
||
| 711 | * Draw all notifications to the page |
||
| 712 | */ |
||
| 713 | private function renderNotifications() |
||
| 714 | { |
||
| 715 | $count = count($this->notifications); |
||
| 716 | if($count === 0) |
||
| 717 | { |
||
| 718 | return; |
||
| 719 | } |
||
| 720 | for($i = 0; $i < $count; $i++) |
||
| 721 | { |
||
| 722 | $class = 'alert '.$this->notifications[$i]['sev']; |
||
| 723 | $button = ''; |
||
| 724 | if($this->notifications[$i]['dismissible']) |
||
| 725 | { |
||
| 726 | $class .= ' alert-dismissible'; |
||
| 727 | $button = '<button type="button" class="close" data-dismiss="alert"><span aria-hidden="true">×</span><span class="sr-only">Close</span></button>'; |
||
| 728 | } |
||
| 729 | $prefix = ''; |
||
| 730 | switch($this->notifications[$i]['sev']) |
||
| 731 | { |
||
| 732 | case self::NOTIFICATION_INFO: |
||
| 733 | $prefix = '<strong>Notice:</strong> '; |
||
| 734 | break; |
||
| 735 | case self::NOTIFICATION_WARNING: |
||
| 736 | $prefix = '<strong>Warning!</strong> '; |
||
| 737 | break; |
||
| 738 | case self::NOTIFICATION_FAILED: |
||
| 739 | $prefix = '<strong>Warning!</strong> '; |
||
| 740 | break; |
||
| 741 | } |
||
| 742 | $style = ''; |
||
| 743 | if(($i + 1) < count($this->notifications)) |
||
| 744 | { |
||
| 745 | //Not the last notification, remove the end margin |
||
| 746 | $style = 'style="margin: 0px;"'; |
||
| 747 | } |
||
| 748 | $this->body = ' |
||
| 749 | <div class="'.$class.'" role="alert" '.$style.'> |
||
| 750 | '.$button.$prefix.$this->notifications[$i]['msg'].' |
||
| 751 | </div> |
||
| 752 | '.$this->body; |
||
| 753 | } |
||
| 754 | } |
||
| 755 | |||
| 756 | /** |
||
| 757 | * Add the no script block |
||
| 758 | */ |
||
| 759 | private function addNoScript() |
||
| 768 | |||
| 769 | /** |
||
| 770 | * Add the analytics script block |
||
| 771 | */ |
||
| 772 | private function addAnalyticsBlock() |
||
| 789 | |||
| 790 | /** |
||
| 791 | * Draw the page |
||
| 792 | * |
||
| 793 | * @param boolean $header Draw the header |
||
| 794 | * @param boolean $analytics Include analytics on the page |
||
| 795 | */ |
||
| 796 | public function printPage($header = true) |
||
| 797 | { |
||
| 798 | $this->renderNotifications(); |
||
| 799 | $this->addNoScript(); |
||
| 800 | $this->addAnalyticsBlock(); |
||
| 801 | if($this->header || $header) |
||
| 802 | { |
||
| 803 | $this->addHeader(); |
||
| 804 | } |
||
| 805 | parent::printPage(); |
||
| 806 | } |
||
| 807 | |||
| 808 | /** |
||
| 809 | * Add a link to the header |
||
| 810 | * |
||
| 811 | * @param string $name The name of the link |
||
| 812 | * @param false|string $url The URL to link to |
||
| 813 | * @param false|array $subment Any submenu items for the dropdown |
||
| 814 | * |
||
| 815 | * @deprecated 1.0.0 Use addLink instead |
||
| 816 | */ |
||
| 817 | function add_link($name, $url = false, $submenu = false) |
||
| 818 | { |
||
| 819 | $this->addLink($name, $url, $submenu); |
||
| 820 | } |
||
| 821 | |||
| 822 | /** |
||
| 823 | * Add a link to the header |
||
| 824 | * |
||
| 825 | * @param string $name The name of the link |
||
| 826 | * @param false|string $url The URL to link to |
||
| 827 | * @param false|array $subment Any submenu items for the dropdown |
||
| 828 | */ |
||
| 829 | public function addLink($name, $url = false, $submenu = false) |
||
| 830 | { |
||
| 831 | if(is_array($submenu)) |
||
| 832 | { |
||
| 833 | $submenu['_'] = $url; |
||
| 834 | $this->links[$name] = $submenu; |
||
| 835 | } |
||
| 836 | else |
||
| 837 | { |
||
| 838 | $this->links[$name] = $url; |
||
| 839 | } |
||
| 840 | } |
||
| 841 | |||
| 842 | /** |
||
| 843 | * Add the login form to the page |
||
| 844 | * |
||
| 845 | * @SuppressWarnings("StaticAccess") |
||
| 846 | */ |
||
| 847 | function add_login_form() |
||
| 884 | |||
| 885 | /** |
||
| 886 | * Add additional links |
||
| 887 | */ |
||
| 888 | function add_links() |
||
| 891 | } |
||
| 892 | /* vim: set tabstop=4 shiftwidth=4 expandtab: */ |
||
| 893 |
Adding explicit visibility (
private,protected, orpublic) is generally recommend to communicate to other developers how, and from where this method is intended to be used.