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.
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
[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
<?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…
<?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…
<!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?