I'm starting a new project in PHP now. I've used the language for a
long time, I think starting with the 3.x days, and it's come a long way. There's a lot of good things about it.
But.
I recently joked in frustration that I missed the days when PHP was a cargo cult version of Perl rather than a cargo cult version of Java. It's a joke, it's not a fully fair criticism, but I can't help but feel there's some truth to it. PHP is a fast dynamic language literally designed for embedding in web pages, with a batteries included mindset that shoves all sorts of useful things into the language itself (yes, kind of haphazardly and occasionally in slipshod fashion). If I want to know what the path of the request is, for instance, I could just do:
$url = parse_url($_SERVER['REQUEST_URI']);
$path = urldecode($url['path']);
But that's not The Right Way anymore, is it? Instead, I should use a PSR-7 compliant request object, and do this:
$request = Laminas\Diactoros\ServerRequestFactory::fromGlobals();
$uri = $request->getUri();
$path = $uri->getPath();
"Okay, Chipotle, but that's just one more line, stop whining." Sure, but it's three lines that instantiates a Request object that in turn instantiates Stream and Uri objects and importa two Traits and implements, I don't know, let's say four? four Interfaces, and--
"Come on, it's still fast." Yes! Sure But you know what's faster? Looking at the $_SERVER variable. I can't help but paraphrase a famous quote:
Some people, when confronted with a problem, think "I know, I'll use a design pattern." Now they have two problems.
I know. I know. Using factories and dependency injection containers and tying yourself into knots to avoid anything that remotely looks like a global makes things testable and composable and decoupled, but it is so freaking heavyweight compared to other dynamic languages. Python's Flask web framework has a global context object just called "g", which is essentially a singleton that lets you arbitrarily store properties on it, not just variables but database connections. OMG GLOBAL RUN AWAY AAAA no, you know what? It's fine. Yes, in some sense it's probably more dangerous because someone could come along and blow away your database connection by overwriting the "g.db" property, but maybe sometimes the simpler answer is "well, don't do that."
Look, I've used Symfony, and CakePHP, and Laravel 4. I think I even contributed a test to Laravel 4's testing framework way back when. They're great. And I'm not saying "let's all just throw out objects and all separations of concern and mix HTML and business logic like it's 1999, baby." But PHP is virtually a framework on its own, and at times it feels like many smart people have put a great deal of effort into building entirely different frameworks on top of it, paradigms that PHP wasn't intended to fit into.
So for my new project, I'm just... rolling it all from scratch. I don't have a composer.json file because I don't need PHP-FIG compatibility--although the four-line autoloader I have would technically load any PSR-4 compatible module if I wanted to. It's using the front controller pattern, it has regex-based routing, it has a simple template system with layouts, it has request and response objects, it uses type hinting. It's probably not super great, but it's... kind of refreshing to write this way.
I love PHP despite its warts, and it's great that it's learned so much from Enterprise JavaBeanServerNetSwingPattern land. But I admit I'm kinda hoping for it all to get a little more relaxed about, you know, still being a dynamic language.