SitePoint Sponsor

User Tag List

Page 1 of 4 1234 LastLast
Results 1 to 25 of 100
  1. #1
    SitePoint Zealot codezilla's Avatar
    Join Date
    Nov 2002
    Location
    upstairs
    Posts
    110
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Front Controllers, Page Controllers, Intercepting Filters

    I think I've come up with a relatively simple (and hopefully elegant) way to combine a FrontController using Intercepting Filters for handling basic pre/post-processing logic common to all pages (output buffering, script timing, page caching, logging, etc.) and URL-based PageControllers (1 URL = 1 Command/Action) for page-specific logic.

    Code posted below. Hopefully this will be of use to someone out there.

    example_page.php:
    PHP Code:
    <?php

    include_once('frontcontroller.inc.php');

    // do page specific stuff (e.g. PageController), for example:
    $user =& new User($_GET['id']);

    ?>
    <html>
    <head><title>User Info</title></head>
    <body>
        <h1><?=$user->getName()?></h1>
        <p><?=$user->getProfile()?></p>
    </body>
    </html>
    (You could also leave out the include_once('frontcontroller.inc.php') part if you are using Apache and instead use the auto_prepend_file directive in a .htaccess file.)

    Yes, I am combining the Controller and the View (which is perfectly acceptable). This is on purpose. You could put the $user =& new User($_GET['id']); line (and anything else) in its own UserController class if you'd like to acheive better separation, but simplicity is one of my design goals here.

    frontcontroller.inc.php:
    PHP Code:
    $t microtime();

    require_once(
    'FilterChain.class.php');
    $fc =& new FilterChain();

    require_once(
    'OutputBufferingFilter.class.php');
    $fc->addFilter(new OutputBufferingFilter());

    require_once(
    'TimingFilter.class.php');
    $fc->addFilter(new TimingFilter($t));

    require_once(
    'PageControllerFilter.class.php');
    $fc->addFilter(new PageControllerFilter());

    $fc->process();
    exit; 
    Notice the exit; line? PageControllerFilter (see below) will include() the originally requested page. This facilitates having Filters that have pre-processing AND post-processing.

    You can put any other stuff that should be global to your application in frontcontroller.inc.php (like define()'s, ini_set()'s, etc.)

    FilterChain.class.php:
    PHP Code:
    class FilterChain
    {

        var 
    $filters;

        function 
    FilterChain()
        {
            
    $this->filters = array();
        }
        
        function 
    addFilter(&$filter)
        {
            
    $this->filters[] =& $filter;
        }
        
        function 
    next()
        {
            
    $f =& next($this->filters);
            if (
    is_a($f'InterceptingFilter'))
            {
                
    $f->run($this);
            }
        }
        
        function 
    process()
        {
            
    $f =& reset($this->filters);
            
    $f->run($this);
        }
        

    InterceptingFilter.class.php:
    PHP Code:
    class InterceptingFilter
    {

        function 
    InterceptingFilter()
        {
        }
        
        function 
    run(&$filterChain)
        {
            
    $filterChain->process();
        }
        

    OutputBufferingFilter.class.php:
    PHP Code:
    class OutputBufferingFilter extends InterceptingFilter
    {

        function 
    OutputBufferingFilter()
        {
            
    parent::InterceptingFilter();
        }
        
        function 
    run(&$fc)
        {
            
    ob_start();
            
    $fc->next();
            
    ob_end_flush();
            echo 
    '<div id="outputbufferingfilter">buffered</div>';
        }
        

    TimingFilter.class.php:
    PHP Code:
    class TimingFilter extends InterceptingFilter
    {

        var 
    $t;
        
        function 
    TimingFilter($t NULL)
        {
            
    parent::InterceptingFilter();
            
    $this->$t;
        }
        
        function 
    run(&$fc)
        {
            
    $t0 explode(' ', (is_null($this->t) ? microtime() : $this->t));
            
    $fc->next();
            
    $t1 explode(' 'microtime());
            
    $t  sprintf('%.6f', ($t1[1]-$t0[1])+($t1[0]-$t0[0]));
            echo 
    '<div id="timingfilter">'$t'</div>';
        }
        

    PageControllerFilter.class.php:
    PHP Code:
    class PageControllerFilter extends InterceptingFilter
    {

        function 
    PageControllerFilter()
        {
            
    parent::InterceptingFilter();
        }
        
        function 
    run(&$fc)
        {
            include(
    $_SERVER['PATH_TRANSLATED']);
        }
        

    PageController include()'s the originally requested page, then the post-processing for each of the filters occurs (the stuff after $fc->next() in the run() methods).

    BTW, the echo lines in the run() methods are just for testing and can be commented out or deleted.

    That's it, there's not much to it (less than there appears anyway), any feedback would be greatly appreciated.

  2. #2
    PHP manual bot bronze trophy Gaheris's Avatar
    Join Date
    Oct 2003
    Location
    Germany
    Posts
    2,195
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Nice, interesting idea and code.
    I'm pretty new in this OOP business so I like example code, thanks for sharing.

  3. #3
    SitePoint Zealot ZangBunny's Avatar
    Join Date
    Jul 2003
    Location
    Mainz, Germany
    Posts
    119
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    (Post removed to protect the innocent...)
    Last edited by ZangBunny; Oct 29, 2003 at 16:48.

  4. #4
    SitePoint Guru
    Join Date
    May 2003
    Location
    virginia
    Posts
    988
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Could it be possible, on the page that is including FrontController.class.php to have a class declaration, without being declared twice? I like this idea a lot, but my pages all use a class, with an interface that the front controller knows, and the fron controller executes the page controller class.

    Matt

  5. #5
    SitePoint Guru
    Join Date
    May 2003
    Location
    virginia
    Posts
    988
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I think i figured out how to make it possible to have a class be your command. If you want your index.php page to use one anyway:

    PHP Code:
    class PageControllerFilter extends InterceptingFilter
    {
        function 
    PageControllerFilter ()
        {
            
    parent :: InterceptingFilter ();
        } 

        function 
    run (& $fc )
        {
            if( 
    class_exists('command') )
            {
                
    $cmd =& new Command;
                
    $cmd->execute();
                return;
            }
            
            include( 
    $_SERVER ['PATH_TRANSLATED' ]);
            if( 
    class_exists('command') )
            {
                
    $cmd =& new Command;
                
    $cmd->execute();
            }
        }

    This lets you also extend a BaseCommand class if you wanted. This is exactly what I've been looking for in a FrontController/PageController! You don't have to use a prepend to execute your command class!

    Matt

  6. #6
    SitePoint Member
    Join Date
    Oct 2003
    Location
    constance, germany
    Posts
    19
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Certainly some nice piece of code. But obviously it is not satisfactory to echo that execution time in the Filter - class.

    How would one be able to rather return this value and then have it stored in a variable so that you can output it later wherever you want?

  7. #7
    SitePoint Zealot codezilla's Avatar
    Join Date
    Nov 2002
    Location
    upstairs
    Posts
    110
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    janosch,

    One possible solution requires using output buffering in the TimingFilter and placing a marker in your html. For example:

    TimingFilter.class.php:
    PHP Code:
    class TimingFilter extends InterceptingFilter
    {

        
    // . . .
        
        
    function run(&$fc)
        {
            
    $t0 explode(' ', (is_null($this->t) ? microtime() : $this->t));
            
    ob_start();
            
    $fc->next();
            
    $ob ob_get_clean();
            
    $t1 explode(' 'microtime());
            
    $t  sprintf('%.6f', ($t1[1]-$t0[1])+($t1[0]-$t0[0]));
            echo 
    str_replace('{TIME}''<div id="timingfilter">'.$t.'</div>'$ob);
        }
        


  8. #8
    SitePoint Member
    Join Date
    Oct 2003
    Location
    N/A
    Posts
    2
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    You should try the Mojavi PHP MVC framework. It has filter chaining already implemented, as well as a built-in security system and validation system.

  9. #9
    SitePoint Guru
    Join Date
    Nov 2002
    Posts
    841
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Very interesting. I like the minimalism.

    I think that the [PRE]$t = microtime()[/PRE] in the frontcontroller.inc.php file breaks the encapsulation of the timing filter. Shouldn't this go into the construction of the timing filter? (preprocessing in the constructor, post processing in the run) I realize that this would slightly reduce the reported time, but then I always thought it was unseemly for scripts to report their own execution time, anyway.

    I don't think the "include back"/exit technique is a good idea.

  10. #10
    SitePoint Zealot codezilla's Avatar
    Join Date
    Nov 2002
    Location
    upstairs
    Posts
    110
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Selkirk
    Very interesting. I like the minimalism.
    Selkirk, I have a great deal of respect for your opinions, so I'm very pleased by your comments -- I was secretly hoping you'd contribute to this thread.
    As an aside, I've got to say that I consult your WACT documentation frequently to aid in the development of my own framework.

    Quote Originally Posted by Selkirk
    I think that the $t = microtime() in the frontcontroller.inc.php file breaks the encapsulation of the timing filter.
    You're right, but I did it purposefully so I could have an more accurate estimation of execution time. However, passing an argument to the TimeFilter constructor is optional, and if no argument is passed, the initial time ($t0) is calculated during preprocessing (see the first line of the run() method).

    Quote Originally Posted by Selkirk
    Shouldn't this go into the construction of the timing filter? (preprocessing in the constructor, post processing in the run)
    I agree that it makes more sense to set the initial time in the TimingFilter constructor. However, I disagree that preprocessing should (in general) occur in the constructor. This would result in preprocessing happening for all the filters in the FilterChain, whether they are executed or not. If, for example, there were an AuthenticationFilter (high in the FilterChain) that determined the user should be redirected to a login page, it would be a waste for the PageCachingFilter (lower in the FilterChain) to perform pre-processing since it would never be executed. In other words, each filter should have the ability to cease execution of the FilterChain and commence post-processing (by not calling $fc->next()), therefore preprocessing should only occur if it needs to.

    Quote Originally Posted by Selkirk
    I realize that this would slightly reduce the reported time, but then I always thought it was unseemly for scripts to report their own execution time, anyway.
    Yeah, I agree -- it's really just intended for testing, and I thought it would be good as an example. I also have an XdebugFilter for more informative profiling (thanks to your recommendation in the WACT documentation).

    Quote Originally Posted by Selkirk
    I don't think the "include back"/exit technique is a good idea.
    Could you expand on this? I know it's not the most efficient way of doing things, but it was the best method I could come up with to get post processing to happen after normal page execution.

  11. #11
    My precious!!! astericks's Avatar
    Join Date
    Mar 2002
    Location
    Vancouver, BC
    Posts
    1,971
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    PHP Code:
    $user =& new User($_GET['id']);
    $user->getName(); 
    from this, if someone plays with the url [esp the id= part of it] they would be changing the names and info being displayed too, right?

  12. #12
    SitePoint Zealot codezilla's Avatar
    Join Date
    Nov 2002
    Location
    upstairs
    Posts
    110
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by astericks
    if someone plays with the url [esp the id= part of it] they would be changing the names and info being displayed too, right?
    That's correct. My example is a bit over-simplified, by you've got the idea I was trying to convey.

  13. #13
    My precious!!! astericks's Avatar
    Join Date
    Mar 2002
    Location
    Vancouver, BC
    Posts
    1,971
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    oh ok...lol i thought that was an integral part of your final solution. my bad.

  14. #14
    PHP manual bot bronze trophy Gaheris's Avatar
    Join Date
    Oct 2003
    Location
    Germany
    Posts
    2,195
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    but it was the best method I could come up with to get post processing to happen after normal page execution.
    What about destructors in PHP 5 and register_shutdown_function in PHP 4?

  15. #15
    SitePoint Zealot codezilla's Avatar
    Join Date
    Nov 2002
    Location
    upstairs
    Posts
    110
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Gaheris
    What about destructors in PHP 5 and register_shutdown_function in PHP 4?
    PHP4 support is one of my design requirements, so I'm not considering any PHP5-specific functionality, although I suppose using destructors may be an alternative if I could use them.

    The problem with register_shutdown_function is this:
    Quote Originally Posted by PHP Manual
    Multiple calls to register_shutdown_function() can be made, and each will be called in the same order as they were registered.
    But I need them to be called in the opposite order as they were registered because the first filter to do pre-processing should be the last to do post processing, the second should be next to last, etc. Like so: Decorator sequence diagram [microsoft.com]

    Also:
    Quote Originally Posted by PHP Manual
    The registered shutdown functions are called after the request has been completed (including sending any output buffers), so it is not possible to send output to the browser using echo() or print(), or retrieve the contents of any output buffers using ob_get_contents().
    (Emphasis added.)
    I don't like that last part either since it detracts from the power of the Intercepting Filter pattern.

  16. #16
    SitePoint Guru
    Join Date
    Nov 2002
    Posts
    841
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by codezilla
    Selkirk, I have a great deal of respect for your opinions, so I'm very pleased by your comments -- I was secretly hoping you'd contribute to this thread.
    As an aside, I've got to say that I consult your WACT documentation frequently to aid in the development of my own framework.
    Thanks. I've been meaning to get into this thread sooner or later. I'm glad someone uses the documentation. I've been working on the Front Controller / Page Controller / Intercepting Filter / Application Controller aspects of WACT. I've been slowly writing things out. I just did the first draft of Intercepting Filter.

    However, I disagree that preprocessing should (in general) occur in the constructor.
    Oops. You are right. I didn't look carefully enough at what was going on. One thing, though. I would hate to see filter classes being included and instantiated, but never called. Seems like a waste.

    I also have an XdebugFilter for more informative profiling (thanks to your recommendation in the WACT documentation).
    Thats probably Harry's recommendation. I haven't used it yet, but I am interested in it.

    Could you expand on this? I know it's not the most efficient way of doing things, but it was the best method I could come up with to get post processing to happen after normal page execution.
    I guess I can't really say much about this other than double declarations (includes) are bad, m'kay.

    I wrote about some alternate methods on the wiki.

    Here is what I've done so far on the intercepting filters in WACT. These are untested proofs of concept. I've tried to start minimally with just straight includes.

    undomagic.inc.php (request filter):
    PHP Code:
    /**
    * Acts as filter for incoming HTTP variables, eliminating magic_quotes if found
    */
    set_magic_quotes_runtime(0);

    function 
    UndoMagicSlashing(&$var) {
        if(
    is_array($var)) {
            while(list(
    $key$val) = each($var)) {
                
    UndoMagicSlashing($var[$key]);
            }
        } else {
            
    $var stripslashes($var);
        }
    }

    if(
    get_magic_quotes_gpc()) {
        
    UndoMagicSlashing($_GET);
        
    UndoMagicSlashing($_POST);
        
    UndoMagicSlashing($_COOKIES);
        
    UndoMagicSlashing($_REQUEST);

    Its hard to pass an "out of band" parameter to one of these filters...
    pathparam.inc.php:
    PHP Code:
    /**
    * Filters incoming requests moving parameters passed in the path into the standard
    * _GET superglobal.
    * This makes urls like:
    *     [url]http://domain/index.php/extra/parameter?otherparam=yes[/url]
    * equivelent to:
    *     [url]http://domain/index.php?otherparam=yes&path=extra/parameter[/url]
    * The name of the parameter to use is defined via a Config option, or the
    * PATH_PARAM_NAME define, or will default to 'path'.
    */
    function MovePathParamToGetParam() {
        if (
    $_SERVER['REQUEST_URI'] == $_SERVER['SCRIPT_NAME']) {
            return;
        }
        
    $param substr($_SERVER['REQUEST_URI'], strlen($_SERVER['SCRIPT_NAME']));
        
    $querypos strpos($param'?');
        if (
    is_integer($querypos)) {
            
    $param substr($param0$querypos);
        }
        if (
    $param !== '') {
            
    $param substr($param1);
            if (
    defined('PATH_PARAM_NAME')) {
                
    $name PATH_PARAM_NAME;
            } else {
                
    $name getConfigOption('config''request''path_param_name');
                if (empty(
    $name)) {
                    
    $name 'path';
                }
            }
            
    $_GET[$name] = $param;
        }
    }

    MovePathParamToGetParam(); 
    An finally an output filter using PHP's output buffering callbacks:

    contentlength.inc.php
    PHP Code:
    /**
    * Filters the output request to add a Content-Length header.
    * @See [url]http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.13[/url]
    * @See [url]http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.4[/url]
    */

    function AddContentLengthHeader($buffer) {

        
    $size strlen($buffer);
        
    header("Content-Length: $size"); 
        
        return 
    $buffer;
    }

    ob_start("AddContentLengthHeader"); 
    I've been trying to decide whether or not to make the jump from a simple "procedural" include style designed to work with auto_prepend_file to a class oriented style.

    Opinions?

  17. #17
    Non-Member
    Join Date
    Jan 2003
    Posts
    5,748
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Opinions?
    Still digesting Some great idea here folks... Keep them flowing

  18. #18
    SitePoint Zealot codezilla's Avatar
    Join Date
    Nov 2002
    Location
    upstairs
    Posts
    110
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Selkirk, still working through your response but I had a quick question:

    Quote Originally Posted by Selkirk
    I don't think the "include back"/exit technique is a good idea.
    Quote Originally Posted by Selkirk
    I guess I can't really say much about this other than double declarations (includes) are bad, m'kay.
    In your opinion, would it still be bad to have exit; in frontcontroller.inc.php if it were called using the auto_prepend_file mechanism instead of include()'ing it at the top of every page?

  19. #19
    SitePoint Wizard gold trophysilver trophy
    Join Date
    Nov 2000
    Location
    Switzerland
    Posts
    2,479
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    codezilla - think you may be onto the perfect solution here.

    How about some guesses at requirements for how intercepting filtes / controllers should work in PHP?

    1. Should not restrict the use of URLs / tie people to a specific URL convention (e.g. http://www.example.com/index.php/module/page)

    2. (Addition to #1) Should be possible to use Apache forcetype / re-writing tricks so URLs like http://www.example.com/index.php/module/page are possible (but not required)

    3. Should be possible to execute a page controller without the frontcontroller. In other words if you remove the;
    PHP Code:
    include_once 'frontcontroller.inc.php'
    from the page controller, the page contoller still "works". This rule should help make the front controller / intercepting filters re-usable. Also (in theory) if should be possible to take an existing project like phpBB, strip out all it already has for front contoller / incepting filter type logic while preserving the existing page controllers.

    4. Does does not depend on any Apache specific features or php.ini settings. Although auto_prepend is nice not all hosts give users the ability to change such settings (plus not everyone is using Apache so need to be careful of which $_SERVER variables are used).

    There's probably some more but just kicking off - anyone with more please add them.

    On the exit() issue, that also makes me nervous somehow but right now don't have a specific reason. My guess is it may limit flexibility in some situations but don't have a specific example.

    An alternative approach may be to wrap the example_page.php in a function which is called from PageController but if this needs to be hard coded into example_page.php, it's already violating point #3 above. Not sure...

  20. #20
    SitePoint Wizard gold trophysilver trophy
    Join Date
    Nov 2000
    Location
    Switzerland
    Posts
    2,479
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Here's another angle on the "problem" with exit().

    If example_page.php can be wrapped in a function, this function can be called from PageController - consider the following just to show what I mean;

    PHP Code:
    <?php
    function runPage() {
        function 
    foo() {
            echo ( 
    'This is function foo()<br>' );
        }
        class 
    Foo {
            function 
    bar() {
                echo ( 
    'This is Foo::bar()</br>' );
            }
        }

        
    $foo = new Foo();
        
    $foo->bar();
        
    foo();
    }
    runPage();
    ?>
    Going back to your first example;

    PHP Code:
    <?php

    include_once('frontcontroller.inc.php');

    // do page specific stuff (e.g. PageController), for example:
    $user =& new User($_GET['id']);

    ?>
    <html>
    <head><title>User Info</title></head>
    <body>
        <h1><?=$user->getName()?></h1>
        <p><?=$user->getProfile()?></p>
    </body>
    </html>
    If this can be turned into something like;

    PHP Code:
    <?php
    include_once('frontcontroller.inc.php');

    function 
    runPage () {

        
    // do page specific stuff (e.g. PageController), for example:
        
    $user =& new User($_GET['id']);

        
    ?>
        <html>
        <head><title>User Info</title></head>
        <body>
            <h1><?=$user->getName()?></h1>
            <p><?=$user->getProfile()?></p>
        </body>
        </html>
    <?php
    }
    ?>
    Then runPage() can be called from PageController.

    This violates #3 if we require people to hard code the include of frontcontroller.php and the runPage() function but what if example_page.php is automatically modified with this code?

    Taking a leaf out of WACT's Template Component Architecture, how about a process like this;

    1. Developer writes example_page.php
    2. Some process is performed which automatically update example_page.php and wraps it's contents in a function plus includes frontcontroller.php at the start.
    3. End users (site visitors) execute the modified version

    The advantage there could also be to add another function to the page controller which contains data needed by intercepting filters to complete their tasks e.g;

    PHP Code:
    function getFilterData() {
         
    $filterData = array (
               
    'action_permissions' => array (
                     
    'view'=>2047,
                     
    'create'=>8,
                     
    'edit'=>8,
                     
    'delete'=>1
                     
    ),
               
    'expires' => 3600
               
    );

         return 
    $filterData;

    If example_page.php is placed somewhere outside of the web root, for example, then "deployed" into the web root with some script which can either be manually executed by a developer or the Apache 404 trick (see here) e.g.;

    Code:
    ErrorDocument 404 /deployer.php
    Not 100% sure again. This should allow re-usability without too much difficulty but whether I'd actually enjoy developing PHP scripts in this type of "environment" I don't know.

  21. #21
    SitePoint Wizard gold trophysilver trophy
    Join Date
    Nov 2000
    Location
    Switzerland
    Posts
    2,479
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    One further thought along the lines of developing frameworks on this basis.

    What if the page controllers in their "pre-deployed" state are required to have an associated unit test of some sort? They are only "deployed" if the test passes.

    Obviously this won't be something everyone wants but would be a great option for the test infected.

  22. #22
    SitePoint Guru
    Join Date
    Nov 2002
    Posts
    841
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by codezilla
    In your opinion, would it still be bad to have exit; in frontcontroller.inc.php if it were called using the auto_prepend_file mechanism instead of include()'ing it at the top of every page?
    If you require the use of auto_prepend_file, why not just also also require the use of auto_append_file and eliminate the tricky code altogether?

  23. #23
    SitePoint Guru
    Join Date
    Nov 2002
    Posts
    841
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by HarryF
    but what if example_page.php is automatically modified with this code?

    Taking a leaf out of WACT's Template Component Architecture, how about a process like this;

    1. Developer writes example_page.php
    2. Some process is performed which automatically update example_page.php and wraps it's contents in a function plus includes frontcontroller.php at the start.
    3. End users (site visitors) execute the modified version

    The advantage there could also be to add another function to the page controller which contains data needed by intercepting filters to complete their tasks e.g;
    Much of the J2EE architecture is designed with a "Deployment phase" in mind. I think one of the advantages that PHP has over java is easy deployment. Deployment in PHP is mostly a copy to the web server. Interestingly, one of the touted benefits of struts is that it frees the java developer from the web server's deployment phase. The current WACT template implementation does not require a deployment phase because the templates are compiled automatically on demand. Could a code generation technique applied to front controllers & intercepting filters also avoid requiring a Deployment Phase? Without contradicting the "naive application logic" model that codezilla seems to be pursuing?

    BTW, I'd also like to make a distinction between a front controller, which I believe has to provide some sort of command dispatching for core logic with what I am calling a filter manager, which simply calls and sequences filters around that logic. I would call frontcontroller.inc.php in codezilla's example a filter manager, not a front controller. A front controller and filter manager could easily be implemented in the same class, however.

  24. #24
    SitePoint Zealot codezilla's Avatar
    Join Date
    Nov 2002
    Location
    upstairs
    Posts
    110
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Selkirk
    If you require the use of auto_prepend_file, why not just also also require the use of auto_append_file and eliminate the tricky code altogether?
    I don't require its use. In fact, it is one of my design requirements that I don't use auto_prepend_file, or any other Apache-specific functionality -- which is why I used include(). I only asked the question to see if you felt that it would be a bad practice. Perhaps I should rephrase the question:

    In your opinion, would it still be bad to have exit; in frontcontroller.inc.php if it were called using the auto_prepend_file mechanism instead of include()'ing it at the top of every page, disregarding the option of using auto_append_file?

    Yes, it's a bit contrived, but I'm still curious.


    Regarding auto_prepend/append_file vs. include() and your earlier statement:

    Quote Originally Posted by Selkirk
    I've been trying to decide whether or not to make the jump from a simple "procedural" include style designed to work with auto_prepend_file to a class oriented style.

    Opinions?
    I (obviously) recommend that you implement a class oriented style for WACT, especially since it is very object-oriented in nature. Of course, keeping things as simple as possible.

    Personally, my main reason for taking the class oriented InterceptingFilter route was to have the ability to easily turn on/off certain pre/post-processing logic. Also, I wanted to do post-processing without using an append file because post-processing logic is usually tied (conceptually) to pre-processing logic. In fact, you mention this on the InterceptingFilter Wiki page:

    Quote Originally Posted by WACT Wiki
    In one approach, the filters can be strung together in a chain and the chain of filters can be passed to the first filter. Then each filter may call the next filter on the chain when it is ready. This allows the filter to do both pre-processing and post-processing. it also allows the filter to couple the pre and post processing.
    I never thought much about it in these terms before, but that's exactly why I've never liked using append files -- they separate the pre/post-processing code.

    Ultimately, I came up with all the above code (from Post #1) just to have a simple page caching mechanism that could be applied to all pages, with the ability to easily add/remove different filters in the future.

    Here's the PageCachingFilter (using PEAR's Cache_Lite):
    PHP Code:
    class PageCachingFilter
    {
        function 
    PageCachingFilter()
        {
        }
        
        function 
    run(&$fc)
        {
            if (! empty(
    $_POST))
            {
                
    $fc->next();
                return;
            }

            include_once(
    'Cache/Lite.php');
            
    $cache =& new Cache_Lite(array('lifeTime' => 60));
            
    $id $_SERVER['PATH_TRANSLATED']
                  . (! empty(
    $_GET) ? '?' $_SERVER['QUERY_STRING'] : '');
            if (! 
    $output $cache->get($id))
            {
                
    ob_start();
                
    $fc->next();
                
    $output ob_get_clean();
                
    $cache->save($output$id);
            }
            echo 
    $output;
        }
        

    This could clearly be done using prepend/append files but, IMHO, this approach (though not necessarily my code) is simpler, clearer, and more encapsulated.
    Last edited by codezilla; Nov 11, 2003 at 12:19.

  25. #25
    SitePoint Zealot codezilla's Avatar
    Join Date
    Nov 2002
    Location
    upstairs
    Posts
    110
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Selkirk
    BTW, I'd also like to make a distinction between a front controller, which I believe has to provide some sort of command dispatching for core logic with what I am calling a filter manager, which simply calls and sequences filters around that logic. I would call frontcontroller.inc.php in codezilla's example a filter manager, not a front controller. A front controller and filter manager could easily be implemented in the same class, however.
    Good point. As I see it (and should have said it) my frontcontroller.inc.php also contains all the code common to the application: require()s, include()s, define()s, ini_set()s, and whatever else is needed. I personally don't like the index.php/module/page mechanism; however, I was trying to accomplish a similar objective in a different way. Anything that normally goes in your index.php (which is a front controller) could instead go in the include()'d frontcontroller.inc.php.

    The reason I still consider my example to be a front controller is because it does do dispatching, or at least what I consider (possibly incorrectly) to be dispatching. It dispatches to the requested page: include($_SERVER['PATH_TRANSLATED']). Granted, it's a very simple front controller, but a front controller nevertheless.

    Perhaps I should rename PageControllerFilter to RequestDispatchingFilter which (hopefully) does a better job of describing its purpose. RequestDispatchingFilter could then contain "command dispatching for core logic" and use include($_SERVER['PATH_TRANSLATED']) as a special (or even default) case.
    Last edited by codezilla; Nov 11, 2003 at 12:20.


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •