SitePoint Sponsor

User Tag List

Page 1 of 2 12 LastLast
Results 1 to 25 of 30
  1. #1
    I solve practical problems. bronze trophy
    Michael Morris's Avatar
    Join Date
    Jan 2008
    Location
    Knoxville TN
    Posts
    2,026
    Mentioned
    64 Post(s)
    Tagged
    0 Thread(s)

    [Advanced] Ideas for a universal PHP front controller class in the vein of PDO.

    Something I think that PHP badly needs in the next release or so (or at least is approaching very high on the want list) is a default centralized front controller. The existence of such a controller, or router, comes into play the moment you turn mod_rewrite rules on. Whether it's the one found in Zend, Cake, Symphony, or what have you, they all do the same thing. I think a lot of programs could be sped up through the adoption of a common front controller that lives in C++ as part of the PHP engine rather than being a PHP script.

    At a glance there would appear to be some overlap with the auto_prepend file directive. However, the major difference is that the script that is 'touched' by the webserver may never get parsed at all depending on what you decide to have the your logic for the front controller do - a good example would be any page in the admin directory when authentication is failed - you'll want to redirect to login.

    From a PHP standpoint this class starts as a per_directory ini directive. At the most basic it would look like this.

    Code:
    php_value front_control my/path/to/front/control.ini
    By default this value in the htacess file loads an ini for a front controller for the directory, and passes all requests for PHP files and all requests for UNKNOWN files pass to the front controller. The front controller's ini will look something like this

    Code:
    [core]
    path: my/path/to/framework 
    router: RoutingClass ; This must extend from PUL\Route
    autoloader: my/path/to/autoloader
    The core path is where the PHP files are going to live. If not defined, it is set as the webroot. This allows a framework to place its php files outside of htdocs.
    The core router is simply a class you wrote yourself that extends the core - sorta like extending the core of php
    The core autoloader should be obvious. If the autoloader[] syntax is used multiple autoloaders and their order of precedence can be set. If no autoloader is defined an internal autoloader will fire that follows the rules of PSR0 (so if your project uses PSR0 then you can have your autoloader in C)

    At the heart of this is PUL - PHP Universal Loader. This class behaves in one VERY different way than any other class you've written PHP for - it never really dies. Objects and data attached to it persist from page request to page request, though session data for individual users will change. This allows you to create a framework with a persistent heart to cut down on load times. Or at least, that is my conceptual idea - it may be a bad one (and that's what this thread is for - for experts to point out what is good and bad about all of this).

    The psuedocode map of the class is this

    Code php:
    <?php
    /*
     * PHP Universal Loader
     */
    class PUL{
      /*
       * First off, a copy of the super globals that is read only.
       */ 
      private $post
      private $get
      private $cookie
      private $files
      private $server
      private $files
     
      /*
       * This class starts session automatically, and the session hive is attached.
       * By default this is read only, but your extending classes can modify how
       * they work with the session
       */
      protected $session
     
      /*
       * The HTTP Headers - this information currently lives in $_SERVER with
       * an HTTP prefix on the value.  This is misleading because unlike the
       * other server values these are user provided and therefore forgable.
       * For example, $_SERVER['HTTP_REFERRER'] is frequently forged.
       */
      private $headers
     
      /*
       * The config ini, after parsing, ends up here as read only by the child classes.
       */
      private $config
     
      /*
       * Like PDO_Statement, the constructor of PUL is protected - it can only
       * be invoked by the PHP Engine itself which does so the first time a
       * page is requested.
       *
       * This is where you'd put your startup stuff that you don't want running
       * on every page load.  The core construct function starts the autoloaders
       * and parses the configuration INI which can be set in either the htaccess
       * or the PHP ini file.
       */
      protected __construct( $config ) {
     
      }
     
      /*
       * If the webserver encountered a PHP file, it hands it to this class and
       * it gets resolved here, in the context of this function, and within an
       * output buffer.
       */
      protected resolve( $phpfile = null ) {
        ob_start();
        if (!is_null($phpfile)) {
          require($phpfile);
        }
        return ob_end_clean();
      }
     
      /*
       * By overriding main you can setup your own routing system.
       */
      protected main( $phpfile = null ) {
        $this->output = $this->resolve($phpfile);
      }
     
      /*
       * Sends a cleartext or compressed response as determined
       * by server settings.
       */
      protected sendResponse() {}
     
      /*
       * Actions to clean the class and prepare to handle the next request
       * go here.
       */
      protected cleanup() {}
    }

    The major change that PUL introduces is that you don't have to have php docs in your htdocs directory at all, but instead use it as a cache if you like. When you write PHP files in there you can bring up a limited version of the framework to handle them, or the full framework if you like. The php files you do set in the htdocs directory are effectively wrapped by PUL and execute in it's scope. This would allow something like this...

    Code php:
    <?php
    class MyCore extends PUL {
      /*
       * We'll attach the database object here.
       */
      protected $db; 
     
      /*
       * We'll start the database now.
       */
      protected __construct($config) {
        parent::__construct($config);
        $this->db = new MyDatabase( $this->config['database'] );  
      }
    }

    Then, our index.php file over in htdocs can get right off to the races...

    Code php:
    <!doctype html>
    <html>
      <title><?= $this->db->queryFirst("SELECT title FROM pages WHERE page = 1"); ?></title>

    Now, keep in mind the above is meant to be illustrative of what this would allow. Like many powerful things, there's good and bad ways to use it, and I'll admit that quick example isn't the best example

    Giving a good example would involve pouring through a lot of files, the scope of which is largely outside what I'd like to discuss here - a universal load and setup system within the PHP Standard Library, which would be as flexible and powerful as PDO is for database accessing, but no more mandatory.

    Thoughts?

  2. #2
    SitePoint Member
    Join Date
    Jan 2013
    Posts
    4
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    ... - it never really dies. Objects and data attached to it persist from page request to page request, though session data for individual users will change.
    Sounds like you want php to behave like a Java Servlet. What can I say - switch to Java. I don't think php will have this feature.

  3. #3
    I solve practical problems. bronze trophy
    Michael Morris's Avatar
    Join Date
    Jan 2008
    Location
    Knoxville TN
    Posts
    2,026
    Mentioned
    64 Post(s)
    Tagged
    0 Thread(s)
    Definitely not helpful. You created an account specifically to make that post?

  4. #4
    SitePoint Member
    Join Date
    Jan 2013
    Posts
    4
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Michael Morris View Post
    Definitely not helpful. You created an account specifically to make that post?
    Maybe. Maybe I'll make more posts some day.

    But your idea sounds interesting. How do you see using this object? When will it load into memory - on the server restart, on first time it's used?
    How do you plan on making modifications to your custom sub-class of this class? You will need to restart the server after you change something in your class?

  5. #5
    I solve practical problems. bronze trophy
    Michael Morris's Avatar
    Join Date
    Jan 2008
    Location
    Knoxville TN
    Posts
    2,026
    Mentioned
    64 Post(s)
    Tagged
    0 Thread(s)
    All of those questions I'm undecided on. For the moment I just noted that the task of locating the local controller doesn't vary much from my own framework, or Zend, or Symphony or Cake. There is a common point task here that would be better served at the engine level, a task created by the use of mod_rewrite at the webserver level. The moment such a rewrite is introduced the framework has to begin mitigating requests PHP wasn't orginally tasked for.

    While MVC architecture is applied to many PHP projects, PHP itself doesn't have an MVC friendly mode. It is still largely a procedural language that happens to have support for objects. This loader class' main job is to provide the engine and the coder with an object mode without dictating the exact structure that the rest of the framework will take.

  6. #6
    SitePoint Member
    Join Date
    Jan 2013
    Posts
    4
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    What's the point of having $pot, $get, $cookie member variable in PUL object if it's shared between requests? I mean $cookie for which request? $post for each request?
    In order for this sort of thing to even work your PUL object has to create a new thread for each request - basically a copy of the object will have to serve a request, each one will have unique $post, $get, $cookie objects.
    Also even with creating new thread per request multiple requests will end up with access to the same member objects, which right away will introduce concurrency problems because php does not have thread-safe versions of its classes or arrays.

    And how will your PUL object behave in case the member object like $db in your example throws an exception or has fatal error? Your whole PUL object may crash, in that would be equal to a server crash because no more requests could be served.
    At least with the way php currently works a crashed php file will not bring down the whole server, but with your PUL object it may.

  7. #7
    SitePoint Guru bronze trophy TomB's Avatar
    Join Date
    Oct 2005
    Location
    Milton Keynes, UK
    Posts
    989
    Mentioned
    9 Post(s)
    Tagged
    2 Thread(s)
    Interesting concept but how are you planning to make it "never really die"? If that is the case, the superglobals shouldn't be attached to it because you want those (with the exception of cookies) to expire at the end of each request.

    I wouldn't force people to make their router extend from a base class either, that severly limits flexibility. Especially if your goal is to be unobtrusive.

    For me, it depends how you're handling persistence. For each user who's on the site, will you have a set of all the framework-level resources (e.g. database connection) loaded in memory and constantly running? Will each of the users use a different instance of this resource or the same one? How will you handle state changes within that object if everyone is using the same one? (consider pdo::lastInsertId as an example)

  8. #8
    I solve practical problems. bronze trophy
    Michael Morris's Avatar
    Join Date
    Jan 2008
    Location
    Knoxville TN
    Posts
    2,026
    Mentioned
    64 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by MeagerSir View Post
    What's the point of having $pot, $get, $cookie member variable in PUL object if it's shared between requests?
    It wouldn't be. I've misspoke while jotting this down I think.

    By 'never really die.' I want the object to basically remember it's state once construct is done. When the PHP engine gets a new request, it clones the object from that ready state instead of building a new one, and sends the clone off on its way to serve that one request. Each clone normally closes with its connection, but if a persistent connection (websockets) is requested, it would stay around. Another possibility is the clone sits around for the life of the session. If a new request against that session ID is made that clone is woke up instead of creating a new one. In this event it needs to do some connection close cleanup, and perhaps some final session close actions.

    For end programmer simplicity I'd like this layer of complication to be hidden as much as possible without creating new headaches I'm also trying to come up with a means to more effectively cache the universal loads and startup sequences all frameworks go through.

  9. #9
    SitePoint Guru bronze trophy TomB's Avatar
    Join Date
    Oct 2005
    Location
    Milton Keynes, UK
    Posts
    989
    Mentioned
    9 Post(s)
    Tagged
    2 Thread(s)
    Quote Originally Posted by Michael Morris View Post
    It wouldn't be. I've misspoke while jotting this down I think.

    By 'never really die.' I want the object to basically remember it's state once construct is done. When the PHP engine gets a new request, it clones the object from that ready state instead of building a new one, and sends the clone off on its way to serve that one request. Each clone normally closes with its connection, but if a persistent connection (websockets) is requested, it would stay around. Another possibility is the clone sits around for the life of the session. If a new request against that session ID is made that clone is woke up instead of creating a new one. In this event it needs to do some connection close cleanup, and perhaps some final session close actions.

    For end programmer simplicity I'd like this layer of complication to be hidden as much as possible without creating new headaches I'm also trying to come up with a means to more effectively cache the universal loads and startup sequences all frameworks go through.
    I think you've highlighted the real issue here: Most framework entry point routines are ridiculously weighty. Simplify those and you don't need to cache them. All you really need to do is:

    -Initialise router
    -Find the route
    -Create object graph for the module in use
    -Dispatch controller

    And only the first of those is identical each time and would be a candidate for caching. Everything else should be created on the fly as it's required. Really, the answer is for frameworks to have minimalist bootstraps.

    Back on topic, the problem with the clone method is that it's likely slower than the traditional method because you need to read both the class definition from the file and the state from the cache. In some cases, this may be beneficial because it will allow you to retain state. The problem is, how are you mapping state to users? And what kind of state are you planning to store? Everything?

    Let's say you have a database query result. Someone searched the database and was presented with a list of products. Now, it may be useful to keep that list in memory, the user might want to sort them by price, relevance or name.. so the user clicks "sort". Presumably your front controller transparently fetches this list from the cache, mutates the data and returns the sorted data to the user. Without PUL, my controller code simply calls, $this->model->setSort(PRODUCT::SORT_NAME); the view then calls $model->getProducts(); and the list is returned to the user. Whether those records come from the database or the cache, I can't quite see how maintaining a state is beneficial, because the state should be encapsulated within the model.

  10. #10
    I solve practical problems. bronze trophy
    Michael Morris's Avatar
    Join Date
    Jan 2008
    Location
    Knoxville TN
    Posts
    2,026
    Mentioned
    64 Post(s)
    Tagged
    0 Thread(s)
    Agreed on all points above. Still, I think a generic, "init, find route, create object, dispatch" would be well worth migrating to the engine, if only to encourage a common baseline for the process as PDO provides a common baseline for database interactivity.

  11. #11
    SitePoint Wizard bronze trophy Jeff Mott's Avatar
    Join Date
    Jul 2009
    Posts
    1,268
    Mentioned
    18 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Michael Morris View Post
    Still, I think a generic, "init, find route, create object, dispatch" would be well worth migrating to the engine, if only to encourage a common baseline for the process as PDO provides a common baseline for database interactivity.
    It's an ambitious goal, but I think the generic process you describe may be an oversimplification. Often times, before you can find a route, the framework will first need to load modules/plugins/bundles, and every framework does that differently. Routes are often defined in config files. What format and what parameters should those config files have? Where should they live? After the config files are parsed, should the result be cached? How? Where? Should there be a security authentication and authorization step before dispatching the controller? That opens a whole other bag of worms.

    I think there may be too many framework-specific details to nail down a generic process.

    Though, for what it's worth, Symfony's HttpKernal component sounds reasonably close to what you're describing.
    "First make it work. Then make it better."

  12. #12
    SitePoint Wizard bronze trophy Jeff Mott's Avatar
    Join Date
    Jul 2009
    Posts
    1,268
    Mentioned
    18 Post(s)
    Tagged
    0 Thread(s)
    Info on Symfony's HttpKernel for anyone interested.

    http://symfony.com/doc/current/compo...roduction.html
    "First make it work. Then make it better."

  13. #13
    Resident OCD goofball! bronze trophy Serenarules's Avatar
    Join Date
    Dec 2002
    Posts
    1,911
    Mentioned
    26 Post(s)
    Tagged
    0 Thread(s)
    I'm not sure I see any benefit in this. In my opinion, your web server (IIS or Apache) IS your front controller. Unless you plan on using something like .NET's Application scope (which can manage the lifetime of an object and reuse it over the course of an entire application, all sessions, and all requests (which is something PHP doesn't do), then this idea doesn't really add anything. Unless I've missed something.

  14. #14
    I solve practical problems. bronze trophy
    Michael Morris's Avatar
    Join Date
    Jan 2008
    Location
    Knoxville TN
    Posts
    2,026
    Mentioned
    64 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Serenarules View Post
    I'm not sure I see any benefit in this. In my opinion, your web server (IIS or Apache) IS your front controller. Unless you plan on using something like .NET's Application scope (which can manage the lifetime of an object and reuse it over the course of an entire application, all sessions, and all requests (which is something PHP doesn't do), then this idea doesn't really add anything. Unless I've missed something.
    mod_rewrite

  15. #15
    Resident OCD goofball! bronze trophy Serenarules's Avatar
    Join Date
    Dec 2002
    Posts
    1,911
    Mentioned
    26 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Michael Morris View Post
    mod_rewrite
    Obviously. I consider that part of apache though. What I meant was, I don't see any need for a PHP based script on top of that, which does the same thing. Apache + mod_rewrite and htaccess is already a fully functioning front controller.

  16. #16
    SitePoint Guru bronze trophy TomB's Avatar
    Join Date
    Oct 2005
    Location
    Milton Keynes, UK
    Posts
    989
    Mentioned
    9 Post(s)
    Tagged
    2 Thread(s)
    Michael, I think there's some merit in this idea but you need a much clearer, more focussed mission statement.

    Essentially what you're trying to achive is similar to what apache does (maps a URI to a local file). but you want to ignore the concept of "files" entirely and go straight for URI => Class->method();

    This makes sense, consider the current chain:

    Apache recieves URI
    mod_rewrite it to a central entry point
    The PHP script analyses the URI for routing (for the third time! apache has alread done this once, and mod_rewirte has done it again)

    You want to eliminate the redundancy and simply give PHP the URI to begin with. You could skip apache entirely and use PHPs inbuilt web server to do this. You would, however, lose all the power apache offers and have to add code to handle serving of non-php files. I'm not sure you'll gain much by offering an apache+php solution because all of the redundancy has happened by the time the first PHP code is even executed.

  17. #17
    I solve practical problems. bronze trophy
    Michael Morris's Avatar
    Join Date
    Jan 2008
    Location
    Knoxville TN
    Posts
    2,026
    Mentioned
    64 Post(s)
    Tagged
    0 Thread(s)
    My terse post is an allusion to this - mod_rewrite pretty much bypasses Apache's role as a front controller. With it in place PHP must deal with situations like 404 that, prior to mod_rewrite's deployment, it wasn't expected to handle. What I'm suggesting is a general structure to use in this environment - not a replacement to Apache outright.

  18. #18
    Hosting Team Leader silver trophybronze trophy
    cpradio's Avatar
    Join Date
    Jun 2002
    Location
    Ohio
    Posts
    5,129
    Mentioned
    152 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Michael Morris View Post
    My terse post is an allusion to this - mod_rewrite pretty much bypasses Apache's role as a front controller. With it in place PHP must deal with situations like 404 that, prior to mod_rewrite's deployment, it wasn't expected to handle. What I'm suggesting is a general structure to use in this environment - not a replacement to Apache outright.
    Hmm... I've been reading/following this unsure if I full grasped the concept you were trying to unload or not (still out on that one), but I think the statement "PHP must deal with situations like 404" is invalid.

    The only reason PHP must deal with a 404 is because you failed to use a proper expression in your mod_rewrite and sent too much to PHP to begin with. I believe @dklynn ; would agree with me on that. You should really only be forwarding what you intend PHP to handle, by passing it more than you expect it to handle, you have set yourself up for that situation, it is by no means the fault of anything else.

  19. #19
    SitePoint Guru bronze trophy TomB's Avatar
    Join Date
    Oct 2005
    Location
    Milton Keynes, UK
    Posts
    989
    Mentioned
    9 Post(s)
    Tagged
    2 Thread(s)
    I'm not sure I agree with that statement. As a trivial example let's say I have a simple site that pulls some HTML into a generic layout. I use /foo.html and it displays the contents of foo.html within an overall layout. I don't want mod_rewrite to be concerned with whether or not foo.html exists because it *may?* in the future. It's up to the PHP script to decide how to handle files which don't exist in this case. Otherwise you create extra work for yourself because you have to make two changes every time you add a page. Having to do this creates problems because it can get out of sync. KISS and all that!

  20. #20
    SitePoint Wizard bronze trophy Jeff Mott's Avatar
    Join Date
    Jul 2009
    Posts
    1,268
    Mentioned
    18 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by cpradio View Post
    The only reason PHP must deal with a 404 is because you failed to use a proper expression in your mod_rewrite and sent too much to PHP to begin with. ... You should really only be forwarding what you intend PHP to handle, by passing it more than you expect it to handle, you have set yourself up for that situation, it is by no means the fault of anything else.
    Even if you were very careful to mod_rewrite only URLs that PHP is expected to handle, the PHP would still have to deal with 404s, because some URLs such as /blog/{slug} would require a database lookup before we could know whether it's a 404. And if our PHP is already required to handle 404s, then we don't gain anything by mod_rewriting only URLs that PHP is expected to handle.
    "First make it work. Then make it better."

  21. #21
    Hosting Team Leader silver trophybronze trophy
    cpradio's Avatar
    Join Date
    Jun 2002
    Location
    Ohio
    Posts
    5,129
    Mentioned
    152 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Jeff Mott View Post
    Even if you were very careful to mod_rewrite only URLs that PHP is expected to handle, the PHP would still have to deal with 404s, because some URLs such as /blog/{slug} would require a database lookup before we could know whether it's a 404. And if our PHP is already required to handle 404s, then we don't gain anything by mod_rewriting only URLs that PHP is expected to handle.
    If you ask me, that isn't a 404. That is validating your input. A 404 was never intended to be used from a validation standpoint, it was to be used to state, this file does not exist. When you redirect all requests to a specific page, the only way you will get a 404 is if your routing page is missing. Otherwise, you are just validating requests.

    To make matters worse, you are then usually allowing these pseudo 404s to exist because you are not always sending back a 404 status, so search engines could ultimately pick up on the bad data too (if the bad data is a link on a page somewhere). But I digress, I understand where you are coming from, but I wouldn't call it a 404 from your viewpoint.

  22. #22
    SitePoint Wizard bronze trophy Jeff Mott's Avatar
    Join Date
    Jul 2009
    Posts
    1,268
    Mentioned
    18 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by cpradio View Post
    A 404 was never intended to be used from a validation standpoint, it was to be used to state, this file does not exist.
    A URL doesn't necessarily have a 1-to-1 correspondence with files. A 404 doesn't mean that some file doesn't exist. It means that the resource identified by the URL doesn't exist. And if the resource requested is /blog/doesnt-exist, then I think a 404 is absolutely appropriate. It's also standard practice to send a 404 in such a scenario.
    "First make it work. Then make it better."

  23. #23
    SitePoint Wizard bronze trophy Jeff Mott's Avatar
    Join Date
    Jul 2009
    Posts
    1,268
    Mentioned
    18 Post(s)
    Tagged
    0 Thread(s)
    Also, for example, what if the /blog/whatever pages were not generated by PHP, but were actually static HTML files on the server. Would you then think it OK that /blog/doesnt-exist return a 404? And if so, then why should it be any different if the responses to those URLs were instead prepared dynamically by a script?
    "First make it work. Then make it better."

  24. #24
    Hosting Team Leader silver trophybronze trophy
    cpradio's Avatar
    Join Date
    Jun 2002
    Location
    Ohio
    Posts
    5,129
    Mentioned
    152 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Jeff Mott View Post
    Also, for example, what if the /blog/whatever pages were not generated by PHP, but were actually static HTML files on the server. Would you then think it OK that /blog/doesnt-exist return a 404? And if so, then why should it be any different if the responses to those URLs were instead prepared dynamically by a script?
    To answer that, consider that /blog/doesnt-exist from a dynamic standpoint is really routing.php?page=doesnt-exist which in turn does not produce a 404 from any default server package. You are validating input, nothing more really. Yes, I could see where it would be beneficial to you to produce a 404 response header, but keep in mind, you took that responsibility on your own when you bypassed the server's behavior instead of letting the server perform its respective duties.

    In short, your processing a input, not the existence of a physical entity which is what I believe the original intent was for a 404 (be it a file or directory). How many places serve up a 404 because search.php?q=my+test doesn't return anything? Or if I could think of a better example that didn't involve searching, I would.

    I'll just agree to the fact that you took a behavior that was intended at a server level to be your sole responsibility because most of the time people fail to restrict what should and should not be sent to their routing pages. If you steal that responsibility, you need to re-invent the wheel and handle it accordingly (or use a framework that already does). But at the end of the day, you are truly providing validation, not the actual behavior the server was attempting to perform.

  25. #25
    SitePoint Wizard bronze trophy Jeff Mott's Avatar
    Join Date
    Jul 2009
    Posts
    1,268
    Mentioned
    18 Post(s)
    Tagged
    0 Thread(s)
    Regardless if we call it validation, ultimately we're testing whether the resource identified by a URL can be found. That resource can be found either by Apache looking for a file on the system, or by PHP looking for a record in a database. Either way, if the resource identified by the URL doesn't exist, then a 404 is what is supposed to be returned.
    "First make it work. Then make it better."


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
  •