#1
  1. Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jan 2004
    Location
    Toronto, Ontario, Canada
    Posts
    40
    Rep Power
    14

    Front Controller, Dispatcher View, View Helper Patterns in PHP 5 (w/source code e.g.)


    Introduction

    First off, I am not a professional at PHP and I have only been coding in PHP for 4 months. Any comments on this code is greatly appreciated and everyone can use this code because it's a common application pattern (but I bet some of you will want to fix some things if I have some ideas wrong here). I really truly believe that a PHP Community Process is needed to get things like this standardized so that everyone can benefit from it.

    Front Controller Pattern in PHP 5

    Keep in mind: You must add require_once for all the Dispatchers you use so that the Front Controller can properly instantiate the Dispatchers before dispatching control to them.

    Comments: You can decouple some of the code in the Front Controller even more if required. For example...

    - You can set up an Intercepting Filter that does all the preprocessing of $_GET, $_POST and $_COOKIE before being passed to the Front Controller instead of having methods such as doGet, doPost, doCookie, etc. in the Front Controller.

    - You can set up a Command Factory to process commands like "login", "logout", "view", etc. instead of performing switch/case in the Front Controller. At present, I only have three commands so I chose to keep it in the Front Controller.

    Issues: One issue I have is that if you request a view that does not exist (i.e., the dispatcher does not exist and you do not have a require_once for it), I cannot seem to figure out how to throw an exception if $objDispatcher = new $strDispatcher(); fails when trying to instantiate the object. I figured what if PHP 5 allows you to use the throws keyword with the function (i.e., function foo() throws Exception) as a default exception in case other exceptions don't catch a Fatal Error or something like that. I don't think it does and I'm not even sure if that's how it behaves in Java because I don't code in Java.

    PHP Code:
    <?php
    require_once('/var/www/htdocs/Dispatcher/HomePageDispatcher.php');
    require_once(
    '/var/www/htdocs/Dispatcher/SomePageDispatcher.php');

    class 
    Controller {
      public function 
    __construct() {
        
    // Gets the current active configuration setting of magic quotes gpc
        
    if (get_magic_quotes_gpc() == 0) {
          
    // Add slashes to incoming GET data
          
    $this->doGet();
          
    // Add slashes to incoming POST data
          
    $this->doPost();
          
    // Add slashes to incoming COOKIE data
          
    $this->doCookie();
        } else {
          
    // Strip slashes from incoming GET data
          
    $this->undoGet();
          
    // Strip slashes from incoming POST data
          
    $this->undoPost();
          
    // Strip slashes from incoming COOKIE data
          
    $this->undoCookie();
        }
        
    // Merge two or more arrays
        
    $_REQUEST array_merge($_GET$_POST$_COOKIE);
        
    // Determine whether a variable is set
        
    if (!isset($_REQUEST['action'])) {
          
    // Send a raw HTTP header
          
    header('Location: '.STR_SERVER_NAME.'/index.php?action=view&page=HomePage');
          
    // Send a raw HTTP header
          
    header('Status: 303');
          
    // Send a raw HTTP header
          
    header('Connection: close');
       } else {
          
    // Process HTTP request
          
    $this->processRequest($_REQUEST);
        }
      }
     private function 
    processRequest($arrRequest) {
        
    // The switch statement is similar to a series of IF statements on the same expression
        
    switch ($arrRequest['action']) {
         case 
    'login':
           do 
    something...
            
    // Dispatch control to appropriate view
            
    $this->dispatch($arrRequest'SomePage');
            
    // Ends execution of the current switch structure
            
    break;
         case 
    'logout':
           do 
    something...
            
    // Dispatch control to appropriate view
            
    $this->dispatch($arrRequest'SomePage');
            
    // Ends execution of the current switch structure
            
    break;
          case 
    'view':
            
    // Dispatch control to appropriate view
            
    $this->dispatch($arrRequest$arrRequest['page']);
            
    // End execution of the current switch structure
            
    break;
          default:
            
    // Dispatch control to appropriate view
            
    $this->dispatch($arrRequest'Introduction');
            break;
        }
      }
      private function 
    doGet() {
        foreach (
    $_GET as $key => $var) {
          if (!
    is_array($var)) {
            
    $_GET[$key] = addslashes($var);
          } else {
            foreach (
    $var as $arrkey => $arrvar) {
              
    $var[$arrkey] = addslashes($arrvar);
            }
            
    $_GET[$key] = $var;
          }
        }
      }
      private function 
    undoGet() {
        foreach (
    $_GET as $key => $var) {
          if (!
    is_array($var)) {
            
    $_GET[$key] = stripslashes($var);
          } else {
            foreach (
    $var as $arrkey => $arrvar) {
              
    $var[$arrkey] = stripslashes($arrvar);
            }
            
    $_GET[$key] = $var;
          }
        }
      }
      private function 
    doPost() {
        foreach (
    $_POST as $key => $var) {
          if (!
    is_array($var)) {
            
    $_POST[$key] = addslashes($var);
          } else {
            foreach (
    $var as $arrkey => $arrvar) {
              
    $var[$arrkey] = addslashes($arrvar);
            }
            
    $_POST[$key] = $var;
          }
        }
      }
      private function 
    undoPost() {
        foreach (
    $_POST as $key => $var) {
          if (!
    is_array($var)) {
            
    $_POST[$key] = stripslashes($var);
          } else {
            foreach (
    $var as $arrkey => $arrvar) {
              
    $var[$arrkey] = stripslashes($arrvar);
            }
            
    $_POST[$key] = $var;
          }
        }
      }
      private function 
    doCookie() {
        foreach (
    $_COOKIE as $key => $var) {
          if (!
    is_array($var)) {
            
    $_COOKIE[$key] = addslashes($var);
          } else {
            foreach (
    $var as $arrkey => $arrvar) {
              
    $var[$arrkey] = addslashes($arrvar);
            }
            
    $_COOKIE[$key] = $var;
          }
        }
      }
      private function 
    undoCookie() {
        foreach (
    $_COOKIE as $key => $var) {
          if (!
    is_array($var)) {
            
    $_COOKIE[$key] = stripslashes($var);
          } else {
            foreach (
    $var as $arrkey => $arrvar) {
              
    $var[$arrkey] = stripslashes($arrvar);
            }
            
    $_COOKIE[$key] = $var;
          }
        }
      }
      public function 
    getScriptInfo() {
        return 
    'Front Controller Pattern' ' Scriptlet Front Strategy';
      }
      private function 
    dispatch($arrRequest$strPage) {
        
    // Get signature of the appropriate Dispatcher class
        
    $strDispatcher $strPage.'Dispatcher';
        
    // Create object instance of the appropriate Dispatcher class
        
    $objDispatcher = new $strDispatcher();
        
    // Forward request to appropriate view
        
    $objDispatcher->forward($arrRequest);
      }
    ?>
    Dispatcher View Pattern in PHP 5

    Keep in mind: You must add require_once for all the Views you use so that the Dispatchers can instantiate the Views and forward control to them.

    Issues: I guess this does not "really" forward control to the view because it's actually accessing the View's display method. For example, you can't use something like header() in forward because you would be sending a new request to the Controller and it would fail obviously. You would be looping requests.

    Anyway, the way this works is when the request comes in like "/index.php?action=view&page=HomePage", the Controller calls the HomePageDispatcher.php and then it just accesses the HomePageView's display method. The display method in the View calls whatever necessary functions it needs to build the View and the View is responsible for retrieving content from the Helpers (i.e., Data Access Objects).

    PHP Code:
    <?php
    require_once(STR_SITE_ROOT.'/Dispatcher/ViewMapping.php');
    require_once(
    STR_SITE_ROOT.'/View/HomePageView.php');

    class 
    HomePageDispatcher implements ViewMapping {
      public function 
    forward($arrRequest) {
        
    // Create an object instance of the TestView class
        
    $objHomePageView = new HomePageView($arrRequest);
        
    // Forward the request to the appropriate view
        
    print($objHomePageView->display());
      }
    }
    ?>
    ViewMapping Code

    PHP Code:
    <?php
    interface ViewMapping {
      public function 
    forward($arrRequest);
    }
    ?>
    View Helper Pattern in PHP 5

    I didn't put any code here for the View Helper pattern but maybe if people ask for it I will. We'll see... Anyway, it's pretty obvious that you set up a HomePageView class and you have a public display() method that the Dispatcher uses to print out the view. Heck, you can use Data Access Objects with your Views and even Transfer Objects (which is actually pretty easy to implement in PHP 5 also but I have not bothered to figure out how to return collections of transfer objects so I usually return rowsets for multiple record instances and use transfer objects when I only need to do a single query to retrieve a single record).

    Closing Remarks

    I did not include any catch/throws here but you can do that obviously if you want to catch user-defined exceptions rather than getting the default PHP errors/warnings.

    Oh and by the way you can have separate Dispatchers each for separate views or have a single dynamic Dispatcher that handles all views.
  2. #2
  3. No Profile Picture
    Junior Member
    Devshed Newbie (0 - 499 posts)

    Join Date
    Feb 2002
    Location
    Buenos Aires, Argentina
    Posts
    1
    Rep Power
    0
    Issues: One issue I have is that if you request a view that does not exist (i.e., the dispatcher does not exist and you do not have a require_once for it), I cannot seem to figure out how to throw an exception if $objDispatcher = new $strDispatcher(); fails when trying to instantiate the object. I figured what if PHP 5 allows you to use the throws keyword with the function (i.e., function foo() throws Exception) as a default exception in case other exceptions don't catch a Fatal Error or something like that. I don't think it does and I'm not even sure if that's how it behaves in Java because I don't code in Java.
    Maybe you could solve this using a Dispatcher Factory that checks the existence of the file first, and if it doesn't exist, return some DEfault or Error Dispatcher.

    anyway, this is a great post and answered some questions I've had About the Dispatcher.
    Last edited by webstudio; February 9th, 2004 at 12:40 AM.
  4. #3
  5. Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jan 2004
    Location
    Toronto, Ontario, Canada
    Posts
    40
    Rep Power
    14

    Uncaught exceptions


    Originally Posted by webstudio
    Maybe you could solve this using a Dispatcher Factory that checks the existence of the file first, and if it doesn't exist, return some DEfault or Error Dispatcher.

    anyway, this is a great post and answered some questions I've had About the Dispatcher.
    I had the exact same problem. I'm not sure how this code would behave in the latest version of PHP 5. If it does the same thing, we need function-level throws. PHP 5 doesn't have that... yet.
    Brian Bisaillon <bisailb@myprivacy.ca>
    “More than those who hate you, more than all your enemies, an undisciplined mind does greater harm.”

IMN logo majestic logo threadwatch logo seochat tools logo