A Response To PHP- The Wrong Way
For anyone who isn’t aware, there is a site call http://phptherightway.com, which is a summary of good (dare I say, best?) practices for writing PHP in 2016.
In addition, there now exists http://phpthewrongway.com, whose aim is to provide a kind of counterbalance to http://phptherightway.com and what is presently mainstream PHP culture. This article is a rebuttal to the arguments found in http://phpthewrongway.com.
In general, PHP - The Wrong Way delivers three positive fundamental messages: keep things as simple as possible, don’t use/apply tools dogmatically, and write secure software by default. This is generally good advice, except PHP — The Wrong Way expresses much of its advice through a series of gross mischaracterizations of the PHP community, the nature of frameworks, standards, and PHP itself.
To set the tone, the article starts off with a hyperbolic hero image depicting literally nobody in the PHP community. I am a long-time, active member of the Reddit PHP community, and follow “the Laravel clique” on Twitter. I’ve yet to come across someone who behaves like that.
Following that image, we come to the second mischaracterization, aimed directly at http://phptherightway.com and the book Modern PHP
In the world of PHP programming a set of trends are massively being propagated by some people (in their books and on websites) as “Modern PHP” while all other approaches are frowned upon as backwards, stupid, or just plain wrong
These people seem to work tirelessly at getting other people to follow their way of doing things.
It’s unclear who “these people” are as the author never actually provides names or even examples of these propagations. It’s a nebulous complaint as abstract as the very abstractions lamented by the author further into the article.
This website has been created in an attempt to present a pragmatic view on PHP programming. A view dictated by experience and practical consequence rather than popular trends, theory, or academic dogma.
Given what has been stated thus far, I’m not so sure that this statement is sincere, as it already has the aroma of rant spiced with a dash of ad hominem. But the real issue here is how the author has implied that only the contents in PHP — The Wrong Way reflect a pragmatic, experienced, and practical view, and that its counterpart is nothing more than “popular trends, theory, or academic dogma”.
That said, let’s continue on to the meat of the document.
The wrong way: Religious following of rules and guidelines.
In all fairness, this is one of the most salient points of the whole article. Every construct in programming is a tool. Use the right tool for the right job and you’ll see the benefits. Use the wrong tool for the wrong job, and you won’t. Unfortunately, the rest of the article doesn’t really seem to follow this tempered approach to tool evaluation.
The wrong way: Always use a framework on top of PHP.
To arrive at the above conclusion, the author puts forth a series of irrational arguments and statements.
This trend has not emerged and become popular because it in any way improve the result of the developing process, or because it is the right thing to do from a technology and architectural point of view. This trend has become popular because some of the developers of frameworks has managed to sweep away the masses with their polemic against programming from the ground up with stanzas like “Don’t re-invent the wheel!” and “Don’t do it yourself, others are more skillful than you”.
Interesting accusation. Would love to see some evidence to back up the claim of the motivations of the framework developers. What’s even more interesting though, is you don’t see quotes like “Don’t re-invent the wheel!” and “Don’t do it yourself, others are more skillful than you” coming from the framework developers, you see them from a broad spectrum of non-framework developers in the community (one of the many mischaracterizations present in this article).
And the point these developers are making is simple: building stabled, tested, secure software is time consuming. Unless you’re learning, there’s little value to be had from building a clone of a battle tested thing that already exists.
Regardless, let’s talk about the idea of building things from scratch as a viable alternative to using a framework.
Naturally, which approach is best depends on the nature of the problem. Given the purpose of a web framework like Laravel or Symfony is to build websites and web applications, and given PHP’s raison d’être is building websites and web applications, we’ll go ahead and assume that web development is indeed the context in which the author is lamenting the using of web frameworks.
When building a website or web application, you tend to run into the same basic problems over and over again: you want pretty URLs, you don’t want to repeat the bootstraping of the same configuration for every view/endpoint of your app, you need some kind of input validation, and if you’re rendering HTML, some kind of very terse output escaping. You’ll also likely need a way to establish and manage sessions, protect all POST requests with CSRF tokens, manage cookies, and many other common problems inherent to web development.
In solving those problems in any reasonably maintainable, readable, and understandable way (e.g. not wet spaghetti), it is *inevitable* that you will end up writing your own abstractions to make things re-usable and organized. If you build enough web applications / websites, you find that you’re not only repeating yourself within a single project, but across projects. Being the “experienced, pragmatic, and practical” programmer that you are, you decide to extract those abstractions out into re-usable bits of code that are project agnostic — a.k.a. libraries. But after you’ve developed those libraries, you realize that you’re always importing the same sets of them over and over again, always setting up the same basic project structure over and over again, and wiring things together the same way over and over again. So you start writing a boilerplate backbone to save yourself some time — a thing that describes what libraries to include, and a basic, logical structure to get you started with. Sounds an awful lot like a framework: an opinionated assembly of libraries and boilerplate. So now you’re effectively doing three things: maintaining a set of libraries, a framework of those libraries for the types of projects you work on, and the actual projects themselves.
To go with a more accurate usage of the author’s carpenter analogy, imagine if you were asked to build a table, but before you build the table, you grew a forest, milled the rough lumber, and also built the table saw with which to mill that lumber. If you’re a better carpenter than you are a machinist or arborist, you just wind up with an unsafe, half-baked table saw and shitty trees, and it’s taken you a really long time to even get started on that table. If you are a first-class machinist and arborist, then you already know exactly how much work goes into growing a forest and building a high quality table saw, and realize it’s a ridiculous amount of effort when all you want to do is build a god damned table.
Thus an “experienced, pragmatic, and practical” carpenter would tell you to just buy pre-made, high quality tools and materials, and focus on getting the job done. I wonder if an “experienced, pragmatic, and practical” programmer might have similar advice when it comes to writing software…
The take-away here is that the author seems to have an odd perception of the purpose of a framework — it’s not a bag of random abstractions designed to trick you out of writing things from scratch yourself, it’s a tool meant to save considerable amounts of time. That’s its purpose — to save time. When used correctly, it will.
Many of todays programmers completely ignore the fundamental principles of sound programming
and they spend huge amount of time fantasying new layers of complexity in order to appear more clever, more cool, and more acceptable by whomever they regard as their peers.
These people seems to be infatuated by the though of having other people follow their “way of doing things”, becoming some kind of PHP community leaders, and having other people use their latest “hip” Open Source tools, that they forget to make sure that the advice they are giving is sound and solid
There we go with the mischaracterizations again. I can’t speak for anyone else, but I sure as hell don’t sit around my computer deliberately thinking of ways to make my code more complicated so that I can feel cooler than or superior to someone else. The only fantasizing going on here is coming from the author.
A framework is not just a collection of reusable code, you cannot simply take a piece of code from the framework and integrate it into your own project.
Patently false. Maybe at some point in the future I’ll take the time to post example repositories of how you can use just a tiny fraction of a monolithic framework in an existing project external to that framework. It would be silly to do it (which makes the author’s point rather moot, by the way), but you absolutely could.
A framework is a system that helps you build software, but at the same time it forces you to work within the limitations and restrictions of the framework itself. The framework itself has lot of interdependent functionality. One piece cannot work without the other
We’ll see this “limitations and restrictions” theme come up later, but it is a concept central to the author’s bias against frameworks (I say bias because the overall negativity, selective quoting, and lack of balance in the language surrounding the discussion of frameworks in the article highlights this bias quite strongly). The author does not actually enumerate these limitations and restrictions, just presumes they are there and then rests their argument against frameworks on that presumption.
In reality, at worst, a framework simply won’t provide a feature that meets your needs. “I need my web application to have an artificial intelligence”. Great, sounds like you’ll have to write one yourself, but the use of a framework certainly isn’t going to inhibit this effort.
In the world of Python and Ruby building websites from the ground up is tiresome because neither Python nor Ruby was originally created to build websites. As a result general purpose frameworks such as Django and Ruby on Rails quickly became popular for building websites in these languages.
The author fails to make a critical separation of two concepts: a standard library for working with the HTTP request/response cycle (which PHP has built in by default, but Python and Ruby don’t have as much support for in their standard libraries), and actual application architecture/abstraction around common web patterns. Django and Rails solve both problems for their respective languages, but PHP by default only solves the first problem, and does not have the same sufficient abstractions for common web patterns that Django and Rails developers enjoy. Hence why the PHP ecosystem also has frameworks. In other words, you can’t as easily do in plain PHP what you can in Django in Rails. They offer what the PHP standard library offers, and then some. Plain PHP and Django and Rails are categorically not equivalent.
As such PHP was, and still is, a framework in and of itself
By the author’s own definition of a framework, this is a dubious claim. PHP is a collection of libraries, but it makes precisely zero assumptions about how those should be used. Regardless, the point that’s implied is absurd. Because PHP itself is a “framework”, we don’t need higher level frameworks?
When you use a framework in PHP you add a layer of abstraction on top of yet another layer of abstraction, one that was already in place for you to use to begin with
The author simply takes it as a given that higher levels of abstraction are bad, and again, rests their argument on this assumption. You can do everything PHP does in C, but you use PHP because it makes those things easier. Similarly, you can do everything Laravel or Symfony do yourself, but Laravel and Symfony make those things easier. They offer an abstraction level designed to simplify very common patterns of web development which have emerged over the last several years, just as PHP offered a level of abstraction to simplify the problems Rasmus was facing in the early days of the web.
The added layer of abstraction that the framework provides may simply serve to organize your code into a pre-fixed set of patterns, or it may add even more complexity by intertwining hundreds or even thousands of classes and methods into a nightmare of dependencies…
The “nightmare of dependencies” part of that quote is particularly interesting. When I’m using Eloquent and want to save some changes to a user object, I just do this:
$user->email = ‘firstname.lastname@example.org’;
I’m not really seeing the nightmare of dependencies there. In fact, that’s the whole point of a framework — a tool that insulates me from that nightmare of dependencies that I would have to require and include myself if I wanted similar functionality. To work with data as PHP objects in my application, I don’t need to know a shred of the architecture that makes `$user->save();` possible. I just use it. And it just works. It’s not like I opened a closet and some avalanche of code buried me alive. So the author is effectively creating a straw man to support their argument. It’s simply not a reality that you have to deal with a “nightmare of dependencies” when you work with a framework (unless of course you have FUD about PSR standards, and don’t have Composer or an autoloader that makes code importing a breeze…)
…either way you’re adding layers of complexity to your code that isn’t needed!
This is the remainder of the above quote, which is quite telling: either way. In other words, frameworks are never needed because the code they add is too complex and is never needed?
The poor construction of the argument notwithstanding, the complaint about “layers of complexity” is unfounded. You are not directly exposed to the underlying complexity. In fact, you are only exposed to the public simplicity, just like you are with PHP and C.
Don’t believe me? Here’s the source for `strtotime()`: https://github.com/php/php-src/blob/master/ext/date/php_date.c#L1460
There’s a whole bunch of underlying complexity there which you are never exposed to. Instead, you only have to worry about calling `strtotime()` with the right argument. The complexity that makes `strtotime()` possible is literally none of your concern. If PHP itself is a framework as the author claims it to be, it’s interesting how they don’t consider the underlying complexity of C to be a problem when using PHP, but God forbid Laravel makes it easy to validate a form, and internally uses some dependencies to do so…
The more abstraction you use, the less efficient the interface becomes and the more error prone the application becomes. The higher abstraction, the more detail and efficiency, is lost.
Wat? The entire purpose of abstraction is to simplify the interface for working with or invoking complex behavior. What the author describes is the opposite of reality. The grain of truth the author may be looking for here is inappropriately applied abstraction causes unwanted fragility and can lead to a poor API, but they did not make such a distinction, only a blanket statement that all abstraction is bad.
Understand this clearly: The ideal number of lines of code in any project is as few as possible!
I’m just going to give the author the benefit of the doubt here and chalk this statement up to lazy writing, rather than poor reasoning. Instead, I’ll address what I assume is their intent: simpler is better. Yes, that’s true. Which is why using frameworks and libraries that simplify common, repetitive tasks, is a good thing…
Some companies began listening to the hype about PHP frameworks and they started their next projects using one of these popular general purpose frameworks only to end up in a disaster. They not only discovered that the general purpose framework was really bad at solving their very specific need, but it was also extremely slow in doing so. It was impossible to scale and as a result they started ripping the framework apart in a desperate attempt to pull out all those things they really didn’t need.
Wait, what? What companies? Author provides precisely zero examples to back up this claim. Regardless, the reasoning doesn’t track. The nature of a general purpose framework is to get out of the way, to NOT make assumptions that would interfere with the specific requirements of different projects. But in fact, the “general purpose frameworks” the author is alluding to are anything but: they are web frameworks (Remember? We talked about this already). People who have to build websites and web applications, have web development problems to solve: CSRF protection, input validation, etc etc (we covered this). Thus web frameworks help solve those specific, but common problems. Once you get past the common low-level stuff that those frameworks help you with, they make no meaningful assumptions about the types of application you will build with them. You can build everything from a simple CRUD gaming community with forums, tournaments and leaderboards, to a pure JSON API to power an insurance company’s claims system, to a Facebook or Twitter clone, to an e-commerce site that sells unwanted fried chicken. If a company finds that the “general purpose” framework they’ve chosen prohibits them from achieving their specific goals, then only one of two conclusions can be drawn: the framework is terrible and therefore not the right tool for the job, or the company is full of incompetent programmers. Since the popular frameworks the author is alluding to are not terrible, and most companies aren’t full of incompetent programmers, I’m inclined to believe the author is inventing problems out of thin air.
There is one point the author makes that has a smidgen of merit: scalability. It’s no secret that frameworks like Laravel, Symfony, and Zend are heavy, and tie up more system resources than say, a simple bare-bones script. However, I will let Anthony Ferrara explain the fallacy of prioritizing hardware costs over human costs, as he does a better job than I could. So while you should strive for performant code, and indeed a framework can be a hindrance to that quest, rolling your own custom-tailored internal framework/libraries doesn’t give you that performance for free. It costs time and money.
My company deliberately sheds our home-grown abstractions/libraries whenever a suitable 3rd party library or framework upgrade can replace them. It’s cheaper and easier for us to delegate that work to the framework/library maintainer than it is for us to try and maintain something on our own, even if our own version is slightly more optimal for our needs. Moreover, the standardization on an external tool makes it FAR easier for new developers to get up to speed, because we can hire for skills and experience that a developer can already have.
The business case for rolling your own framework/libraries manifests only in a thin set of circumstances where performance or uncommon needs are ultimately paramount. But as Anthony Ferrara pointed out, you probably don’t have the scalability issues you think you do, so optimize for fleshware, not hardware.
On to design patterns…
The wrong way: Thinking of patterns when solving problems.
As stated, the above is not necessarily bad per se. But once again, the supporting arguments that led to such a conclusion are…. off.
Object-oriented design patterns typically show relationships and interactions between classes or objects, without specifying the final application classes or objects that are involved.
PHP supports imperative, functional, object-oriented, procedural, and reflective paradigms. PHP is a huge toolbox with lots of different tools that makes it possible to solve many problems in many different ways — not just one way.
PHP is all about freedom, fast and scalable solutions, and having many different ways to deal with problems.
It’s unclear what the author’s line of reasoning here is. All of these different paradigms have their own design patterns. Functional programming has monads and currying, for example. Yet the author seems to be arguing against the use of design patterns as if they somehow only pertain to OO, and thus preclude other programming paradigms. This is simply not true.
Regardless, the conclusion drawn is at least sound: don’t apply design patterns for the sake of applying design patterns. Like anything else, they’re a tool. Use them appropriately or suffer the consequences.
The wrong way: Always use object-oriented programming.
The author gets back into referencing PHP’s different paradigms, and at this point it’s worth calling out a fallacy in thinking they’re equally viable.
For starters, I’m not sure what “reflective” programming is. It’s not a paradigm by any classical sense. Reflection is something you can do in PHP, but it’s not a paradigm for managing state and behavior.
Regarding procedural/imperative programming (procedural is just a form of imperative programming, and for the purposes of this argument, I will just refer to procedural as the paradigm) it is fundamentally harder to write complex software using purely procedural code, than OO or purely functional code. It’s a mix of global and semi-global state management, and function definitions without any of the advantages of encapsulation or polymorphism provided by functional or OO programming, or the advantages of a nominally typed object system to express complex objects and behaviors as named “bundles”.
It’s “simpler” in so far as you are working only with basic language constructs and primitives, rather than a potentially very complicated matrix of named objects, but it’s more complex because you have to work harder to maintain clear separations of concerns, and hide information from unwanted changes/mutations, or behavior from inappropriate access. This is fine for very simple projects, but anything complex benefits strongly from the extra structure that OO provides.
Where did `$foo` come from? How many times was it mutated before you got access to it? Which file supplied it? Which files mutated it? What about `bar()`? You might dig into one of those files, only to see that they too require their own files, and now you have a rabbit hole to explore. An IDE will only tell you some of what you need to know, but this very typical procedural code is much harder to grok than equally typical, modern OO PHP:
$user = new User('Bob', 34);
$user->getAge(); // 35
Where did `User` come from? the `Acme\User` class. Where is that? In the defined PSR-4 root folder, under `Acme/User.php`. Very clear 1–1 mapping of classes and files. Where did `$user` come from? From the instantiation, silly. It’s not global, it didn’t just appear out of thin air from some nested sequence of required files. Where are `sayName`, `haveBirthday`, and `getAge` defined? Obviously right in the `User` class, which you know the location of. Just open it and see them. Devil’s advocate: those methods could be located anywhere along a very deep inheritance hierarchy. But that’s why you shouldn’t write very deep inheritance hierarchies…
The kicker is that even simple OO usage doesn’t really cost anything. The tradeoff line where OO is too complex is incredibly small. FizzBuzz doesn’t benefit from OO, but representing a data structure as a named object instead of an associative array has immediate benefits. You are 100% in control of how much complexity you build into your OO design. OO itself does not inherently make something more complex, as this author naively states:
The fact is that so-called object-oriented programming as such often inflict a heavy burden of unneeded complexity!
You can just as easily over-complicate procedural programming or functional programming by adding unnecessary abstraction layers. OO doesn’t have a monopoly on over-complication.
Today one of the main strengths of PHP is it’s support for both imperative, functional, object-oriented, procedural, and reflective paradigms. PHP is a huge toolbox with lots of different tools that makes it possible to solve many problems in many different ways — not just one way!
As soon as we try to force-feed different problems within an application to a single specific programming paradigm, we’re not thinking creatively and we’re not working efficiently!
The author’s final point would be made more clear if they gave some real examples, specifically ones that show the link between consistent use of OOP and reduced creativity and efficiency.
Now, onto the FIG…
The wrong way: Following the PHP-FIG beyond the PSR-1 and PSR-2.
This is the least thoughtful section of the article. The entire first half of this section is just an ad hominem against FIG, trying to construct a boogeyman, and going so far as to say shit like this…
They do this by classifying the work of the PHP-FIG as “Modern PHP” in their books, on their websites, blog-posts, forums, etc., and by classifying other ways as backwards.
…without any examples or evidence.
Unrelated ad hominem aside, there are several arguments made in this section which don’t track:
One of the problems with the PHP-FIG is that even though many frameworks and Open Source projects has adopted several of their standards, these standards mainly deal with problems from a “framework perspective”...
Given the author has summarized anything beyond PSR-1 and PSR-2 (which are code style and formatting guidelines) as bad, and references PSR-0 and PSR-4 later on, let’s assume PSR-0/4 are included in the reference to “several of their standards”. Given that, this makes not a shred of sense. PSR-0/4 describe a standard by which code may be imported. In fact, it effectively describes a standard that makes code importing in PHP as seamless and simple as it is in Java, C#, and many other languages. Without PSR-0/4, this is what you have to do to use a class:
$instance = new myclass;
Except without standards, this is fraught with peril. The path could work in some places, but not in others since it’s relative, meaning libraries don’t have a standard way of determining where a file is located in the project structure. Different libraries and frameworks and users can require code in different places, and it’s not at all clear when or where a file may have been included in a request lifecycle, thus allowing the possibility of usage to come before inclusion, and a fatal runtime error to result. `require` could be called several times during the request lifecycle, eating unnecessary memory. It’s not guaranteed that `require_once` will be used everywhere there is a dependency on `myclass`. The class name could be completely different from the filename. Could be `my_class`, `MyClass`, `My_Class`. The file could even have more than one class in it. You can’t know how to use the contents of the file you’ve imported just by virtue of importing it. You HAVE to open the file and inspect it to see what’s going on. Even worse, that file might have dependencies it doesn’t `require` itself, and it expected those dependencies to have been loaded by something earlier in the request. So you have to just execute the request to see if it blows up. If it does, gotta dig into that required file to see what hidden dependencies it has. (None of this is FUD by the way; I used to live and breathe these very problems. It was a dark time.)
PSR-0/4 corrects this issue by describing conventions which allow PHP’s autoloader to work in a consistent way. Developers are all on the same page about where code physically lives in the project structure, and how to import that code where it’s needed. Importing always happens lazily at usage time (e.g. when `new` or a static call is made), and only has to happen once. Moreover, since the autoloaded path is derived from the namespace of the class, and the name itself, it simultaneously enforces namespaces (avoids collisions) AND consistency of the class name. When writing object-oriented code in PHP, this simplicity and standardized consistency eliminates entire classes of bugs and manifestations of complexity.
But the biggest advantage of all is that it has allowed two awesome things to happen: it’s allowed libraries to be consumed quickly and easily, dramatically increasing the re-usability of libraries that solve common problems, and it’s allowed Composer to be a thing. No more downloading a zip file that might contain some arbitrary file structure, manually extracting those files out into the right folder structure, and dealing with that one asshole who distributed his library as a .rar instead of .zip. And then if that library had dependencies, you had to hope the author included the right versions of those dependencies for you, and then you had to find a way to avoid collisions with different versions of that same file.
With Composer, it’s like any other sane language with a package manager: you simply type a command into your terminal, and it handles all of that installation process for you automatically. It puts the code in a consistent location, all dependencies are recursively found, and that code now just works.
composer require some/library
$foo = new Some\Library\Class;
Done. Simple. Easy. Painless.
Yet the author claims all of this library interoperability that PSR-0/4 allowed to flourish, is only a framework thing? I think not.
Ok, what about PSR-3? That describes a logger interface. What’s the advantage? If project A and project B both need a logger, and are written to accept the PSR-3 standard, guess what? They can use the exact same logger. It’s guaranteed to work in both. No adapting or re-implementing needs to be done. Frameworks have nothing to do with it. If you agree with the PSR-3 logger interface, then use it. If you don’t, then don’t (but don’t complain when you no longer want to maintain your home-grown logger and proprietary API and then can’t swap it out for a 3rd-party maintained, well-tested implementation).
… which renders them pretty unusable in many real-life industry situations
Such as? Not sure how being able to easily and quickly import new libraries, or adhere to a contract that lets you hot swap different implementations of a library, is “unusable in many real-life industry standards”. Once again, examples would go a long way.
If those supposed “real-life industry standards” are rooted in the old manual process I described above, it seems to me those standards are quite inferior to the new standards (hence why FIG exists, and “Modern PHP” and http://phptherightway.com were written), which save time and reduce complexity. If legacy standards are prohibiting a company from adopting new standards, then that’s some technical debt they will have to deal with, but for the author to imply that you should flat out avoid PSR-0/4/3/n because they are not compatible with antiquated or half-baked proprietary standards, is preposterous.
Many people develop software for the industry that has to be extremely efficient, secure, and cost-effective, software that customers are willing to buy and use. They cannot be bothered with standards that has to conform to the needs of framework fanatics. If they tried to be it would be a disaster for business.
As you can tell, I’m not really one to be dismissive (as per the length of this article), but the absurdity of this statement speaks for itself. It’s a non sequitur — applying PSR-0/4 (an agenda by framework fanatics) is incompatible with the goals of writing “efficient, secure, and cost-effective” software??
This is like saying the color blue is bad because it smells like the 21st of Jupiter
If you choose to adopt the standards developed by the PHP-FIG, you have to understand that some of these standards — such as the autoloader standards PSR-0 and PSR-4 and several other standards — has a direct effect upon how you code your software.
I should hope so, that’s the entire purpose of adopting those standards after all…
Many industries demand highly scalable, run-time critical, and cost effective software that simply cannot be developed using these standards of the PHP-FIG.
Again with the non sequitur. PSR-0/4 have nothing to do with ability or inability to write highly scalable, run-time critical, and cost effective software. It’s like saying you can’t cook potatoes if it’s raining out.
Examples of these supposed problems would go a long way in reinforcing the arguments the author is making.
The wrong way: Not developing secure software by default.
One of the few redeeming qualities of this article. [Oh but wait…]
At least PHP — The Right Way takes the time to provide concrete examples of how and what you should do, rather than un-actionable nebulous rantings about what you shouldn’t.