PHP Frameworks? Just roll your own! – PART 1
There are quite a few php frameworks out there. Some huge, some small; Some useful, some not. I often hear developers, even myself, complain about frameworks in php not having or not doing something the way they/we want it too. So my solution? Roll your own framework! This series of articles is going to give you a brief and quick intro on how to do just that. We are going to cover url routing, basic database connection, basic templating and basic rewrite rules for Apache. What we will not cover is ORM development, advanced routing and installation of LAMP (Linux, Apache, MySQL, PHP). Also error checking will be next to none as this is just an example.
Mini-series Overview
- Week 1: URL Routing and Directory Setup
- Week 2: Templating
- Week 3: Database Interaction
To get started let’s setup our directory structure. We are going to call our framework “Peanut” (first thing that popped in my head).
So for our structure let’s make it really basic:
MyWebApp/
'---actions/
'---helloworld/
'--- actions.php
'--- models.php
'--- templates/
'--- yourTemplateHere.php
'---system/
'--- template.php
'--- database.php
'--- dispatch.php
'---www/
'--- js/
'--- css/
'--- images/
'--- index.php
Let’s break this structure down, it’s simple.
MyWebApp: the root of your web app that will be using our “Peanut” framework
- actions: this will contain all the pages our web app will use.
- helloworld: is the name of our page action or ’section’ of our site
- actions.php: is where we will code our page actions
- models.php: where we will write all our database interaction
- templates: this is the folder we will store all our views/templates
- helloworld: is the name of our page action or ’section’ of our site
- system: this is where we will store all our framework specific classes/source/libraries
- template.php: This will handle all of our template rendering.
- database.php: This is for our database connection and access
- dispatch.php: This will parse our url and call the correct page action.
- www: this is our root web directory, it is also where we will pull everything together.
- js: the directory where you store your javascript
- css: put your style sheets in here
- images: guess!
- index.php: This is the file that will initiate and pull together the pieces of Peanut.
Now that we have a directory structure, let’s setup a virtual host in Apache to accommodate for Peanut. Here is an example of a virtual host you can use (here is what I do on my Mac):
NameVirtualHost *:80
<VirtualHost *:80>
ServerName mywebapp.local
DocumentRoot /Users/wcope/Sites/mywebapp/www
AcceptPathInfo On
RewriteEngine On
RewriteRule /*\.(css|js|gif|png|jpe?g)$ - [NC,L]
RewriteRule "^(.*)$" "/index.php?_url=$1" [QSA,L]
<Directory "/Users/wcope/Sites/mywebapp/www">
AllowOverride All
Order allow,deny
Allow from all
</Directory>
</VirtualHost>
And In my /etc/hosts file I add this line:
127.0.0.1 mywebapp.local
That will allow ‘mywebapp.local’ to resolve to my localhost where Apache will pick it up and kick it off to the virtual host we created. Inside that virtual host we have some rewrite rules that will redirect all request made (except css, js, gif, png, jpeg/jpg) to index.php?_url=$1. To break that down in the simplest way, basically Apache takes: mywebapp.local/some/page and redirects it to index.php?_url=/some/page (but you never see the latter). Also notice that we made the root directory of our virtual host to our www folder where index.php is.
Ok, so now we have our directory structure, virtual host and rewrite rules all setup. If you have any detailed questions about Apache, mod_rewrite, virtual hosts, host files, etc. Sorry they will not be covered here due to the fact that there is tons on it out there… so hit up your local google.com :).
So now let’s get to some code.
So first lets write our dispatch code since that will be what is responsible for routing all our request to the proper pages.
Open up index.php in your favorite text editor and let’s slap some code in it:
In this file is a good place to put constant variables, include any files and call our dispatch (once we write it). But for right now let’s do a little test, and also write a little helper function I’m going to use to print stuff to the screen that can help us see what’s going on.
<?php
// First let's define our apps root directory
define("PEANUT_ROOT_DIR", realpath(dirname(__FILE__) . '/../') . '/');
// now a pretty little dump function (or if you have xdebug you can just use var_dump)
function dump($item, $die=true)
{
$printString = '<pre>' . print_r($item, true) . '</pre>';
if($die)
die($printString);
else
echo $printString;
}
// Now let's just dump our URL to make sure that all paths lead here
dump($_GET);
?>
Now if we bring up our url: http://mywebapp.local/some/url/ we should see something like:
Array
(
[_url] => /some/url/
)
So now we can see that our rewrite is working and we are ready to start writing our dispatch function, so let’s change our index.php code a little.
<?php
// First let's define our apps root directory
define("PEANUT_ROOT_DIR", realpath(dirname(__FILE__) . '/../') . '/');
// now a pretty little dump function (or if you have xdebug you can just use var_dump)
function dump($item, $die=true)
{
$printString = '<pre>' . print_r($item, true) . '</pre>';
if($die)
die($printString);
else
echo $printString;
}
// Let's comment this out
// dump($_GET);
// and add this:
// first we are going to drop our dispatch file in here
require(PEANUT_ROOT_DIR . 'system/dispatch.php');
// then call our dispatch function to handle the url
// Notice Im not passing in $_GET or $_GET['_url'], being that
// $_GET is a global system variable, I don't have to worry about it
// here.
dispatch();
?>
So now let’s save that and crack open our system/dispatch.php file and and add some code to it to handle our new found url pattern. As I said before, this will only be a simple router with no config or predefined url pattern to page action mapping.
So, inside dispatch.php, let’s add:
<?php
// Here we are going to handle our url request
function dispatch()
{
// First we are going to grab our url path and trim the / of the
// left and the right
$url = trim($_GET['_url'], '/');
// Now we are going to split the url on the / which
// will give us an array with 2 indexes.
$url = explode('/', $url);
// Let's just print out our array to get a visual of
// what we are working with.
dump($url);
}
?>
Now if you refresh your browser at our current url (http://mywebapp.local/some/url/) you should see:
Array
(
[0] => some
[1] => url
)
Great, now we have 2 clean items to work with. So let’s tell dispatch that the item at index 0 will be our folder and item at 1 will be the page action inside our actions.php file. Since our directory structure is setup to use actions/helloworld/actions.php, let’s setup dispatch to include the file with our action in it in the directory that was requested in the url. So change dispatch.php to look like:
// Let's handle that url
function dispatch()
{
// First we are going to grab the url path and trim the / off
// the left and the right.
$url = trim($_GET['_url'], '/');
// We changed this from before to make it easier to read and also so we can
// work with easier variables. using the list (http://php.net/list) function
// will map the array indexes to the respected order of the var names inside
// the list function call (better explanation at php.net/lists).
list($directory, $action) = explode('/', $url);
// Now we drop in our respected actions file from the directory in the url
require(PEANUT_ROOT_DIR . 'actions/' . $directory . '/actions.php');
// And call the action from inside our actions.php file
// and since we want to end our request with the return value of our requested method
// let's just die on the return.
die($action());
}
Now we need to add our action inside our actions.php file, so open it up and drop the following
in it:
<?php
// We simply define the function (the second item in our request url)
// making sure it is the same name as the one in the url.
function mypage()
{
// For now we are simply going to return "Hello World" string
return "Hello World";
}
?>
So let’s give this a shot. Open up your web browser and point it to: http://mywebapp.local/helloworld/mypage.You should see a pretty little “Hello World” in the browser. If you do, congrats! You now have routing in your new Peanut framework.
Next week we will see how to add templating into our framework.
Since the days of the Commodore 128, Wess has become an expert in C, C++, Java, Javascript, Python, and PHP and he also has extensive knowledge of Objective-C/Cocoa, Ruby, and Erlang. His blog can be found at www.wattz.net.




I’ve been meaning to develop my own framework for quite a while…. and one of my first headaches was URL routing. You just simplified it for me. Thanks.
Great tut! can wait for the rest!!
Great, looking forward to see next one.
Not object oriented? Seems like a waste to me. Might as well just grab a copy of CodeIgniter if you’re not going to make a proper framework.
Tim, this is an example to show how to perform URL routing using the simplest means necessary, not to be a real-world example or something that should be used in production. To do either we would also need some serious error checking and input filtering as well.
Not everything can or should be an object, anyway.
I’d suggest
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ /index.php?_url=$1 [QSA,L]
Very nice. I look forward to the rest of these tutorials.
Hi.
I just copy’d and paste every code just to check out what i was doing wrong but I didn’t do anything wrong I guess.
Cause when I try even only the first dump thingy, it says:
Array
(
)
only that so no output..
what do i have to do