At the start of this year () I embarked upon a small personal challenge which I'd set myself: could I build a PHP MVC framework in a week? I had a fairly quiet schedule at the time, and I believed it was possible, so I began.
Now, I have had quite a few years experience using various frameworks, so I had a rough idea about how I wanted to go about structuring my own:
- Basic request router to controller(s)
- Controllers fetching and pushing to models
- Database abstraction layer built on PDO
- Form validator
- View handler, with ability to chain in required parameters as needed
As you can see from my code commits, I did make this deadline (if you forgive the fact that I skipped the because I wasn't able to work on it that day!) It wasn't the prettiest of frameworks, it had more than a few holes, but it did work, and it did contain everything from my shopping list. I was pretty happy, but then I started looking at it some more. In my heart, I knew I could make it better, and actually turn it into something useful, and not just a toy project. Here was something I created, and unlike Doctor Frankenstein, I wasn't afraid to improve upon it.
Improving Upon the Base
First, I worked on some of the areas around error handling and logging, as this was an area I'd previously neglected in my rush. This is now a lot better, allowing for varying levels of information to be logged in a set of custom application logs (these don't effect standard web server logs). My choice for creating custom logs like this, is that I often work on projects where the hosting is quite out of my control, so getting access to raw logs is difficult and sometimes impossible.
Next on my wishlist was a PSR-0 autoloader to allow me to give me code a more structured layout that followed best practices, and also would let me use tools like Composer to extend the basic functionality of my framework. This was soon followed with multi-lingual support using
It was at this point that I began some of the much-needed cleanup work, neatening up various logic constructs, and moving out core classes to the
vendor directory and namespacing them so they could use the PSR-0 autoloader. I learned a lot about namespacing that I previously had never had to deal with before, most specifically namespace scope. For example, when within a namespaced class, this won't work as you expect unless you specify the fully qualified namespace (this is in the documentation, but I admit to not fully reading that before ploughing in):
$obj = new stdClass();
Some Fun After a Break
At this point a took a short break from working on it, as I'd been going at it for a couple of months with few days apart. I decided that the next addition to it would be a bit more fun, and not generally essential to a framework. I have done previous work with images, including writing custom effects filters, so I dug those out of my old code lying around and added them in to an image helper, and a smart image resizing method, which I tweaked a lot to allow for some more complicated resizing to happen, much like can be done on the command line with ImageMagick.
Following on from this came a much needed caching mechanism, which taught me a lot that I didn't know about APC. I built upon what I was learning to create a caching class that could switch between file or APC (with the former being a fallback where APC wasn't available), and even added the capability to cache output headers. This meant that whenever a request came in that had already been served recently, the exact same response could be given out.
Before I knew it, I was nearing my 100th commit, and decided to celebrate in traditional style; by adding an easter egg! Any time the 418 status code was used in the output headers when building a view, a lovely ASCII art image of my mug along with the framework name and the phrase
We are all teapots! was injected as an HTML comment just before the closing
</body> tag. I added the ability to turn it off from the config, but chances are most people won't actually be using the 418 status code seriously anyway!
The Future of the Project
Since then I've been constantly adding to it, and have even built sites and applications on top of it, which has been the best test of it capabilities thus far (one of which is a CMS which is in the second branch of the project.) The feature-set of what was once meant to be a small, quick challenge project is now pretty impressive:
- Complex routing, allowing routes to be split by request type, and using regular expression matching
- Route pre-parsing with custom functions to analyse and modify the URL, such as for language routing
- View templating, and the ability to extend this with custom templating functions
- i18n multi-lingual capabilities using the industry standard gettext features in PHP
- Caching via APC, allowing a fallback to file-caching where APC isn't available
- Versatile config split across files for ease of management
- PSR-0 autoloading that allows the framework to be extended further with Composer
- Powerful database abstraction layer built on PDO that uses query parameterisation for security, and includes the capability to create
INSERT...ON DUPLICATE KEY UPDATEqueries.
- Powerful form validation based on rule arrays
- Form helper class for building forms using JSON strings for the element configuration, that utilises the templating engine to allow you to fully customise your forms
- Image helpers to generate and modify images, with a large variety of filter effects to choose from.
- File helpers to generate directory trees and obtain detailed information about files
- Other HTML helpers for generating lists and various types of tables more easily
This was and is a fun project to work on. All the code for MaVeriCk is on my GitHub account, so feel free to play around with it and give me any feedback you have.
Incidentally, if you're wondering at the name and the odd capitalisation, this word was the only one I could think of in the English language that contained the letters MVC in that order. Plus, this framework is essentially me doing things my own way, so I felt it was aptly named.