PHP the Wrong Way
phpthewrongway.com
phpthewrongway.com
I hate working on codebases that have been built without a framework. There's rarely any proper organisation of code. The quality of the code that the developer has rolled themselves is usually poor. At least if folks use something like Symfony or Laravel it's harder for them to shoot their foot off.
Throw in things like caching, background jobs, notifications, scheduled tasks etc., and a general purpose framework is often a very good choice—especially for a language like PHP which does not handle asynchronous operations nicely.
Could I write my own versions of that stuff that only does exactly what I need? Sure, but why would I? It would take valuable time away from actually solving the problem at hand, and I can guarantee what I write wouldn't be as good as the stuff hundreds of people have worked on for several years.
- "actually, authentication is so important that it should probably live in your application's git diffs" and
- "glue code isn't as bad as I was led to believe -- it's nicer to read a simple application-specific implementation than have to dig into a framework's hyper-abstracted, generalized, one-size-fits-all, complex implementation when things go wrong".
- I trust Laravel's auth code more than I would trust my own. Maybe that's naive, and it's probably an indictment of my own abilities, but the framework has hundreds of thousands of users and active support, while I am certainly no security expert.
- For a lot of the things I've listed, an application-specific implementation would be far from simple. You can certainly make the case that debugging something like background jobs is harder because Laravel supports multiple different queue drivers and does have to be very generalised (though in practice I find it's a very understandable codebase), but I will happily cede some control to not have to worry about that stuff myself, much in the same way I don't want to be writing my own database adaptor.
For what it's worth, I don't work on big systems. We mostly produce bespoke back-of-house systems that small to medium-sized companies use to manage their operations. I am well aware my priorities will not align with those who work on more important things.
> while I am certainly no security expert
While this is the circulated wisdom, I would respond:
1. This idea keeps us from ever learning how authentication works. "It's surely too hard". But by delegating it to complicated framework abstractions, we tend to guarantee that we never know how our app's authentication works.
2. Authentication can be surprisingly simple. Seeing it in the code is a feature. Maybe, "oh, it's just res.cookie('token', securelyRandomNumber())" vs. "hmm, I hope someone writes a blog post about how Ruby on Rails' Devise gem works!" or "I hope the framework's authentication wiki pages answer my exact question!"
Frameworks sometimes make simple things complicated. I remember a common Rails/CodeIgniter question was how to redirect the user to the page they were viewing once they log in. That should be trivial. I think Devise eventually got a "redirectAfterLogin = true" config option, but that's kind of my point -- "I hope the framework thinks of my use-case" can be an unpleasant game to play.
Of course, there are always trade-offs. I reckon I've simply been burned enough times, perhaps more than you, to prefer erring on the other side for a change. After after 10 years I do prefer it.
I can imagine truly horrific bespoke hand-rolled frameworks that you probably have in mind. I won't defend those, haha. This is probably one of those things where your preferences are determined by what just happened to burn you the most so far which is most likely a coin flip.
I don't disagree though; having tons of stuff available in the framework does give less incentive to learn how it all actually works, and I do worry that my base programming skills may atrophy (though for me this is more because a large portion of my workload is fairly uninteresting CRUD stuff).
> I can imagine truly horrific bespoke hand-rolled frameworks that you probably have in mind. I won't defend those, haha. This is probably one of those things where your preferences are determined by what just happened to burn you the most so far which is most likely a coin flip.
Agreed completely. My personal experience with PHP development has been 50% an awful undocumented "framework" written by a guy who no longer works at the company, and 50% Laravel on PHP >= 7. So yeah, I suppose it should be no surprise that I like Laravel!
I've worked with engineers who hated frameworks. They saw them as overhead imposed by force on everything they did that didn't know or care what they actually needed. Surely it was better and simpler to compose libraries to do what they needed, as they found they needed it.
Ultimately, they failed to understand that they needed CSRF protection, security headers, input validation, any kind of meaningful authentication or authorization framework, and so on. In their arrogance, they assumed their insight into their needs complete and total.
I can definitely say that's been correct in a few cases. I'll be damned if it doesn't normally make it really easy to switch out the DBMS though (not that this portability is exclusive to an ORM!).
When choosing to use an ORM i think portability should be very low in the list of benefits.
If someone codes big projects like in the Symfony tutorial, he's going to have a bad time.
I'd like to consider giving Symfony or Laravel a try, but at work we run Debian and stay with a given release until it is near the end of extended long term support.
Laravel's oldest release that is still supported is 5.5 LTS, which requires PHP >= 7.0.0. The next oldest still supported is 6 LTS, requiring at least 7.2.0.
We're on Debian 8, Jessie, which is on extended long term support until 2022. It has PHP 5.6.29, so no Laravel for us. Even if we were on 9, Stretch, which has PHP 7.0 and so could use Laravel 5.5 now, Stretch is on long term support until 2022 and will be on extended LTS for probably a couple years after that, we'd run past Laravel's LTS.
Heck, even if we were on the current Debian, which is 10, Buster, which has PHP 7.3, it is not clear we could use Laravel. Laravel only does security fixes for 3 years on LTS releases. Buster has Debian support until 2024, LTS for a couple years after that, and then extended LTS for a couple year, so it will be past the end of security patches for the current Laravel before it even reaches LTS.
So unless I can be sure that the next LTS release of Laravel does not raise the PHP requirement past 7.3, Laravel is out of the running.
(But wait...what about https://packages.sury.org/php/ which is a package repository maintained by one of the main Debian PHP packagers where he packages newer PHP for older Debian? That's close enough to the official packages that I could probably convince the IT folk to add it. Alas, he only packages for older Debian until they go out of LTS. If you stick with it through extended LTS, you are out of luck).
With Symfony, 3.4 is the oldest supported release. That requires at least PHP 5.5.9, so will work on Jessie or later. However, 3.4 support ends in a couple months. That will leave all supported Symfony requiring at least 7.1.3 which means you have to be on Buster. It looks like they have a similarly short LTS support life as Laravel, so even if you are on Buster today, there doesn't seem to be any good reason to believe that there will still be a supported Symfony that works there at the end of its regular support lifetime, let alone that one that can take you into Buster's LTS or extended LTS.
Debian is the only Linux distribution for which I've looked at this in detail, but a brief look at others suggests that the situation is similar for Red Hat, and maybe Ubuntu.
See https://security-tracker.debian.org/tracker/source-package/p... for a list of all Debian's PHP 5 patches. The latest dozen or so are all less than a year old.
I understand the logic behind running Debian until the end of extended LTS, but it's going to have a number of drawbacks. Not being able to use a supported version of Laravel is probably not the biggest one either.
It's not a tradeoff that would be viable for my team, at any rate.
That's actually one of the main points of an LTS distribution. They run the same version of languages, databases, web servers, firewalls, and everything else at the end that they did at the start, so that you get a stable platform throughout their lifetime.
If you go down the road of updating individual components to something significantly newer than what your distribution supports you can easily end up breaking other things--so then those need to be updated too, which might break more.
Even if that doesn't break anything else (and PHP would probably not...offhand I can't think of anything that I need in Debian that depends on PHP so switching to a later PHP would probably be safe on that front), it leaves you with software that isn't covered by your distribution's regular updates. For each such thing you install, you've got to put in place some way to find when the upstream has a bug fix or security fix you need, and some way to get and apply that.
Maybe you should suggest to management that instead of staying till end of life you stay till one-half end of life, that way you still keep somewhat modern stacks.
Maybe only python2 will match your expectations.
> The wrong way: Always use a framework on top of PHP.
If a dev is very knowledgeable with e.g. laravel, then let him use this framework as he wishes.
> The wrong way: The religious following of rules and guidelines.
If a dev team commits on a set of rules and guidelines and enforce them throughout all their projects, then let them do so.
> The wrong way: Always use object-oriented programming.
If a developer only knows how to program with this kind of programming paradigm (or chooses so), then this is fine too. Don't force him to learn other paradigms. There is no "wrong way" and no "right way" because programming is not black or white.
its when you force that mentality on others because you don't know how todo it a different way that's wrong
e.g. If I only use Laravel why should my decision of only using a larval dictate that the next project has to be done in laravel, it might be something that can be done in 50 lines of code
But I only use laravel, there for you have to use laravel to solve the problem
All decisions you make might be forceful for others in collaborative work. It's also forceful when you have to work on a project that does not use any framework.
Ok, I don't know if I would want a developer that won't learn or can't be forced to.
> The danger of extremism
And then in the very next section makes this statement:
> Many of today’s programmers completely ignore the fundamental principles of sound programming and they spend a large amount of time fantasizing new layers of complexity in order to appear more clever, more cool, and more acceptable by whomever they regard as their peers.
A 100% completely accurate view. Nope, the author is definitely not an extremist.
Currently I am using homemade sort-of-framework for my pet projects. It has one big advantage - there is no need to learn API from external documentation as I am building it along my needs. I also learn a lot about reinventing wheels.
I disagree, unless you consider using a language to be inherently creating a framework.
Frameworks are generalizations, intended for flexibility. Their abstractions exist, typically, to shield a developer from the lower level stuff.
If you're writing plain ol language of choice code to do a specific thing, it's none of that. People can look at your code and see, oh, he's using built in cookie management and headers and standard language database calls.
It's readable, non abstracted code.
So a good programmer will start to abstract those things, and at that point you're going down the road of creating your own framework.
I think you're defining it way too broadly.
When I started learning PHP, I approached it the same way I do with every new language, I add a second file and use it for generic functions. I do it the native way inline as part of the main program flow, and then whilst ad-hoc refactoring (as I learn the language a bit better) I break out repeated chunks of code as a callable function and put in the second source file. I then re-use that second file for my next app in that language. Over time, I end up with my own personal framework. If I realise that I have a complicated function in my framework for which an inbuilt function or command exists (that I wasn't originally aware of) I'll refactor again and remove that function from my framework.
Doing it this way just made sense to me. The first time out with a new language is quite slow, but once I know what I'm doing, I have a bunch of useful functions on tap for each new project. It's a massive time-saver and conforms to the DRY principle.
Is what I'm doing 'a framework' really? I don't know, but to me its my workflow and I like it.
I also write PHP in a 'code behind' sort of way, with all HTML in separate template files, but I think that's a step too far for most PHP people ;)
Tbh, I simplified slightly - depending on the language, I can have many support source files full of functions, separated based on function type - so networking, IO, calcs etc.
For what I do, throwing up Slim, Guzzle, and Doctrine works well. You could make the argument that any one of those individually is a "framework", or that collectively I've made a new framework in 3 lines of composer.json by combining them. Point is many (and the article suggests) see framework or no framework as some massive all-or-nothing choice, when picking the parts you want, even after you've built your system out, us also an option.
Use what you need to use, in a way that makes the code easier to read, shorter, etc. A simple brochure site with little in the way of interactivity doesn't need to be built in React or Angular. A one page portfolio site doesn't need Bootstrap, and even many bigger ones don't need to import the entire framework.
What's best practice for Google and what's best practice for a small widget manufacturer in the middle of nowhere are likely not the same.
That's basically the lesson behind all of these points to be honest. Use what's right for the project instead of taking a list of rules as gospel and overcomplicating things to hell.
A framework always feels silly when you're just doing some fairly simple thing once. But if you anticipate doing similar things tens or hundreds of times, it is usually worth the cost up front to avoid reinventing wheels later.
Genuinely curious of real world examples of this.
I don't treat PHP-FIG's word as dogma; But to my mind several of the PSRs that came from them enable scalable, cost effective software to be written.
Case in point: Magento2.
Developers for this in the Anglosphere have found better jobs so it now becomes a matter of outsourcing to other not-so-first-world places to get work done. If only they had kept it simple and not gone for every framework imaginable then it might be something that people not desperate for money would want to work with.
I am involved in a legacy PHP project from the early 2000s. The code is as you would expect, maybe a little bit better because the programmers involved were pretty capable, but it is code visibly evolved by first targeting PHP 4 and only then 5. For quite some time I thought that modernizing it thoroughly would be the way to go, just that it'd be too much work. Then in my day job I got confronted with a modern PHP project. Using a framework of course, written like it was Java. It was horrible. Abstractions everywhere, impossible to follow the code without getting into the details of the framework, thus very hard to make changes. That experience confirmed the decision we made with the other PHP project of only modernizing it carefully, putting the HTML in a view layer and stuff like that, but staying away from the framework trend.
So this article seems spot on to me and something that can give useful input to other programmers.
It was just a ridiculously confused "MVP" pattern application for what amounted to a simple web app.
I actually agree with you and the general assessment of modern PHP (see below).
What I wanted to say is that I feel like it is slightly misleading to advertise these things as bad practices if the audience is unknown. For example a beginner should probably just stick with the default idiom of a language/domain/project and understand it first, before adhering to advice given in these kinds of articles. PHP just has too many footguns so you either go with modern idioms or you are experienced enough to avoid them _safely_. Also there is the issue of documentation and learning.
---
PHP is almost never my language of choice, but since I do freelance work it chooses me more often than I would like. The language is more expressive than many think and has some nice and sometimes unique useful features. But it is so inconsistent (in many ways) that your code is already ugly _before_ you start writing it (as opposed to after).
That said I personally dislike writing overly abstract "OO-everywhere" programs and in my opinion PHP is _specifically_ unfit for this kind of style, since it is stateless, so everything you need to load before you do actual work, becomes a continuous performance hit (unlike for example Java, which also starts to get very fast once the JVM is warmed up).
There are abstractions and utilities that absolutely make sense when dealing with PHP though:
- Response & request handling with routing (parameters etc.) and middleware abstractions, including convenience for session handling and validation.
- A sane abstraction over building HTTP requests (if needed).
- SQL query builders (if needed) for convenience, increased safety and better error reporting. PDO is still a bit too error prone to use and is inconvenient compose.
- DB migrations (if needed).
- A convenient logging library.
- A collection library. Built in PHP array functions are very inconsistent, straight up weird in terms of naming/functionality and incomplete in terms of what you'd expect there to be.
- File handling uploads, streaming etc. (if needed).
- You definitely want to use composer and auto-loading.
- Probably _some_ more but you get the drift.
A framework will give you all of these things (hopefully) plus a whole bunch of stuff you don't need (like ORMs, DI, code generators, template engines etc.)
My most memorable experience was during the interview process for a company that used PHP for their backend. I attempted to solve the take-home assignment using a couple of libraries (some Symfony components). An overarching criticism of my work was "you should have used a framework" and "always use pre-existing packages rather than write your own." The reasoning was that popular packages are vetted and tested by the PHP community.
I understand why they believe this. I also find it astounding that PHP developers distrust vanilla PHP so much (and likely for good reason).
Instead of thinking of “my code that I wrote is simple and good and true” and “their code is over-engineered and complex and terrible”, choose to “adopt” third-party code: Treat it like it’s yours to understand and maintain.
Doing this, you’ll get a much better sense of how the “other” code handles things like error handling, security, stability, upgrades, etc., and more importantly, whether your point of view on these topics is in alignment with the maintainers of the third-party code you’ve adopted.
You can even see it in a lot of the comments on this topic. Things like: "I like rolling my own code so I don't have to learn someone else's API." I think that's typical of a less experienced dev that already feels overwhelmed by all the other technologies that they have to learn. Dunning-Kruger and all that.
Better tools, and some practice and experience have made it easier, but when I ask a less experienced dev to read someone else's code I do try to keep in mind how it was for me early on.
Cool! So now we’re expressing something we value, and we can evaluate third party code and our own against that value.
> Understanding the source of libraries we’re using is great...
Again, we may value that, we may not.
“Treat it like it’s yours to understand and maintain” does not mean eagerly read all of the source. It means that you should act like you’re also responsible for the code’s maintenance, whatever that is and to the best of your abilities. Because it is a part of your app when you use it.
My point is that web applications are, at least from a security standpoint (but also some others) anything but simple, and I'm fairly sure most attempts at creating a focused, lightweight, custom framework are littered with security issues. There are counter-examples in both directions, but this is just my general experience; people who know all the pitfalls you would have to avoid also don't generally want to try to implement all that by themselves.
There are many framework out there that is hard to add another extension / plugin when it's not included in configuration. Some example maybe if you want to connect to multiple db, elasticsearch library, redis, etc. It's sometimes hard to do something when the app booting due to the nature of framework.
On the side note though I find Laravel is mature enough for standard use.
There are many, many more superior options when it comes to general purpose 'no-framework' styles.
Now, most of the application servers that implement JSP will implement at least a big chunk of some version of Java EE, but you don't really have to use most of it.
1. Turn magic quotes on
but actually it's about religious use of (popular) frameworks and design patterns. Which I might actually agree with (people at my old job were always trying to get me to use Kohana, and then Laravel, and I hated them both and still don't understand why people like that kind of thing), but not in the mood to get riled up about it today. Fight the good fight, phpthewrongway people.> All general purpose PHP frameworks suck!
> – Rasmus Lerdorf
Rasmus said this an awfully long time ago - 2013 or earlier?
Truth of the statement (then or now) aside, Rasmus was a C programmer long before he was a PHP programmer, which presumably strongly informed this position.
1. Maintenance: We support a mostly successful ecommerce application that was launched on CakePHP2 in 2012. These guys, like many of our clients (and yours too!) are stingy and while we've worked consistently on the site since then, upgrading to a newer version of CakePHP is a non-starter (Cake 3 was a total rewrite). As you may know, PHP in 2012 was a different world and we're stuck in it. I've just found out that CodeIgniter 4 is also a total rewrite. Sure CodeIgniter 3 is still supported, but for how long?
2. Deployment/Dev environment: It's possible that we just weren't doing it properly, but I have a large non-framework PHP cli app and several Go/Angular apps that move seamlessly between dev, staging, and production with very little human intervention. It always seemed more complicated with PHP frameworks.
3. Too much stuff: Basically the same argument as the article makes: Why should I use Cake\Network\Http\Client when I could just use Guzzle? Why does this ORM produce 30 unnecessary queries per request? Why in God's name would I use this "Helper" class to generate a <p> when I could just write HTML?
4. It's slow: As always, slow is a relative term, and PHP frameworks are relatively slow. Because there's too much stuff mostly.
The maintenance point is the most critical. Most of these frameworks last ~5 years between total rewrites. I'm sure there are exceptions. Laravel seems to have risen to the top, and might not have this problem. If you expect your application to have a lifetime of less than 5 years, then maybe it's OK. 5 years seems like a long time when you're just getting started. Ten years later...
I last used PHP a decade or so ago, and heavily relied on CodeIgniter framework. They had a good, tight feature set, and a fantastic community.
The fact it's now mostly gone kind of speaks to your point, though it may just be that PHP's marketshare overall has dropped so much that there really isn't sufficient energy to keep more than a couple of frameworks alive.
Well, the comments are (blissfully) in ISO 8601 format, but the article, the URL, the headline, etc are above any kind of chronological temporality.
The author does mention 'last October' ... as though that only happens once every hundred years.
it would have been very difficult for him to have been a PHP programmer before being C programmer, since he invented PHP by writing it in C, which is why most PHP functions are almost exact syntax as C
ftfy
For instance, it's Paul Graham: > When we try to improve ourselves, and in this case more specifically our code, we sometimes get hung up in the philosophy of a particular pattern or idea and tend to forget to think practically.
>> When I see patterns in my programs, I consider it a sign of trouble. The shape of a program should reflect only the problem it needs to solve. Any other regularity in the code is a sign, to me at least, that I’m using abstractions that aren’t powerful enough - often that I’m generating by hand the expansions of some macro that I need to write.
But it seems like the author is misunderstanding pg. pg is claiming that if a pattern (of any kind - whether it's a "GoF-approved" design pattern or not) keeps repeating in your code, your code is not abstracted enough — either because you're not using the right abstraction for the job, or (what will be inevitable in a language like PHP) because your language is not powerful enough.
The solution presented by this article is just: "Do not always use patterns" - but it doesn't clarify which patterns you should use. But Paul Graham is saying something much broader than that: design patterns are just a type of pattern, and patterns of any type should not be repeat. Essentially, this is a strong version of DRY that considers any generalizable patterns as repetition.
> Later, procedural programming was derived from structured programming. Procedural programming is based upon the concept of “procedure call”. A “procedure call” is just another name for a “function call”.
I'm pretty sure it's the reverse.
Following this we get a justified criticism of OOP (although we should be clear here: this is mostly the bastardized Java brand of OOP). And there we get another quote from pg (who I assume was criticizing the very different CLOS and its necessity in a Lisp-family language).
Then this bit ends again with the suggestion "Do not always use object-oriented programming". I can't agree more with that, but alternatives should be provided. When should I avoid OOP, and when OOP would be good (the source for pg quote is actually followed by a recommendation for Common Lisp)? What should I be using instead?
The funny thing is that I probably agree with the intent behind this manifesto, but I can't ask any PHP programmer to reconsider their best practices following this. Since the rationale is not presented clearly enough, and no guidelines or alternatives are defined - or even proposed - it won't help to convince a lot of programmers from my experience.