The no-framework PHP MVC framework
March 1, 2006 - Disclaimer: Since a lot of people seem to me misunderstanding this article. It isn't about OOP vs. Procedural programming styles. I happen to lean more towards procedural, but could easily have gone more OOP. I simplified the code a bit for brevity, but have added a light OO layer back in the model now. Not that it makes a difference. What I was hoping to get across here is a simple example of how you can use PHP as-is, without additional complex external layers, to apply an MVC approach with clean and simple views and still have all the goodness of fancy Web 2.0 features. If you think I am out to personally offend you and your favourite framework, then you have the wrong idea. I just happen find most of them too complex for my needs and this is a proposed alternative. If you have found a framework that works for you, great.So you want to build the next fancy Web 2.0 site? You'll need some gear. Most likely in the form of a big complex MVC framework with plenty of layers that abstracts away your database, your HTML, your Javascript and in the end your application itself. If it is a really good framework it will provide a dozen things you'll never need.
I am obviously not a fan of such frameworks. I like stuff I can understand in an instant. Both because it lets me be productive right away and because 6 months from now when I come back to fix something, again I will only need an instant to figure out what is going on. So, here is my current approach to building rich web applications. The main pieces are:
MVC?
I don't have much of a problem with MVC itself. It's the framework baggage that usually comes along with it that I avoid. Parts of frameworks can be useful as long as you can separate the parts out that you need. As for MVC, if you use it carefully, it can be useful in a web application. Just make sure you avoid the temptation of creating a single monolithic controller. A web application by its very nature is a series of small discrete requests. If you send all of your requests through a single controller on a single machine you have just defeated this very important architecture. Discreteness gives you scalability and modularity. You can break large problems up into a series of very small and modular solutions and you can deploy these across as many servers as you like. You need to tie them together to some extent most likely through some backend datastore, but keep them as separate as possible. This means you want your views and controllers very close to each other and you want to keep your controllers as small as possible.Goals for this approach
- Clean and simple design
- HTML should look like HTML
- Keep the PHP code in the views extremely simple: function calls, simple loops and variable substitutions should be all you need
- Secure
- Input validation using pecl/filter as a data firewall
- When possible, avoid layers and other complexities to make code easier to audit
- Fast
- Avoid include_once and require_once
- Use APC and apc_store/apc_fetch for caching data that rarely changes
- Stay with procedural style unless something is truly an object
- Avoid locks at all costs
Example Application
It is a form entry page with a bit of Javascript magic along with an sqlite backend. Click around a bit. Try to add an entry, then modify it. You will see the server->client JSON traffic displayed at the bottom for debug purposes.The Code
This is the code layout. It uses AJAX (with JSON instead of XML over the wire) for data validation. It also uses a couple of components from the Yahoo! user interface library and PHP's PDO mechanism in the model.
The presentation layer is above the line and the business logic below. In this simple example I have just one view, represented by the add.html file. It is actually called add.php on the live server, but I was too lazy to update the diagram and it really doesn't matter. The controller for that view is called add_c.inc. I tend to name files that the user loads directly as something.html or something.php and included files as something.inc. The rest of the files in the presentation layer are common files that all views in my application would share.
ui.inc has the common user interface components, common.js contains Javascript helper functions that mostly call into the presentation platform libraries, and styles.css provides the stylesheet.
A common db.inc file implements the model. I tend to use separate include files for each table in my database. In this case there is a just single table called "items", so I have a single items.inc file.
Input Filtering
You will notice a distinct lack of input filtering yet if you try to inject any sort of XSS it won't work. This is because I am using the pecl/filter extension to automagically sanitize all user data for me.
View - add.html
Let's start with the View in add.html:The main thing to note here is that the majority of this file is very basic HTML. No styles, or javascript and no complicated PHP. It contains only simple presentation-level PHP logic. A modulus operation toggles the colours for the rows of items, and a loop around a heredoc (<<<) block performs variable substitutions. head() and foot() function calls add the common template headers and footers.
If you wanted to make it even cleaner you could use an auto_prepend_file configuration setting which tells PHP to always include a certain file at the top of your script. Then you could take out the include calls and the initial head() function call. I tend to prefer less magic and to control my template dependencies right in my templates with a very clean and simple include structure. Try to avoid using include_once and require_once if possible. You are much better off using a straight include or require call, because the *_once() calls are very slow under an opcode cache. Sometimes there is no way around using these calls, but recognize that each one costs you an extra open() syscall and hash look up.
ui.inc
Here is the UI helper code from ui.inc:
This file just contains the head() and foot() functions that contain mostly plain HTML. I tend to drop out of PHP mode if I have big blocks of HTML with minimal variable substitutions. You could also use heredoc blocks here, as we saw in add.html.
Controller - add_c.inc
Our Controller is in add_c.inc:
Our controller is going to manipulate the model, so it first includes the model files. The controller then determines whether the request is a POST request, which means a backend request to deal with. (You could do further checks to allow an empty POST to work like a GET, but I am trying to keep the example simple.) The controller also sets the Content-Type to application/json before sending back JSON data. Although this mime-type is not yet official so you might want to use application/x-json instead. As far as the browser is concerned, it doesn't care either way.
The controller then performs the appropriate action in the model according to the specified command. A load_item, for example, ends up calling the load() method in the data model for the items table and sends back a JSON-encoded response to the browser.
The important piece here is that the controller is specific to a particular view. In some cases you may have a controller that can handle multiple very similar views. Often the controller for a view is only a couple of lines and can easily be placed directly at the top of the view file itself.
common.js
Next I need to catch these JSON replies, which I do in common.js:
The postForm() and postData() functions demonstrate the genius of the Yahoo user interface libraries: they provide us with single-line functions to do our backend requests. The fN function in the callback object does the bulk of the work, taking the JSON replies generated by our controller and manipulating the DOM in the browser in some way. There are also fade() and unfade() functions that are called on status messages, and on validate errors to produce flashing red field effects.
Note the bottom half of this file where fancyItems() and fancyForm() implement all the client-side magic to animate the forms by attaching handlers to various events. Often you will see server-side business logic nicely separated from the templates, but then there are big blocks of complicated client-side Javascript mixed into the template which in my opinion defeats the clean separation goal. By going through and attaching appropriate mouseover, mouseout, focus, blur and click handlers after the fact I can keep my templates extremely clean and still get a very dynamic experience. Here I am using the event library from the Yahoo! user interface libraries to add the handlers.
Model - db.inc
Now for the model. First the generic db.inc which applies to all our model components:
I am using sqlite via PDO for this example, so the connect() function is quite simple. The example also uses a fatal error function that provides a helpful backtrace for any fatal database error. The backtrace includes all the arguments passed to the functions along the trace.
The load_list() function uses an interesting trick: it uses APC's apc_fetch() function to fetch an array containing the list of item categories. If the list isn't in shared memory, I read the file from disk and generate the array. I have made it generic by using a variable variable. If you call it with load_list('categories'), it automatically loads categories.txt from the disk and creates a global array called $categories.
Model - items.inc
Finally, I have the model code for the items table, items.inc:
At the top of each model file, I like to use a comment to record the schema of any associated tables. I then provide a simple class with a couple of methods to manipulate the table: in this case, insert(), modify() and load(). Each function checks the database handle property to avoid reconnecting in case I have multiple calls on each request. You could also handle this directly in your connect() method.
To avoid an extra time syscall, I use $_SERVER["REQUEST_TIME"] to retrieve the request time. I am also using PDO's named parameters mechanism, which is cleaner than trying to use question mark placeholders.
Conclusion
Clean separation of your views, controller logic and backend model logic is easy to do with PHP. Using these ideas, you should be able to build a clean framework aimed specifically at your requirements instead of trying to refactor a much larger and more complex external framework.
Many frameworks may look very appealing at first glance because they seem to reduce web application development to a couple of trivial steps leading to some code generation and often automatic schema detection, but these same shortcuts are likely to be your bottlenecks as well since they achieve this simplicity by sacrifizing flexibility and performance. Nothing is going to build your application for you, no matter what it promises. You are going to have to build it yourself. Instead of starting by fixing the mistakes in some foreign framework and refactoring all the things that don't apply to your environment spend your time building a lean and reusable pattern that fits your requirements directly. In the end I think you will find that your homegrown small framework has saved you time and aggravation and you end up with a better product.
Trackbacks
blogZero on : Building a Simplified PHP MVC Framework
Show preview
Jacques Marneweck's Blog on : Rasmus's no-framework PHP MVC framework
Show preview
Gabriel on : The no-framework PHP MVC framework - Rasmus’ Toys Page
Show preview
Âåáäåâ on : The no-framework PHP MVC framework
Show preview
Jack Ma's Weblog on : The no-framework PHP MVC framework
Unfortunately, the contents of this trackback can not be displayed.ScummBlog on : A PHP Framework rethink
Show preview
www.the-unseen.co.za on : PingBack
Unfortunately, the contents of this trackback can not be displayed.scapbi02.wordpress.com on : PingBack
Unfortunately, the contents of this trackback can not be displayed.www.adifco.fr on : PingBack
Unfortunately, the contents of this trackback can not be displayed.www.php-resource.de on : PingBack
Unfortunately, the contents of this trackback can not be displayed.plind.dk on : PingBack
Unfortunately, the contents of this trackback can not be displayed.www.websightdesigns.com on : PingBack
Unfortunately, the contents of this trackback can not be displayed.www.littlehart.net on : PingBack
Unfortunately, the contents of this trackback can not be displayed.www.semanticblog.eu on : PingBack
Unfortunately, the contents of this trackback can not be displayed.blog.swiac.fr on : PingBack
Unfortunately, the contents of this trackback can not be displayed.www.blogoolic.fr on : PingBack
Unfortunately, the contents of this trackback can not be displayed.www.littlehart.net on : PingBack
Unfortunately, the contents of this trackback can not be displayed.www.littlehart.net on : PingBack
Unfortunately, the contents of this trackback can not be displayed.www.liushan.net on : PingBack
Unfortunately, the contents of this trackback can not be displayed.LornaJane on : Preparing for ZCE 5.3
Show preview
The author does not allow comments to this entry
Comments
Display comments as Linear | Threaded
Douglas Clifton on :
On a side note, I'm curious to see how developers will take to Yahoo! REST requests being returned as serialized PHP.
Brian Dickson on :
I am a PHP noob, an OOP noob and an MVC noob. You may already be thinking about dismissing my opinion before I can explain my position, pls bear with me.
I have been building websites (small ones), over the past four years using Dreamweaver, for both static and dynamic projects, (this is an instance where size DOESN'T matter, trust me). It was only recently when I have been involved in the creation of one of my own projects, that I decided I would have to, do things as I did when I first became interested in computers over twenty five years ago, that is, get my hands dirty in some heavy code.
I needed a site that would through up articles and video/audio that was related to the article. I needed to create a tutorial website, that makes heavy use of tracking users actions, article use over time etc. I first looked at the two major open source frameworks, but I soon ran into obstacles when I wanted to do something specific. I spent over three months becoming familiar with both of them before realising a simple truth.
Then I spent another month researching PHP frameworks, having been sold the marketing mantra of, access to libraries, cleaner and better, more organised code. Again after trying to wrap my head around MVC and OOP, I realised that same simple truth. What is this truth I speak of?
I spent so much time learning these CMSes and frameworks, that I believe I could have competed the the whole project within the same time period, using PHP5 classes and straight HTML/CSS.
As for cleaner organised code that you can come back to in a year? I don't understand the argument. If you split your code down into manageble block, as in the example of this article, managability is not an issue.
As for code re-use? again, I don't understand. Do people not build up a library of common classes that are well commented? can you simply not include ir call these classes in your future projects or the same project?
Enough of this nonsense. It seems people against this Rasmus' comments are simply wishing to defend their particular way of working.
I realise I am a guy developing on his own and the needs of a team might be different, but I doubt it.
OK, I'm off to do what I should have done three months ago. Thanks Rasmus.
PS I found CodeIgniter the lightest framework out there and it doesn't impose too many restrictions.
ismael on :
I can always override the default model or controller if I want more complex functionality...Caching takes care of that extra performance hit. I've made my own multi layered framework and up until now it's made me happy, and I don't see why I shouldn't use some other people's framework if it is well designed and solves the common problems I don't need to go over - again.
Rasmus on :
Jürgen on :
A framework isn't ment to just add layers of complexity, it also enforces a certain way of coding (ok I realize you could just as well write coding specs).
Your statement about keeping it procedural as much as possible means losing ALL the benefits of OOP in the first place.
Next please compare your controller to (for example) this:
class ItemController
{
function ItemController()
{
$this->model = new ItemModel();
$this->view = new ItemView($this->model);
}
function run()
{
$action = isset($_POST['itemAction']) ? $_POST['itemAction'] : 'listItems';
switch($action)
{
case 'listItems':
$this->view->renderList();
break;
case 'addItem':
$this->model->addItem();
$this->view->renderList();
break;
}
}
$controller = new ItemController();
$controller->run();
Which one do you find easier to come back to after 1 year?
All that's left to say is : globals? SQL Injection?
I can't believe what you posted, I'm downright shocked to the bones.
Andrew on :
Which one do you find easier to come back to after 1 year?
All that's left to say is : globals? SQL Injection?
I can't believe what you posted, I'm downright shocked to the bones.
This is very effective and maintainable. In fact, I think it'd be even more maintainable than some object oriented mess of code, especially when that object oriented mess of code, is just a mess.
Ries van Twisk on :
@Andrew: We assume that OO and procedural code is correct of course... So no mess..
This way of working is excellent for small websites with just one maintainer, and maybe one developer sitting close to each other, simple as that.
But when things start to grow you can't do it like that anymore. Code will be repeated all over and will be un-maintainable anymore. Not to mention security problems later on, when things change...
Also doing direct SQL calls from an application is without a layer is 'not-that-nice'.....
Rasmus on :
Jürgen on :
Indeed I overlooked the fact you're using PDO. My bad ;)
I agree that your code isn't really spaghetti code (and we all know how capable you are, let that be clear), and I certainly agree 100% about your comment in regards to extremely bad designed and totally besides the point written frameworks.
So how's Pear doing these days then? I'd say it is a PRIME example of how NOT to design an OO framework, so there you have it. People download pear code, learn from it, and think it's OKAY to have a Date class which in total uses nearly 250kb of code... I rest my case ;)
I think you are doing a lot of damage with this posting. Do you realize what's going to happen if ONE of the 30 programmers in the building I work in find's your post? For years I have struggled convincing fellow programmer's that PHP is NOT a simple scripting language for the gamers and what not, that it actually CAN compete with J2EE and ASP.NET and C# if used where appropiate. To read this from the creator of the language....I'm still shocked
Besides, how this example is supposed to show that using a monolithic controller is bad is beyond me.
Greetings,
Jürgen
Rasmus on :
I still think you are hung up on OOP vs. Procedural. Both the controller and the DB layer could easily have been OOP and it wouldn't change the point of example nor does it increase readability. I guess your biggest beef is that I used a global for my database connection handle instead of making it a property of a database wrapper class? It's a minor point as far as I am concerned, and yes for namespace-related concerns you may want to wrap it like that, but again, this was not the focus of this article. The focus was the separation and where to put what, not the line-by-line details of the actual implementation. You also wouldn't use sqlite for a huge project, but it is perfectly fine as a way to illustrate the concepts here.
George Jempty on :
Noel Darlow on :
In the anarchic and often anti-intellectual world of php there needs to be some leadership. Anyone who seriously wants to learn how to program is badly served by articles such as above. Most people churn out code exactly like this but there must be clear goals for those who want to go on to learn more - OOP & testing basically.
I don't doubt that you found some bad examples of OOP frameworks. That's not surprising given the lack of an object culture (http://www.procata.com/blog/archives/2006/01/13/building-a-culture-of-objects-in-php/) in php but it doesn't mean that OOP is a bad choice. OOP doesn't add complexity: it's a way to tame the complexity which already exists but which might not have been understood.
Rasmus on :
Jules on :
I've rewritten this application using Ruby on Rails.
DISCLAIMER: I'm fan of rails. Make sure you have some salt to take.
Rails is one of the frameworks which fit more or less in this description:
"Most likely in the form of a big complex MVC framework with plenty of layers that abstracts away your database, your HTML, your Javascript and in the end your application itself. If it is a really good framework it will provide a dozen things you'll never need."
Rails is not big, some things are complex, but simpler than the same thing in PHP. There is only one layer of abstraction for the database (ActiveRecord). It abstracts html to some degree, but you still have "code" templates (like vanilla php), not a separate template language. (like smarty). It abstracts javascript (I did not write a line of JS for this application). I'm not sure what you mean with "abstracts [...] the application itself".
"If it is a really good framework it will provide a dozen things you'll never need."
Ehh, how many functions does PHP have? How many do you use? Providing these is still a good idea, because others need them.
Well, back to the rails version. It is less than 100 lines (excluding CSS). Yours is 400 lines (excluding CSS again). You may formulate a conclusion.
Rails uses the shared-nothing architecture, like PHP, so both are perfectly scalable.
And, in my opinion, the rails version is better designed. You have validation code in the controller. It should really be in the model. Plus, there are 0 lines of sql in my application. This means that you can use any database, mysql, pgsql, sqlite, etc. without rewriting your sql.
I agree that your code is not spaghetti. It doesn't mix controller, html and data code (if you don't count the data validation, which is data-code, so that means model)
If I find some time, I will write a more in-depth reply to this. (with code comparison)
Rasmus on :
Johan Bergens on :
Regarding the actual framework I like your idea that we should write a good base and use that, and there are a lot of framworks to choose from, but given that you could choose one that you like and use in every project you will probably not have much problem going back to the code a year later.
The naming in your models made me nervous about building larger apps where you need more than one model/table for a view. The can't all have load_items() and be included at the same time. Therefore some grouping is needed and I think objects could help a lot. But namespaces might do the trick if someone gets them into PHP...
Rasmus on :
Jules on :
The javascript effects are created using ruby code that gets compiled to javascript. There is a feature called rjs (ruby-javascript) templates for these things.
Example:
page.visual_effect(:highlight, 'the_id_of_a_div_on _the_page')
Gets compiled to something like:
new Effect.highlight('the_id_of_a_div_on _the_page');
And this javascript code is executed in the browser.
Rails is consistent: all validation in the model:
class Product < ActiveRecord::Base
validates_presence_of :description, :category
end
If you try to save a product with an empty category/description, you'll get an errormessage.
I've studied your php example, and I see that I can strip some more lines off the ruby code: your script just refreshes if the input data is good, my version updated the products table, and highlighted the succesfully saved product.
But I really like your approach overall. This mvc 'framework' could even be used for really big applications. You would probably create a few folders (views, controllers, models), but you don't need objects to do mvc.
And I agree that you don't need a template language. PHP was originally intended as a template language, and it is really good for html generation. You have to keep attention not to mix the m, v and c if you are used to spaghetti code though.
No on :
Rasmus on :
function fade(o, dur, fnc) {
var oAnim = new YAHOO.util.Anim(o, {opacity: {from: 1, to: 0}}, dur);
if(fnc) oAnim.onComplete.subscribe(fnc);
oAnim.animate();
}
function unfade(o, dur, fnc) {
var oAnim = new YAHOO.util.Anim(o, {opacity: {from: 0, to: 1}}, dur);
if(fnc) oAnim.onComplete.subscribe(fnc);
oAnim.animate();
}
where I can pass in a function pointer to be called when the animation completes. I don't really see how Rails can abstract that away. How do you tell Rails the starting and ending opacity levels on a fade and the fact that you want a certain function to be called at the end of the fade unless you actually specify this somewhere?
Jules on :
visual_effect :fade, 'modified', :duration => 2
Rasmus on :
div = document.createElement("div");
div.className='status';
div.innerHTML = resp['status'];
pos = YAHOO.util.Dom.getXY(resp['elem']);
div.style.visibility = 'hidden'
document.body.appendChild(div);
YAHOO.util.Dom.setXY(div,[pos[0],pos[1]+40]);
div.style.visibility = 'visible'
fnc = function() {
if(resp['reset']) { document.forms[resp['formName']].reset(); }
window.location.reload(false);
}
fade(div,2,fnc);
unless something is reading your mind somewhere.
Tomas Jogin on :
{ :action => "add" },
:update => "modified",
:complete => visual_effect( :fade, 'modified', :duration => 2)
) %>
That's all the JS/AJAX code necessary to update a given element and to add a visual effect such as a fade-in or an animation of some sort. Much easier to read IMHO.
Rasmus on :
Tomas Jogin on :
Another way to do it would be to just add a html snippet to the bottom or top of an element container (instead of replacing the contents entirely), simply by adding :position => "bottom" to the code.
Except for some insignificant differences, the code does what your code does (in about 200 lines less).
Rasmus on :
Tomas Jogin on :
Rasmus on :
But this is getting a bit semantic. If you prefer writing Javascript in Ruby and that works for you, great. I'll stick to writing my Javascript in javascript.
John Wilger on :
:update => "modified",
:complete => visual_effect( :fade, 'modified', :duration => 2)
) %>
Rasmus,
The :update => "modified" portion is basically telling the page to replace the contents of any element on the page that has an id attribute of "modified" and replace its contents with what is returned from the AJAX call. There are other options you can provide that would allow you to add the new stuff either at the top, bottom, before or after the specified element.
I might suggest reading the documentation[1] before arguing that it can't be done.
[1] http://api.rubyonrails.org/classes/ActionView/Helpers/JavaScriptHelper.html
Rasmus on :
Anonymous on :
Rasmus on :
pwb on :
John on :
Noel Darlow on :
OK, apologies. You seemed to be rather sceptical about OOP frameworks. I'd go along with that for the most part. Just because it's object-oriented doesn't automatically make it good. It's hard to find well-written examples in php.
I'd agree with your point about templates (and html classes which emit br tags...) although I'd definitely have a whole pile of objects doing all the validating, data gathering, business logic etc prior to throwing a few vars at some simple php presentation code in the template.
I find I'm not sure exactly what you mean by "monolithic" controllers though. To an OOP bigot like myself, there's a choice between FrontController (single point of entry) or PageController (multiple points of entry) but I can't see any compelling reason to choose one over the other. FrontControllers can hand over to any number of objects to carry out the application controller logic for a given request (that's their only unique role otherwise carried out by apache) and PageControllers can extend a superclass which provides a single location for common processing tasks. Six of one, half a dozen of the other. I tend to prefer PageControllers but I think you can write a flexible FrontController design - and you can have more than one FC if need be.
Rasmus on :
So the gist here was small discrete controllers, clean HTML templates where the HTML looks like HTML and a clean and separate data model layer. The point wasn't so much to say all frameworks out there are bad or that OOP is bad, more to say that it isn't hard to build your own base that does just enough for your particular problem and uses whatever style of programming you are comfortable with instead of having to try to fit your problem into someone else's idea of how you should structure your code.
I was also hoping people would pick up on how the Javascript code manages to connect events to the various form elements without having all sorts of onclick/onmouseover stuff in the templates along with the various performance hints related to opcode caching and storing lists in shared memory.
James on :
Just a thought.
Rasmus on :
[Files *.inc]
deny from all
[/Files]
Replace []'s with angle-brackets. Silly comment system here doesn't let me put them in.
Naming them .php instead and letting people browse them directly can be a much bigger problem since they are now being executed out of context. So you need to either put your include files outside your docroot tree, or you need to block direct access to them with an Apache rule as above.
(and no, this rule is not in place on the talks server where this app is hosted, because the whole point here is to actually show the code ;)
remmelt on :
But I agree, I also always name my incluse files .inc.php, just to make sure.
gaetano Giunta on :
It is true that if every php coder had used inluded_file.inc.php from the beginning, this would not ever have been a problem...
Rasmus on :
ismael on :
Milhouse on :
Matt on :
Sadly, I feel confident in saying that the PHP frameworks that are here today, probably wont be in 2 years. That's what I'm getting burnt out on. One thing that I'd love to be able to do is come back to an application in 6 months, and know what the hellz is going on. I can say for sure that I wouldn't want to read about how the "framework" works. Ugh.
I've been trying out PHP frameworks for at least 3 years and now... I love to see all of the oop/framework purists squirm! PHP does a great job when used simply. Nice work.
Buddha443556 on :
Hope you post more articles, more often. The PHP community could use someone pushing simple, small and fast.
Douglas Clifton on :
Well said Buddha. If the Java vs. PHP argument means you have to turn your code into something that looks like Java, then you might as well use Java.
Xenon_54 on :
Now I changed my way of coding, puting the controller away and now I'm free to do more in less time. Maybe I'm not a super MVC guru but I'm free.
(If you find my comment quite unreadable, it's normal: I'm from Quebec)
gaetano Giunta on :
I think I can fully subscribe to the above post.
Hire me! ;)
I know OOP and MVC purists will frown at the code, but I really have a hard time 'getting' the benefit of turning all (and I mean all) functions into objects and adding layer upon layer of abstraction for the sake of 'architectural style'.
I my own experience, when a new coder is brought on the project 2 years after the initial one left, most of the time it will in fact be easier for him to grasp what a single function does and what it should not do than fully understand the delicacy of object manipulation - especially if the object coded by guy nr. 1 is poorly documented and makes a lot of assumptions about its usage patterns.
As for the framework craze, they generally have a very steep learning curve. The main benefit arises from the fact that you can expect the next-programmer-in-the-row to know the chosen framework and understand it better than the code you have developed in house.
Unfortunately, they come and go so quickly that it is more than likely that this will not happen.
My 2c.
gaetano
Lee Queen on :
That is what makes PHP a joy to code in. You are not hog-tied into doing things a certain way. If you want to make your application complicated by developing your own or using an existing framework, go ahead. More power to you. But I like to keep things as simple as possible. And I totally agree with Rasmus. HTML should look like HTML, Javascript like Javascript.
All I know is that, when I go back to work on code I wrote years ago and all I did was page-based development with the controller at the top of each view, it is fair easier to debug and maintain than the complicated mess you can get into by abstracting your application and splitting up all the elements that make up a request across a billion different files.
In my book, simplicity always wins out versus complicated.
ismael on :
True, the learning curve to most of frameworks may be steep, but once you really understand their structure (which is similar to most) it gets much easier to take someone else's work, just because you know where to find things. You know what to expect from the framework. It is not about piling up layer after layer for the sake of it. It is about a scalable structure. When everybody knows the structure it can only be a good thing, and that's what patterns are all about.
Lee Queen on :
Again, nice article, great way to break MVC down simply without all the objects.
Rasmus on :
Jonathan Aquino on :
Matthijs on :
Ok, this turned out to be more then one question, but I hope you get my point and shed some light on this.
Rasmus on :
Buddha443556 on :
Trying to keep file operations small and few has lead me to build the PHP files with only data and functions needed (I use a perl script to do this). This results in PHP files optimized on a per request basis but with the drawback of lots of code duplication on the production server. There other benefits when it comes testing, maintenance and code reuse. It has developed into metaprogramming.
Jules on :
Jules on :
http://www.web-site-build.com/no-framework-application-on-rails/
Luis on :
Rasmus on :
Don't get too depressed though, it's not a big hit, but if you are trying to squeeze every last bit of performance out of a system, this is one of the places where you can save a syscall. Have a read through this for some more APC optimization hints:
http://news.php.net/php.general/231433
Tim on :
If the file filex.php may be included more than once and there's seems to be no way to rewrite the code to avoid include_once then I use define('INC_FILE_X',true) at the top of filex.php
and instead of include_once
if(!defined('INC_FILE_X)) {
include 'filex.php';
}
Quicker than an open() syscall right?
Lee Queen on :
Mike on :
Junrey Beduya on :
There is a sample blog application along with it, to let you experiment with the feature. Though this is still in its pre-alpha release, but early evaluation will determine if it looks promising or not.
You might be interested in taking a look at phptarsier (http://www.sourceforge.net/projects/phptarsier/).
G.Hopper on :
- very easy installation ( No PEAR, just unzip, update config and ready to get coding )
- source code easy to understand
- clear and concise documentation
Lee Queen on :
I'm very curious to know whether you will stick with your current approach to building php apps (which I very much like by the way), or if you'll adopt the Zend Framework once it becomes more stable.
Rasmus on :
Lee Queen on :
Jianing on :
Rasmus on :
Jianing on :
Rasmus on :
You can simply make $dbh a protected static to force all instances to use the same database handle and then change all $this->dbh to self::$dbh throughout. No real point in making the methods static since you are still likely to carry some other info with each instance, but with a static database handle you get the single connection. I actually have it this way in CVS, but it isn't showing up here because of the annoying way CVS handles symlinks. I'll fix it.
Matthew on :
ra tion al (adj.) Devoid of all delusions save those of observation, experience and reflection.
The saddest part is that 90% of the comments are attacking the implementation rather than the concept. Who cares about the database? He could have used a flat file for all of the difference it would make to the concept. And if you can't realise that with all of your talk of abstraction being a key factor in a Framework, then you really are clueless.
At the start of the Article, Rasmus outlines his goals. The first question you should be asking as a supposedly intelligent person is "Did he reach his goals with his design?" I think he did.
The next question you should be asking yourself as a supposedly intelligent person is "Does he forward anything in this article that I can put in my own knowledge base and perhaps even apply to something I am doing?" If nothing else it got me to look at the Yahoo API and so the article was worth it.
To sum up, get over yourselves. Look at things with an open mind unless you feel you are the best programmer you can be. If you do feel you are the best you can be, send me your resume so that I can be sure NOT to hire you.
Leave debates over the mean of "is" to the philosophers and get real.
Matthew.
AJ on :
I have to say a lot of people have seemed to have missed the whole point of this. Its simply an example of how you could do things. But you are free to choose, you are after a php coder not a mindless sheep.
Personally I have some problems with certain frameworks, yest I do understand the need for them to be there. I think that a lot is to be said for coding standards and just because you don't use a frame work does not mean you can't avail of the reusability of desigining with OOP too.
All in all, I very rarely comment on PHP stuff cause there is someone out there whom always knows more, but seriously this article gives a very good example of how you can achieve your end goal relativly easily, yes you may very be able to pick holes, but that could apply to the framework your using too. In short, use what you think works for you and your client. But do document everything :) and if you can use a coding standard, its so worth it.
Jeff on :
Great article!
Thanks
Kevin on :
To my disappointment, the code with this article doesn't appear to be available. If the code is supposed to be in the IFRAMEs embedded in the article, then that is a disappointment too, since some of them are being rendered (and they appear to be broken at that), some are code, but its all presented ugly and unformatted (which I could deal with, but only part of the code is here).
Anyone else have these issues? Can the site be fixed? Could the code be available as a separate download?
You have me interested, but I still don't have enough info to see how much effort is involved vs. learning something like Zend.
Rasmus on :
Bihi on :
Lee Queen on :
I read this article back when it first came out and thought it was awesome. Because I had spent many months on researching "frameworks" that were mostly OO and a lot more complicated. After coming to the realization that, the apps that I wrote 5 years ago still work fine, and they don't use any framework, I wanted to forget about the whole framework crap and get back to improving my current apps in place.
Well shortly thereafter, the Zend Framework was released and I decided to try and learn that, since all I was hearing was it was going to be the way PHP apps are built in the future. Well, after many months of playing with it, and after deadlines for releases are passed without any releases, I have just about giving up on it totally.
Then, when I was cleaning out my bookmarks one day, I found the bookmark for this article. Very refreshing after all the months of time I wasted on trying to learn a framework, that I have decided to go back to what you originally suggested as an approach to MVC in this article. Heck, I don't even necessarily need MVC...I mean I work alone, don't have a team or anything, so I can pretty much do what I want. And what you suggest is so much easier and what i'm use to, than any of those "frameworks".
Coming from one person who has invested the time in learning numerous frameworks...I'm back to the real world, getting real work done! Thankfully.
One question I do have for you is this. When implementing authentication and authorization, how would you go about that in your suggested MVC approach?
Once a user is logged in, would you have a function run in each controller checking whether they are logged in, and then another function for authorization to check whether they are suppose to use that particular section of the app? Just curious. Thanks!
James Colton on :
Federico on :
Bart on :
Also try to benchmark code that uses a lot of objects and the same thing in procedural code. You'll be amazed in how many cases the procedural code will win cause no useless definitions were made.
Side note: Before you also start saying things like "You just don't know how to use OO", I do use OO; I just don't use it when its not needed. OO is great for handling connections.
Dityo Nurasto on :
PHP is the fastest development language I ever use, the simplicity of PHP make the programming could follow the logic easily. I always use the way you describe to specific environment, I don't want to add extra effort when I could develop an application with all built-in functions provided by PHP.
Mickaël Desfrenes on :
Jay Blanchard on :
The single most important point here is that you should make your code modular. In the simplest state this is a Model module, Controller module, and a View Module. My customer Model is different from my Shopping cart Model, so why would I want them both handled by a Master controller (regardless of OO or procedural)? For some apps this would be OK, but if you want scalable you must plan on implementing modular code. (BTW, RoR is probably not the most scalable -- handling thousands and thousands of requests per minute in a mission critical web application -- framework that I have ever seen but it sure works well for certain sites.)
Personally I find that too many of the frameworks add too much complication to an enterprise web app design. Within our team we have specified standards and that is the framework from which the team operates. Other than that they use MVC pretty much as Rasmus has described here. Sometimes the Controllers are a little too close to the views, but they are easy to maintain. You can completely re-design a View and Controller for a Model and pop it into place without much hassle...usually just by replacing 2 or 3 files. In the mega-Controller, unless you organized it really well, this would not be the case.
Another thing for me on the large Controller; if all of your applications users are hitting it all at nearly the same time you will suffer performance degredation, especially in the very large scale applications where 1000's of connections each minute are possible.
Ah well, just another rant for a Friday afternoon. As Einstein once said there is beauty and elegance in simplicity. Thanks Rasmus.
Ralph Dahlgren on :
I stumbled across this article while doing additional research on MVC and OOP while developing a plan of action for for a new CMS project which will supersede my current CMS, Etomite. I have to agree with several respondents in that many of the complaints here are not addressed to the original intent of the article.
I use PHP every day. Some days I need to use Javascript. These two languages are a drop in the bucket compared to the number of languages I have used over the years. I am currently debating the use of YUI as in integral part of the new code base for the new project. With all of the research I have done, and testing of software packages, I keep coming to the same conclusions. Some folks just like making their lives more complicated than they need to be. Some explanation is in order.
This whole idea of Web 2.0 (AJAX) is a prime example. Is it really anything new? Not really! It's simply finding new things to do with previously existing technologies.
Do these new features make developers lives easier? Not really, because you have to learn the new code library. Yet more syntax and semantics.
Same goes for frameworks that rely on too much OOP. Learning the framework, or the multitude of classes, can take longer, and cause more development problems, than simply writing good code. Sure, it all works well and looks real fancy once you know your way around and get all the bugs worked out. But it may have been a whole lot easier to just write good code using what you already know rather than thinking that new cryptic pseudo-languages are the answer to everyones problems.
Performance is yet another issue that needs to be considered more than it has been in recent months. All these supposedly new technologies, or the over-use of old technologies, has brought parts of the internet to its knees. And don't even get me going on the lack of compliant code being wretched out due to the use of these splendid ideas that people have. And, yes, even too much OOP will slow down your application performance, as well as development time line.
Sometimes it's better to use the KISS concept. Granted, there are times when a framework might make sense - but make sure you really need to go all out. Simply knowing something doesn't make it the right thing to do for every task.
Again, great article, Rasmus...
Zuhair Naqvi on :
I've been into web development for 7 years now, and currently managing quite large teams of developers and I have learn't the harder way that usage of OOPs where it is not needed has such an adverse effect on the developer productivity & code maintainability that it brings down PHP to the level of JAVA & .NET
One indicator that you are misusing OOP is when you write a class without considering a design pattern for it. Like, when you don't need an observer or factory or singleton etc. for a task, let it be procedural. Or, you will end up with code like $html->br() as Rasmus rightly mentioned.
Maximum conceptual integrity, maintainability, re-usability & simplicity can be achieved when you write classes with perception of writing re-usable tools for usage within your standards compliant procedural code.
Consider this procedural controller (generated by my MVC code generator) from the maintainability point of view:
include('init.inc');
include(LIBRARY . 'records.inc');
include(LIBRARY . 'authentication.inc');
include(LIBRARY . 'forms.inc');
include(LIBRARY . 'class.genericformvalidator.inc');
include('models/register.inc');
$action = $_REQUEST['action'];
InitModel();
switch($action)
{
case 'register':
$RegistrationInfo = ProcessRegistration();
if(count($RegistrationInfo) > 0)
{
if(ProcessLogin($RegistrationInfo['username'], $RegistrationInfo['password']))
{
header('Location: memberships.php');
}
}
break;
case 'failed':
DisplayErrors($_REQUEST['errors']);
include('views/register.phtml');
break;
default:
include('views/register.phtml');
}
Note the 'class.genericformvalidator.inc' in the includes, it is a class I wrote for form validation and is used for a form in register.phtml. Now why did I write only that in OO? because I wanted there to be one central validation engine which can be extended by custom validation classes. OOP is a great feature unless its used just for the heck of it.
Rasmus, this is an excellent article and I enjoyed reading it. Just ignore the object orient idiots, they will eventually learn the harder way that you were right.
Enzo on :
At first glance, it seems so. But instead of understanding code in one place, you have to go looking at code in a zillion different files all over the place. Presentation is in one file, db connection in another, validation in another, javscript somewhere else,etc. etc. You end up including a bunch of shit you'll never need....bloat, bloat, bloat.
How is this easier? You have to cd back four directory structures just to see where the validation is taking place? Then the frameworks always have to be extended and going through someone else's code and customizing it is always a pain in the ass.
I much prefer a lightweight framework as Rasmus is suggesting and separate a few things where necessary. But these frameworks are waaaay out of hand. Have you see the websites using some of them? Slooooow as hell.
$html->br() is classic crap you have to do in many of these frameworks.
Rex S. Sacayan on :
I prefer to make it simple and less code as much as possible.
Clean, secure and fast, that's it!
Great article Rasmus!
P Appleton on :
For a comparison my previous project was in ASP and the majority of the logic was held in single ASP pages, maintenance was a breeze and performance was good (the projects were functionally very similar).
As a comparison I would say a similar change would take 1 hour on the non framework system and about 1 day on the frameworked system. OK the frameworked system was allowed to get out of hand but when you get a number of people doing their own thing anything can happen.
I still think a framework/code generation can be a good thing but you have to watch out for the point where it becomes a hindrance – and it this point you are probably too reliant on it to back out or abandon it.
I think Rasmus has demonstrated a viable approach to flexible development philosophy and on a more careful read though of his example it looks even better. I will be trying out his methods and will try to sneak some code generation and framework advantages alongside it. Hey – I want my cake and eat it. The jury is still out for me. Great article thanks.
tonyb on :
Any PHP developers that have been doing PHP long enough has developed their own frameworks, of course they are not as abstracted or generalized as the big one (PhpCake, Symfony, Seagull), but they get the job done.
Agreeing with most of the other posters, frameworks abstract the development process so much , that you'll end spending more time configuring them, looking for file(s) and overall managing the development process instead of actually developing. the time savings in my opinion are negligible and the developer time lost in learning, training or retaining the framework structure is much greater than stock PHP.
When it's all said and done the bulk of your coding is in the logic , the controller piece, no framework in the world simpifies that. The only "big" framework I would consider is CodeIgniter, its the one the most closely resembles a grown up version from what Rasmus illustrates here.
Programming is hard enough why add more abstraction?
Cesar D. Rodas on :
nice post.
Nazz on :
Plus those frameworks are slow as a dog. No thanks. I stick to regular old php and a few common libraries.
Jusamus on :
Very nice article, refreshing, interesting and informative! Reminded me that I don't HAVE to choose a framework to be effective.
Robert on :
I don't know if you'll write the next edition of "Programming PHP" but personally I would like you to write it, maybe you can extend this no-framework in the book. Thanks for this great article!
vanderbilt on :
Michael on :
Thanks for the great article Rasmus.
retryistherealretry on :
Donatello on :
Dillon on :
devcodus on :
PHP vs ROR!!!
Guy Steuart on :
René on :
I really enjoyed reading.
I tried python, i tried ruby...
But I always got back to PHP.
I prefer clean, pure PHP code
using objects only where I really benefit from them.
I call my models components.
My pagecontrollers look like this:
manglio on :
i agree with you rasmus that many php frameworks give many tools that must be questioned as really useful,
but the truth is that frameworks provide a standard way to do things, instead of lots of antipatterns spead across the project , they help to keep order and mantain visibility (in a certain way)
...but the fact is that layer separation es quite useful,
ive worked in a project where all the code as mixed as a gigantic spaghetti and (we just received the design and it was already made that way) the debugging sessions were a nightmare......
keep going and good luck!!
Prakash Sankar on :
Very practical and without the scary abstractions of frameworks.
Jquery + PHP rocks
There is a PHP framework that uses this philosophy
http://code.google.com/p/simple-php-framework/
Thanks
Rainer on :
The Greyhound on :
It's illuminating to see how vitriolic some people are, tribal even. You'd think Rasmus was a blasphemer or something! I suggest everyone chills out. Take a slug of vodka or something, before you explode.
a on :
Ryan Cross on :
alphonse on :
In the case of CMS', someone came up to me and asked me if i could do his site using one of the leading open source CMS'. I told him, I had my own and would not delve into these open source CMS' (note: I have nothing against them, it's just that I find it easier to do the darn programming myself, especially when a job requires additional procedures. No hassle in skimming through other people's code (and other people's thinking), and no hassles in deployment.)
OOP has its benefits, I agree, but there is totally nothing that OOP can do that procedural, linear or what ever you call cannot do. Some people are fine with OOP, but I am sticking to where life is simpler. :)
Regards to all! and thanks Rasmus for the work you have done and for a fine article.
Zeeshan Arshad on :
It's better to stick to standard php and its techniques with custom libraries instead of trying foreign frameworks.
Doing something with instant knowledge is better and powerful than finding short cuts and put the things on the way without knowing the background.
retry on :
I've worked with ruby gems in the past, and pretty much every gem update broke something in the project that required debugging and work arounds. In the sysadmin role, I wanted to just pin gem versions, but you never know when you're dealing with a major security issue or just some random code updates.
PHP is not immune to this phenomena either. There are some PEAR libs that have done this...the HTML_QuickForm package comes to mind. The original is deprecated, but HTML_QuickForm2 is alpha code with NO help in upgrading.
Sometimes those initial conveniences can become long term serious problems.
Syed R Ahamed on :
Ed on :
Many many HUGE sites have been built around php and NO MVC framework (shock horror!)...
Lets be frank - we did well before them... and we'll do well even if we don't use them...
regards
s4l1h on :
Chris Griffin on :
Ultimately I took it as a a clean demonstration of square/rectangle logic to the Framework/MVC. A framework is [usually] an MVC but an MVC need not be a framework.
As apparent as it may be to most, I think it is enlightening to many that you can adhere to MVC patterns without doing so on top of a framework. This is very helpful for smaller 'shops' grappling with the decision to adopt a framework vs. just adopting more structured coding conventions.
This in no way defames the frameworks which are indispensable to many larger operations.
Joseph on :
Again, I love the idea.
John Kenwood on :
http://www.devlopr.com/do-frameworks-fail-when/
http://www.devlopr.com/after-all-what-is-this-no-framework/
http://www.devlopr.com/the-mystery-of-the-dead-project-%E2%80%93-exposed/
dSims on :
Does this mean you have your ini setting
filter.default "string"
Pete on :
As an anology, you wouldn't build a house from an outline of a skyscraper, well you could but people may think you're wasteful and a little crazy.
retry on :
Using php means you don't have to write a session handler from scratch or various other important features needed for real web development. Some languages can't do much web work without a framework. Ruby and python have zero built-ins for web dev and without a framework, you're rolling a lot of core stuff yourself. With php, that is not the case, and there is some memory overhead to loading all the core functions and classes. There are times I wish I could control what gets loaded in and what doesn't. It would be amazing if you could load everything in dev mode, run some analysis on required libs, and then explicitly declare your imports during production. How cool would that be? Oops I'm wandering...
Anyway, the framework approach is very anti-posix. The beauty and key to the *nix principle is having many small programs that do one thing just right and the ability to chain things together for power and flexibility. Frameworks take the monolithic approach, the end result being bloat and loss of control. It's the same thing I hear C++ guys say about scripting languages and even java. Tradeoffs. In a language like php, frameworks seem extravagant, unnecessary, and just plain restrictive. Better to take the piecemeal approach and include just what you need. This is what pear/pecl is all about. Unfortunately pear quality and maintainence has been quite poor to the point where I trust very few pear packages these days.
A php "framework" that lets you pick and choose components to use within your design structure rather than force structure upon you would suit php best. And isn't that pretty much what 3rd party libs were before the framework fad came about?
solostyle on :
I've been implementing my own mvc in php recently and stumbled upon this, which is in many ways similar to what I've been writing. I have a concern about the controller, which i am facing right now in my code.
When I look at your code, it seems like the real controller is in common.js's usage of event listeners in fancyItems(). Initial page load is controlled by the thing you call the controller: add_c. I understand that in conventional mvc definition, your add_c is the controller. But really add_c does nothing more than what back-end model should be doing anyway. The view never directly interacts with the model in your example; it interacts only with your add_c controller. So I consider add_c, what you call the controller, really a layer upon the model. The view is defined by your html, but more significantly it is defined by the javascript. AND, in my opinion, it looks like the javascript also functions as your controller. (My purist view thinks "controller" refers to the thing that processes user input and decides what to do with it, "view" referring to the output and how to serve it.)
Do you see it this way, too, or could you? In my application, I have taken to using asyc requests to update the view (and I use YUI 2.7), and my javascript ends up doing what you do--it passes some parameters and a resource (URL-type thing) to the back-end code, which figures out which back-end controller (similar to your add_c) and view (similar to your add.php) to give control to. I can see flaws with this.. it just doesn't seem as beautiful as I imagined my mvc would be. Of course, I am willing to incorporate some imperfections, but only if they are totally necessary.
I guess my concern is really highlighting a flaw I see in the way mvc is being defined/interpreted/implemented... Maybe it would be useful to separate client-side view and controller functionality. So the callback function fN() would be seen more as a view updater, the clickFormEvent() more as the controller. Or if not separate them, at least begin to think of them in this way, so that when the application is expanded and more things are added, you might consider more of a separation. Any thoughts?
Jose on :
I think that will make a lot of sense for PHP, I think like having a YUI library approach, but for PHP. That would be really cool!!
justpassingby on :
I think Ruby's Sinatra and PHP's rough equivalent - Fat-Free Framework (http://fatfree.sourceforge.net) - are both in the right direction. Programmer has freedom in how MVC design pattern should be separated, or even no separation at all. No clutter in directory structures. It implements the front controller design pattern just as well, so you have all routers/controllers in a single file. But even that is under your control.
All told, it could qualify as a no-framework framework. It's not the size, the speed, the OOP or procedural style employed, blah, blah, that matters. It's how elegant the code is, how the code is easily distributed across a team, and how usable it is.
Kirk Bushell on :
I think this kind of style and approach is great for simple projects, but the moment you need to implement themes, offer support for different storage mediums.etc, you suddenly feel "stuck" and have to start coding "around" the code, than with it.
My templating engine, PHLite (look it up on github), uses this philosophy of simplicity.
All in all, I think this post is a great one and definitely a great brain exercise. i.e. How CAN I make my code simpler?
Cheers =)
Neil Girardi on :
The responses are actually more interesting to me than the article itself, for they paint a vivid picture of the obstacles to team work faced by engineering departments. Programmers tend to be somewhat arrogant and fixate on proving that they know more than their peers. Most of the OOP vs. procedural comments and Rails vs. PHP comments have little to do with the article; they are merely attempts by "know-it-all" programmers to prove that they're more clever than Rasmus. If you want to be a great programmer and accomplish awesome things with your dev team you have to have maturity. You have to keep your ego in check and focus on working with your peers, not besting them.
My two cents...
-n