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.
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.
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.