Roll Your Own PHP Framework: Part III
In Part I and Part II we looked at how to set up our file structure, how to get URL routing working and how to set up templating for our little framework. In this final part to the series, we are going to briefly look at database access.
Mini-series Overview
- Part 1: URL Routing and Directory Setup
- Part 2: Templating
- Part 3: Database Interaction
You can download all the files put together during this three part series here:
I’m not going into detail about how to create an ORM or ActiveRecord clone. We are just going to write a simple helper class to setup our connection and query. Also I’m going to assume you know how to create a database and table in the database.
I have created a database called “peanut” and created table:
users: +----------+--------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +----------+--------------+------+-----+---------+----------------+ | id | int(11) | NO | PRI | NULL | auto_increment | | username | varchar(255) | NO | | NULL | | | password | varchar(255) | NO | | NULL | | +----------+--------------+------+-----+---------+----------------+
In this example we are going to use Mysql and the php mysql_ methods. So break open system/database.php and drop the following in:
<?php
// Here is where we are setting up a simple wrapper class around mysql
// functions just to make it a little more convenient. Our models will
// simple extend our database class and simplify making queries just a bit.
class Database
{
protected var $connection;
// Every time you instantiate this class you are going to create
// a new connection to the database.
public function __construct()
{
// First we setup up a nice little connection to the database,
// make sure you use your connection information. If the
// connection fails we just die with the error.
$this->connection = mysql_pconnect("localhost", "root", "somepassword") or die("MySQL Error: " . mysql_error());
// And let's tell mysql which db we are going to use.
mysql_select_db( "peanut", $this->connection );
}
// This is just a helper function to help out against
// possible sql injection attacks.
public function escapeString($string)
{
// we call mysql's 'cleaning' function on strings
// just to make sure we get a little safer item to query
// with.
return mysql_real_escape_string($string);
}
// Here we will query the database with the passed query string,
// build up an array of objects and return them, simple enough.
public function query($qry)
{
// Here we make our query and set the result to a $result variable
$result = mysql_query($qry) or die("MySQL Error: " . mysql_error());
// Create a container array variable to hold all of our result objects.
$resultObjects = array();
// This might look weird, but all we are doing is saying,
// While you are still getting results, please put the next
// result into the next spot on my array.
while($resultObjects[] = mysql_fetch_object($result));
// Now we just return our array that has all our result objects in it
return $resultObjects;
}
// Here we add a simplier method for handling INSERTs and UPDATEs since
// they do not return a result set.
public function execute($qry)
{
$exec = mysql_query($qry) or die("MySQL Error: " . mysql_error());
return $exec;
}
}
?>
So we have our database class in place and ready to use, now we just need to require it in the index.php like we did the others, so pop that bad boy open and after the template require, do one just like it but for the database.php
Now let’s crack open actions/helloworld/models.php and let’s write a simple addUser and getAllUsers method to it. We are going to make our model a class as well so that it can extend our Database class that we wrote.
Here are the guts:
<?php
// All of our database back-and-forth will be handled in our models file
// Don't be mistaken, this is no where close to an ORM (Object Relational Mapper)
// it's just simplified database access.
class myModel extends Database
{
// When our model is instantiated we just need
// to also instantiate our parent class (Database)
// so it knows to make the connection to the database.
public function __construct()
{
// We just call the __construct of Database class.
parent::__construct();
}
// addUser will use the execute method of Database
// since we are inserting a value and not expecting
// anything in return.
public function addUser($username, $password)
{
// Here we use that little cleaning method we
// wrote to make our strings pretty and make sure
// they will play nice with mysql
$username = $this->escapeString($username);
$password = $this->escapeString($password);
// We execute our insert and if it worked $success
// will be true else it will be false.
$success = $this->execute("INSERT INTO users (username, password) VALUES ('{$username}','{$password}')");
// We return $success to inform our page action that it has or hasn't worked.
return $success;
}
// This method does just what you think it would.
// We are going to use the query function because
// we are expecting data back, then we are just
// going to return the array of user objects.
public function getAllUsers()
{
// Populate the $users variable with the results of our query
$users = $this->query("SELECT * FROM users");
// Now we return our results
return $users;
}
}
?>
Now we need to change our actions/helloworld/actions.php mypage page action to:
<?php
// We need to include our models file so we can access the database
require(PEANUT_ROOT_DIR . 'actions/helloworld/models.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()
{
// Let's do some database work!
// Here we are going to instantiate our model
$model = new myModel();
// Now let's add a user
// On a funny note, if you keep refreshing it will add this
// user over and over in your users table so your getAllUsers call
// will result in a list that grows by one each time.
$model->addUser("wess", "password");
// Now let's fetch all of our users and put them in our users
// variable.
$users = $model->getAllUsers();
// Let's create a new template object and pass it the path to our template
$template = new Template("helloworld/templates/helloworld.php");
// I bet you want to display a table in your template with all your newly
// created users. So let's do it
// That's write we use the same set command, and it will set our array of users
// to our template variable $users, and since php is our template language
// it's real easy to print them to the screen.
$template->set('users', $users);
// Set our page variable "title" with the value "Hello World"
$template->set('title', 'Hello World');
// Set our page variable "message" with our little message
$template->set('message','This is my first message for my template');
// Now we can call render and return it to dispatch to
// display in our browser
return $template->render();
}
?>
So now you are handing all your users from your database to the template, so let’s display them. Change your actions/helloworld/templates/helloworld.php file to:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html> <head> <meta http-equiv="Content-type" content="text/html; charset=utf-8"> <title>My PEANUT Page</title> </head> <body> <h1><?php echo $title ?></h1> <p><?php echo $message ?></p> <h2>Users:</h2> <table> <thead> <tr> <th>ID</th> <th>Username</th> <th>Password</th> </tr> </thead> <tbody> <!-- Here we are going to use native php foreach look to create each row of the table with our list of users. Notice how the foreach is done with a : and surrounding php tags. --> <?php foreach($users as $user): ?> <tr> <td><?php echo $user->id ?></td> <td><?php echo $user->username ?></td> <td><?php echo $user->password ?></td> </tr> <?php endforeach; ?> </tbody> </table> </body> </html>
Now if all went well, you should save all your files and open up: http://mywebapp.local/helloworld/mypage and see all the users that you added in a table.
So you have created a template class, url basic url routing, simplified database access, and a page action. You have all the core basics of a PHP Framework at your hands now. This is just for a starting point or foundation of understanding, please do not use this in a production environment.
I have really enjoyed writing this and I hope you learned something from my rambling! The source code is available for download at the top of this article.
Take care,
Wess “Wattz”
Updated October 26, 2009: Thanks to Alex and Ian for spotting some errors in the code and supplying the fixes. The code examples have been updated to reflect the changes as has the ZIP file.
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.



Thanks for the Part III, great tutorial.
I will check it latter when I’m back home.
This has been a really helpful series so far, but I found a number of errors with the Database class. First, it needs a private member variable called “connection”. Also, in the constructor, the result of mysql_pconnect should be stored in $this->connection. And lastly, the arguments for the call to mysql_select_db should be reversed. Other than that, it works great, and I’m looking forward to enhancing it for some additional functionality.
Thanx Alex, you are exactly right, when i was writing it, i must have just burned through it. Thanx for the catch!
Wess
@wattzilla
I’ve been waiting for part 3. I really like how it is kept simple enough to to portray the concept and not be to overwhelming. A great launching point for a first time framework builder.
Great tutorial, but there seems to be a small typo in the query function. The variable $resultObjects is spelled $resultsObjects in the while loop. Who knew that 1 small letter could cause so much trouble :)
You’re also using mysql_fetch_object instead of mysql_fetch_assoc in the query function, which means that you should be accessing the objects with “$user->id” in your template.
Yeah, I caught those last week too. Let the editor know about them and just waiting for the update.
I do appreciate you paying so close attention and pointing out the type-o’s for me!
Very nice. I’ve always wanted to make a framework, and this series showed me how. Thanks!
Do you seriously need to make a database class? mysqli, in case you didn’t know, is a native OOP MySQL extension. Whenever possible, native > hand-coded.
I think you are missing the point. It was just to resemble a model class, an example. I prefer PDO to mysqli but there are a few choices for native libraries as well as ORM libraries people have written. Please understand that this article is to give understanding and direction not to provide a complete framework ;)
Hi Wess – thanks for the great tutorial! I have a quick question (and I realize you probably can’t answer this without seeing my code, but I didn’t really change anything from your tutorial, only dressed up the helloworld.php template) – every time I load the page, I get 7 results added to the database instead of 1. Does anything leap to mind as to why this might be happening?
Interesting – I think it’s a problem with the mod_rewrite. I added a unix timestamp field to the database to verify whether the entries were all being inserted at the exact same time, or a few seconds off, and they’re a few seconds (or rather, fractions of a second) off. When I comment out the mod_rewrite an access it via index.php?_url=foo, no duplicate entries.
So something is funky with the mod_rewrite. The page is not reloading, so I’m guessing an image or css or javascript file is somehow being redirected and triggering the script again, causing the duplicates.
Strangely, the first time I hit the page with an emptied database, it inserts one. When I hit it again, it inserts seven on every page load.
Hey everybody. I’m not sure if there’s any interest in this, but I put the code up on http://github.com/mrdude/peanut-framework if anyone would like to take this any further. I have somethings I’d like to do to it (if I ever get the time), and obviously Lars is making changes to it. So if it’s alright with you Wess, since it is your original work, would it be alright if I licensed it as GPL?
Caleb, I don’t mind, but this is no where near anything that I would expect people take any further as it’s just to give a very high overview. I have already started a project called: Actionleaf (actionleaf.googlecode.com) which you could move to Github and work on, Im open for that. If you want Peanut, feel free. Actionleaf is closer to being a completely solution , I am just currently swamped and haven’t had time to work on it. Shoot me a DM on twitter if you have any other questions (@wattzilla).
Thanks for the interest everyone!
Wess
Hi Caleb,
I have NO idea why my comments came through as Lars – guess I wasn’t paying attention to the stored fields.
It did turn out to be a mod_rewrite issue, although I’m still not entirely sure what it was. I hopped onto tech support chat (I’m cloud hosted, and wanted to confirm there were no weird hacks in place that might be causing problems). The tech support guy swears he didn’t change anything, but it suddenly stopped doing the 7-inserts and worked properly. So, needless to say, I’m skeptical that he didn’t change anything, but the situation is resolved.
I’m curious as to how you would suggest best handling (within this specific framework structure) functions and classes that affect a global template.
For example, in my template pages, I’m including a main header and footer template since that is the same throughout the site, but I’d like to make submenus, active navigation elements, etc easier to generate, so I would create a set of functions to handle those. Where in this file structure would you recommend keeping those, since the classes for everything else have specific places? In CakePHP or Symfony, that’s a little clearer, but I’m not sure what you envisioned in this one.
Are you talking about making methods available to all your page views? If so if you change the structure slightly to use classes for pages (like appengine, and actionlead) then you can extend a central class AppController that would have your ‘global’ methods, ActionLeaf( actionleaf.googlecode.com ) does:
class MyPage
{
public function get(request)
{
// page code
return “Hello World”;
{
}
Using that same structure you could make a class like:
class AppController
{
protected function doSomething()
{
return “Something fun!”;
}
}
class MyPage extends AppController
{
public function get(request)
{
$this->doSomething();
return “Hello World”;
{
}
This is not a new way or anything, quite a few frameworks do it.
Well I’m not sure what Wess would do, but I like how Ruby on Rails does it (yea I use both ^_^). In Rails you have views for different actions and a layout for the entire site. This framework’s directory structure does not at once have a logical place to put that, so here’s a fix but it’s a little kludgy. I’d create a file called app_layout.php in the templates folder then in the template class you’ll need to change the code to include the layout and pass the url parameters to the layout so it can dynamically include the view for that action. So using this approach you would put everything that would be on every page in the app_layout.php file. That would obviously be the header and footer, but there should be just a snippet of code that uses the variables passed to the layout by the template class that calls the view file for that action. This may not be the approach you were thinking of but after using Rails, to me it just seemed logical.
I’ll tell you exploring Rails has been an enlightening experience, and will help in the long run with PHP. HTH
P.S. If I was the IT guy I would have said “No I didn’t change anything” *thinking to himself* -> “I knew I shouldn’t have touched that option!” and then went and fixed it. I’d venture to say he did it, but then again I can’t see your code.
Also, make sure you realize that Peanut is just an example of how the pieces fit together. I refer back to ActionLeaf again here, what actionleaf does (like django) is allow you to ‘extend’ a template.
So if you have:
then in your sub template you can do:
extends(’layout.html’)?>
Hello World
Which would wrap your h1 in the extended template.
Sorry, the filters killed that, if you want more info on actionleaf or anything, DM me on twitter @wattzilla or email me: wess(a)wattz(d)net
Yeah I took a look at actionleaf. It’s very nice, and I enjoyed reading the code. It’s very clean and well thought out. I would like contributing to such a project but being as young as I am (and I mean just a little older than ethan) my coding hasn’t reached the level of proficiency I’d like. Also I do realize that Peanut is just an example. I’m basically using this as a learning tool, going through it and seeing what features I could add and how this could be done differently and how that could be implemented. Trust me I’m not about to tell people to use it for a project. Though it does seem that snipe has taken a fancy too it.
Heh – well, I’ve taken a fancy to it as a learning tool – I don’t know that I’d use it in production. I’ve been a PHP dev for many years and have used several frameworks, but had never bothered to write my own just for the experience. I have my own structure that I typically use when creating new apps, file locations, templating, etc – but this has been fun to play around with.
Likewise Snipe, well except for the many years of development part ^_^. Being as young as I am, I really need things like this to help with my experience even though it’s “off the job” so to speak. So to Wess, thanks for writing this series, and I hope you write more. And a last note: As a young and inexperienced developer I’ve not used as many frameworks as you two probably have. What would you consider your favorite framework?
Haha – wow, that’s a dangerous question to ask – some people get pretty bent out of shape when their favorites are challenged. I know some of the devs on CakePHP and Zend, so I’m probably more experienced with them than others, and I’d have to say I prefer Zend, but my intro to Cake was no ideal. I had to step in and take over a poor implementation of Cake so it sort of left a bad taste in my mouth, thanks to the previous dev, not Cake itself. So my preference is probably Zend, with Symfony a close second. But that’s just me, just my personal opinion, and I’m sure lots of others will have their own preferences are reasons for it.
Yeah, a real loaded question there. You are, most likely, going to laugh when I tell you I don’t prefer php at all, the reason ActionLeaf is there is because i was forced to use php and needed php to be more tolerable.
For web dev, my favorite framework for ‘websites’ is Django, otherwise I develop a bit different for ‘web apps’ where I would use a bit different setup that would take a long minute to explain, perhaps my next article ;)
Yeah, but I didn’t really think it would cause a flame war here though. Everyone here seems to be decent, and I was _not_ going to ask that on Stack Overflow and get my rep downvoted. So I just thought I’d ask you guys. Thanks for your answers, and I would be interested in hearing your setup for developing web apps. Maybe they’ll let you write an article on that, if it’s something that would be useful to other people.
Hey all
Having some issues with this, it seems to not like the fact that instead of creating a virtualhost I am trying to do it via a .htaccess instead
A lot of people seem to be getting on with this so what am I doing wrong?
I thought I would metion that the while statement that you have you have in database.php on line 45 will always add an extra boolean element to the end of the array. It’s not a big problem I thought i would point it out because it took me 20 minutes to figure out why it wasn’t adding a user to the db after i checked to see if it was already there and my table was alway empty.
Sorry if this doesn’t make sense I think my train of thought might have derailed somewhere. :(