I mostly work on legacy web applications in PHP + MySQL. Usually the original developer is not available, so I have to figure out the code so I can fix problems or add features. For a long time most of the PHP code I worked on was written in the classic PHP style: URLs for each page, PHP code and HTML (and Javascript) all jumbled together, business logic and presentation mixed in the same file. A few years ago I started to see more MVC-style code based on frameworks like Zend, Symfony, Laravel, CodeIgniter, etc. I thought this was a good thing, and that maintaining PHP code that was based on an MVC framework would be easier. My experience, however, has been just the opposite. The classic PHP style is easier for me to understand and refactor even when it has degraded into spaghetti code with PHP and HTML mixed together. It’s easier to work on classic PHP, even badly-written code, because everything you need to know to follow the request/response flow is in one place and reads top to bottom. By comparison trying to understand the thought process behind an MVC application, with the OOP baggage it usually entails, is an order of magnitude harder, and the MVC/OOP code is not necessarily higher quality, more maintainable, or more reliable.
MVC and web applications
MVC (model-view-controller) is a pattern or style of application design that dates back to 1970s and 80s. The idea is to enforce a separation of concerns, decoupling the user interface from the business logic and data persistence layer. MVC doesn’t require OOP (object-oriented programming), but in practice most MVC frameworks are implemented with classes and objects, because OOP can also support strict separation of concerns. There were MVC-style frameworks for PHP before Rails came along, but looking at the current popular PHP frameworks it’s obvious that Rails has inspired and influenced a lot of them.
MVC dates from the pre-web time of mainframes and dumb terminals, and long-running server processes that could update the user interface at any time. The web’s stateless request/response model meant giving up long-running server processes and dynamic interface updates. It’s common for Java- or .NET-based applications to maintain and coordinate state across requests and user sessions, and it’s now possible for the server to push updates to the client (web browser). That’s not how PHP works, though.
So-called MVC frameworks for web applications generally aren’t strictly MVC. Instead they use a router-handler-template-model style that superficially looks like MVC, but doesn’t have some of the important features of MVC. For example web framework “models” are usually dumb layers over a database to do CRUD (create, read, update, delete) operations. Business logic that probably belongs in the model is usually pushed into the controller, and models usually don’t notify the controller or view when something changes in the model. That doesn’t fit the CRUD paradigm or how request/response PHP applications work.
If you look at PHP MVC frameworks what you find are big class libraries that implement some flavor of MVC. The class libraries don’t model a business domain, so the business logic is forced into the framework’s structure, rather than the framework supporting the business logic. Trying to figure out where the business logic should go—controller? model? view?—is a big source of confusion, with no clear right answer. If the main advantage of using a framework is starting with a structure to hang the business logic on, why is it so hard to figure out where that business logic should go?
My experience with PHP frameworks
I’ve used several PHP frameworks in my own projects, and encountered them in code that I’ve taken on for maintenance and enhancement. I’ve written my own PHP framework that is deployed in a couple of real web applications. In every application I’ve run into the same thing: business logic scattered around at all levels, and difficulty figuring out exactly where something happens. Separation of concerns shouldn’t mean fragmentation of concerns, but that’s often what happens.
Real example: Where exactly should I put the business rule that says my client doesn’t accept American Express cards at checkout? In the template, where there’s a Javascript function to checksum the card number and figure out the card type? The template validates things like required entries and which countries my client does business in—why not which credit cards they accept? Or does this rule properly go in the controller that accepts the card number the user entered? Does obtaining an authorization from the merchant processor through an external service API belong in a controller or a model? What if my client decides they will accept AMEX for purchases over a certain amount? Even simple real-world business logic like this doesn’t always have an obvious right place, or it may have to go in several places for usability reasons (or to avoid submitting transactions that the processor will reject, because there’s a penalty for doing that too often).
Another real example: I inherited a web app that displayed a single page of Instagram and Twitter posts matching a handful of hashtags and authors. The previous developer (whom my client had fired after the application was delivered very late and buggy) had chosen to use the Zend Framework 2 for what seems like a fairly simple task. The code was classic OOP ravioli code: many classes with methods doing very granular operations. It was hard to figure out where anything happened because the overall control flow was not apparent, and state was scattered among many objects that had implicit relationships. All of this was wedged into ZF2 for no clear reason. This was tens of thousands of lines of opaque code that didn’t work. I replaced it with a lighter-weight page that just did what it was supposed to do: call the Instagram and Twitter APIs, provide some simple caching, and generate a page of posts. Adding authors or updating the hashtag list turned into simple one-line changes in a config file. The result was less than 1,000 lines, including comments.
This doesn’t mean that every application based on a PHP framework is poorly written or hard to maintain. It means that I’ve worked on a lot of framework-based applications and I’ve seen the same problems again and again.
Is everyone doing it wrong?
Before I started working on web applications I spent over ten years developing and working on OOP enterprise and educational applications, mostly in C++. When these projects went into the weeds or failed, or turned out to be very hard to maintain and extend, the programmers would always blame it on a poorly-designed class structure. If we (or the original developers) could just model the domain correctly in classes and objects everything would be great. The OOP paradigm was never doubted; it’s just that most programmers apparently are too inexperienced or lazy to do it right.
Read the threads online where someone asks how to implement something “correctly” in their PHP framework. If you put any business logic in the template (or view) you are doing it wrong. If you emit HTML from the controller you are doing it wrong. If the model “knows” about user interactions you are doing it wrong. Do business rules that affect presentation, like shipping options based on order total and destination, go in the presentation layer? Do external services like credit card processing or calculating shipping charges go into the model or into the controller? How do I know when I’m doing it wrong when there’s no agreement on how to do it right?
Programming is subject to fashion and religion, based on opinion, faith, and fear of looking stupid or inexperienced. Pick a programming topic and you can read all day about the right way to do it, or a criticism of a supposedly wrong way. These arguments seldom have any foundation based on facts or studies—they are anecdotal and biased. Programmers can be just as certain that their favorite approach is the one right way as they are about spaces versus tabs in source code, with exactly the same facts to base their arguments on: none. The only time I take the right way/wrong way discussions seriously is when they are about algorithms or relational databases, because there’s an actual theoretical and mathematical foundation to base opinions on.
My return to the “mullet style”
Recently I took on a ground-up development project, something I rarely do. Originally it was a debugging job, but the WordPress-based application was such a mess that I had to tell my client they’d be better off starting over, and I don’t tell clients that very often. I decided to revisit my simple MVC framework and improve it with the latest PHP features. I had a single entry to the application to handle global configuration, class autoloader, security, session management, and request routing. I had models that did actual business logic operations on the database (not just CRUD). I had controllers that would process inputs, update the models, and set up variables for the templates. And I had templates that could only contain simple PHP if/then/else and loops. Referring to models or knowing about business rules implemented elsewhere was not allowed. I tried hard to work within the framework and the rules I had made for myself. I refactored the framework two or three times to implement pure solutions to problems I ran into. I had over 7,000 lines of PHP code, a lot of it framework boilerplate and moving data around to enforce separation. It worked, but I was unhappy with it.
I looked at other frameworks, assuming I was doing it wrong, but my architecture was very close to the other leading frameworks. I got some good ideas but never shook the feeling that I was trying too hard to force the application into the framework, and spending a lot of time enforcing what I thought was the right way (or at least not the very wrong way that would invite abuse on StackOverflow and github).
What finally pushed me to abandon the framework approach was realizing that I was flipping through too many files to follow the flow of control, and that too much of my code was there only to comply with the framework’s separation of concerns. Having business logic scattered around in multiple files and classes is painful because I work on a 13″ laptop with vim. Maybe MVC frameworks are easier to work with on multiple 23″ screens, but on a small screen the buffer changes and screen splits cause too many mental context switches. I wanted to see everything that was happening on a single page, so I could keep it in my head and think through it.
Now I have under 4,000 lines of PHP written in a more readable and, I think, maintainable style. URLs map to individual files (with some simple Apache rewriting to support pretty URLs like /email/inbox mapping to email_inbox.php). Every PHP file that corresponds to an HTTP request is written in the classic PHP “mullet” style: business in the front, party in the back. Request handling and business logic, including querying/updating the database through the models, is at the top of the file, followed by HTML that can only use variables defined in the same file and a handful of global utility functions. There’s no embedded SQL or database operations—that’s still in the models, and once the boundary is crossed from PHP to HTML there’s no reference to model functions or any embedded blocks of PHP code. It’s now easy to find where every request is handled and to follow it through to the response (HTML output) in a single file. Common PHP functions and HTML code (page headers and footer, style sheets, Javascript) are in separate files. There’s no more boilerplate, and no more long blocks of code that simply copy validated user inputs or a database query result into a structure to pass to a template renderer.
Of course this is all just my opinion, based on my own experience, biases, and work style. If PHP MVC works for you, great, but you may be surprised to find it’s not as robust or maintainable as you think it is. There’s still no silver bullet when it comes to software development.
Postscript: Don’t throw the baby out with the bath water
I’m not advocating writing spaghetti code or ignoring good programming techniques. It’s possible to write good or bad PHP code whether you use a framework or not. Keep in mind that the popular frameworks do some useful things for you that you will have to do yourself if you don’t use a framework. You have to protect your application from common attacks: SQL injection, XSS (cross-site scripting), CSRF (cross-site request forgery), session hijacking, cookie tampering, insecure API endpoints and AJAX functions, etc. Learn about these attacks and use PHP’s built-in functions or a library you trust to protect your application. Treat all inputs as suspect, whether they come from an HTTP request, a cookie, a database query, or an external service. Use a database library such as PDO, never interpolate variables into a SQL statement. Sanitize everything that is output as HTML or JSON. Make sure your web server blocks attempts to access git or svn repositories, or anything else in your document root that shouldn’t be accessed. Build in error reporting and logging from the beginning. Don’t use global variables. Keep it clean and simple, err on the side of caution.
Selected comments
Cindy on 22 March 2015 at 8:58 am
Overarchitecturing makes things a lot harder to understand in my experience too. I thought I was crazy for wanting to remove layers most of the time. I work in ASP.NET MVC these days, and just a controller class, the Razor template and and class to send data between the two makes for code that’s easier to understand and maintain in my opinion.
John on 23 March 2015 at 6:38 am
I enjoyed the 2nd last paragraph before the Postscript. Have you considered to make a diagram of that?
Also I’m astonished when I see all this Java overarchitecturing coming to JavaScript. We seem to go from IE6-days JS to heavy frameworks that go out of control eg. AngularJS.
Regarding changes that affect both client and server-side code, I’d go ideally for a globally scoped config file that is read for both purposes.
Greg Jorgensen on 23 March 2015 at 11:43 am
@John A lot of this applies to Javascript as well, thanks for pointing that out.
I don’t have a diagram or any code samples. I assumed that anyone who has worked with PHP has seen what I call the “classic” style, but maybe there are people new to PHP who have only seen the MVC framework bloat and think that’s the only way to do things.
Over-architecting is an age-old problem in programming, and it’s a problem that programmers mostly bring on themselves. It’s not unusual at all for me to get involved in projects where the programmers spend more time solving technical problems they created, or dealing with excessive complexity in the programming and build environment, than they spend solving business problems.
Tom on 8 April 2015 at 12:47 pm
Great post. I think about this all the time especially the part about where the business logic goes with current MVC frameworks.
Dale on 10 April 2015 at 8:32 am
Finally! I’ve been waiting for an article like this for a long time. I started to dig into MVC world about 2 years ago and I don’t think I’ve ever really understood the hype surrounding it…(im referring to the PHP community) I do agree that the design principles surrounding MVC are important… however, everytime I try out a new framework I find myself spending countless hours researching how to use it / how to extend it or how to modify it and I usually just drop it and move on to the next framework. I sometimes feel that that doing things the “right” way turns into some pretentious rambling just so one can feel like their part of the new “cool kids mvc crowd”. I ask myself countless times “is this really all that necessary?” when trying to use a php framework. “Do I really need to work out all of the Service Container bullsh!t? Where the f**k should I instantiate this class?? Oops I mean ‘service’….” … by the time I figure all of this out I could’ve read the entire encyclopedia brittanica…twice!! Oh and not to mention all the hip cool terminologies that get used… annotations, migrations, depency injection, ioc container, interfaces, service providers…Give me a break! I just want to start building my site now please? ! Anyways… rant over… once again… thank u for the article… a breath of fresh air!
trustradius.com on 3 November 2016 at 8:19 am
I was just thinking this morning that I cannot believe I haven’t seen the last Harry Potter movie yet! I’ve heard good things about it, so I’ll have to watch it soon. I also want fro-yo now after reading this post. I wish that I had a place nearby with dairy-free options other than sorbet.
BobT on 17 April 2015 at 3:08 pm
I couldn’t agree with this more. I am a freelance developer myself, and I have built and maintained several (decent sized) apps from the ground up in PHP (intricate commerce, custom CMS, etc.).
Given many years of experience working with an approach similar to yours while trying to use various MVC frameworks has led me to completely agree with you. I thought I was doing it wrong at times. Turns out I wasn’t.
I much prefer straight-forward, easy to follow and read code to be much more maintainable. It’s solid & secure OO code which is good for the client and good for the developer.
That isn’t to say it’s okay to throw “modern” coding practices out the window, but to use what makes sense where it makes sense.
Thanks for the great post!
Noah on 21 April 2015 at 7:35 am
“Ravioli code” as the opposite extreme of spaghetti code – brilliant. Let me also contribute “lasagna code” – layer after layer after layer after unnecessary layer, once again to support “separation of concerns” as an end rather than a means. And then there’s lasagna made out of layers of ravioli – not some new super unhealthy dish from Cheesecake Factory, but the actual state of a large majority of architectures, for web apps and beyond. (As a former lower-stack Java EE guy I can attest to this.)
That’s just the beginning of what I like about this piece. I have been a silent reader of your blog since late 2013, and this is one of your best.
Greg Jorgensen on 22 April 2015 at 2:00 am
@BobT and @Noah — Thanks!
Karen on 18 May 2015 at 4:22 pm
I have been trying to get my head around all these “new” ideas in programming after decades of coding procedural style in a dozen or so languages, including almost 15 years of writing PHP spaghetti code that I understand. I was told by all the smart people that I not only needed to do OOP, and not only MVC, but should use a framework to force myself to “do it right”. I can see some advantages to the new ideas, but I just haven’t been able to get my brain to think that way naturally – your comment about where the heck to put which piece of business logic rings so true! I thought I was just a hopelessly inept dinosaur who had done too much hand-coding in my formative years, until I read your article. Thank you for courageously challenging the assumptions – it’s a breath of fresh air. It might still be that my main big DB interface should still be refactored in MVC and even a framework, but I don’t feel like a dunce for thinking that the old way is in many respects easier/simpler.
Ahmed M on 8 February 2017 at 1:50 pm
QUOTING: “Now I have under 4,000 lines of PHP written in a more readable and, I think, maintainable style. URLs map to individual files (with some simple Apache rewriting to support pretty URLs like /email/inbox mapping to email_inbox.php). Every PHP file that corresponds to an HTTP request is written in the classic PHP “mullet” style: business in the front, party in the back. Request handling and business logic, including querying/updating the database through the models, is at the top of the file, followed by HTML that can only use variables defined in the same file and a handful of global utility functions.”
That is a breath of fresh air finding someone as successful and talented as you sharing my same thoughts about php MVC frameworks. My freelance jobs are usually small/medium ones, and I hate being ‘forced’ to do it the ‘correct MVC’ way. I tried CI and Laravel and I genuinely believe that using these frameworks results in more confusion and spaghetti code than the good old, straightforward OO code just as you said.
Thanks for a great post, and a boost of confidence in a time I started to doubt my ability for not being able to get comfortable using a MVC framework.
Juris Trusevičs on 26 March 2017 at 5:56 am
Wow,
I’ve been thinking about all this for 5 years at least and all this time I just hated the idea about being forced to learn Laravel ,Yii or whatever crap they have declared to be the best MVC ever. Today I still don’t care how this Laracrap, Symphony or Ci works, I decided to create my own simple MVC framework without things like ORM, ROUTING and DEPENDENCY INJECTION CONTAINERS. I feel like my “framework” should take less than ~200 lines of code. And if a VIEW code inside a controller makes my happy , without doubt I will put it there. And Routing is Apache/Nginx problem , not mine.
Thanx a lot for your article!