Modern PHP without a framework
kevinsmith.io
kevinsmith.io
We used to spend 90% of our time determining the right nail to use, the right wood to use it on, and where best to place it, and only 10% of our time on selecting and wielding the hammer. Now we spend 90+% of our time figuring out which GenericAbstractToolFactoryManagerFactoryInterface to invoke and how to properly invoke it a in such a way as to coerce it to ultimately generate something that eventually might generate something else that will hopefully drive some sort of fastener into something in such a way that will possibly hold it together, and the rest debugging why instead it is generating a generator that generates chisels that are chopping away at the foundation.
Yes, that's an exaggeration, but how much so? We are smart people, we do figure out how to use these complex combinations of systems to build functioning (bloated monstrosities of) applications. But are they really reducing technical debt or adding to it?
Are these systems really going to be easier to maintain in a few years when they have 357 dependencies that have each evolved over time (and or been abandoned)? Easier to optimize, when that means modifying what's going on under the hood of those dependencies? Easier to extend, when doing so would lead to conflicts with those dependencies?
I'm predicting that the next wave of discovery and hype in the field will be focused on improving productivity while reducing technical debt by removing the cruft that we're building now and replacing it with something simpler and more to-the-point.
It's built with php5, uses pretty static file structure, no namespaces but an autoloader. It has around 150 source files. I must say that it was surprisingly easy to get into the code base. The fact that there are no namespaces means class names have been picked carefully, no generic stuff like "dataholder". About half the classes are entirely static and there is still a lot of assoc arrays being passed around while in other places proper classes are used. The front end is just bootstrap with a little bit of additional js and ajax where it makes sense. Still it is really easy navigating the code base and finding the right spot to extend. I hardly ever felt constrained by the project's design, if ever rather by the inconsistencies of php(5) itself. Yes, sometimes it's annoying to implement a dynamic page update via ajax by hand without fancy abstraction, but if you're not building a modern spa it's not that bad and you actually get what's going on.
That other java code base I'm involved with is about 4 years old and really modular and fancy and maven and every time something needs to be added a discussion starts on how to do it, which parts to abstract away in which way, which library or framework might be suited best, and after five trillion years coding might actually start. I might have exaggerated a bit here but that's somehow the feeling I have more and more in modern software development.
Boom. You can stop here. This has nothing to do with PHP or your project.
It's just the previous devs were good professionals. This is why "it's easy to navigate and extend".
Proper naming is one of the most important tell of a code work well done. You got lucky.
But a (good) framework will enforce a reasonable structure that will drive even less skilled professionals in a unperfect but sane direction. It's still possible to take bad decisions, but less often, and the cost of it is reduced.
You did indeed have a more professional set of folks developing that.
I just inherited a small PHP project written 2 years ago:
* no version control * running as root on apache 2.2 in /var/www/html * php 5.5 * about 12 "pages" each with a healthy mix of html/php ('header/footer' are abstracted to external incudes)
the problem with the 12 "pages" is there's around more than double the logic states and I need to add more. there's a bunch of
if($_SESSION['step3done'] && $_SESSION['step3errors']!="") {}
interspersed all over the place. Do I continue the pattern? Or refactor? The previous developer would add 'new' stuff in a matter of a few hours, because they'd just expand on that pattern; it's now unwieldy, and I need to add 'new' stuff, and expectations are "a few hours".
FWIW, it's probably what I'll do, but am migrating code in to version control, adding items in an issue tracker, and documenting my concerns in tickets, a README, and some code comments. It may come across as a bit passive-agressive, but it's not meant to; just trying to keep a history of concerns while also hitting their short deadline.
PHP needs to be upgraded regardless.
It's the difference between flask and django in the Python world.
Beginners tend to go for flask. It seems easier.
But I actually always recommend Django to beginners, and only flask to experts.
Why ?
Well, because I've worked on a lot of code bases with both django and flask, and when you don't have good devs, the result with flask is disastrous. Indeed, flask delegates a lot of decisions to the dev, and as a result, if you don't know what you are doing, the project ends up with bad structure and terrible security.
E.G: django out of the box rotates your session key, provides csrf token, and en entire auth system. But I've worked last year on a flask project that used unsalted md5 password hashes. Their auth backend returned 403 for most errors in the authentication process, even for bugs, with unrelated 3 words messages.
The thing is, if your project is not a success, you don't care. But the goal of a project is being successful, and if it is, less skilled devs will probably have to work on it.
Hell, most projects don't even start with skilled devs. The reason is simple: there are too much demand for tech, and not enough devs. So we do with what we have. I mean, I have companies in the silicon valley paying a lot of money for me to work from my bedroom in France because they can't hire the expert they need around them anymore.
And when I arrive on a new project, guess what happens ? If it's a framework I know, I can start working on it from day one. If it's not, I'll need time to adjust. It's it's custom, I'll need more time, in direct correlation with the skill level of the other devs that worked on it.
Regarding the beauty of DI, let me provide an example of a typical large application. Such an application may need a UserInfo class to model a user's information in the app. This user info class will likely have many dependencies: it needs a way to persist itself to disk, to make network calls for server syncs, and any other ancillary components. As an app component, I don't need to understand any of this. I just add a component to my constructor and I immediately have access to any functionality I may need. Using DI in this way also has a nice side effect: classes can be stateless singletons and still be easily testable with mocks. And the best part is none of this is magical. All of it is governed by a few simple rules.
I pity them
Taking learning from previous companies, I've found the main thing PHP was lacking at the time was a good routing mechanism. We obviously don't want to rely on Apache hitting .php files to handle requests. So we used ToroPHP (https://github.com/anandkunal/ToroPHP) for routing, which is a seriously lightweight routing lib. Indeed we used composer and a lot of dependencies, but for our own codebase we used manual includes instead of the autoloader. With the usual PHP production setup like APC with opcode caching enabled and file stat'ing off (later on opcache + APCu), we handled scale extremely well. (mentioning this because a counter-point for manual includes is that many requests would unnecessarily load way too many code files)
As far as codebase maintenance and growth go, that PHP codebase was actually very easily manageable even with manual includes. Having routing solved frees you up to organize your code files in any ways you want. Even today, I have a mini-framework structured this way for myself to start side projects and test ideas with. (Yes, using PHP5 for side projects in 2018 ;)
My point in bringing all this up is that the architecture this article is trying to take you in, seems unnecessarily complicated for PHP.
This past year I've been working in a full nodejs shop and this entire architecture (from this article) reminds me heavily of how a node/express app is structured. Someone else on this thread mentioned modern languages have their architectures converging. This is exactly what's happening. Even as a PHP fan, it makes me feel a bit of "Why use PHP at this point, instead of nodejs/go/python?" while reading this. It feels like if you wanted to go this route, nodejs is a better option on almost all fronts -- language syntax brevity, availability of packages, community, developer-availability. The only reason to use PHP at this point is nostalgia/sticking to PHP syntax.
Now the reason to use PHP over Java or Go is that PHP at this point has a type system that’s on par with Go’s but at the same time it gives you an advantage of fast development process thanks to the lack of need for compilation and/or restarting the container after each change in the code. I’m currently working with Java and before that I had a long experience of PHP, while I personally would choose Java over PHP almost in any case because I value type system features perhaps a bit too much, but I do suffer from those slow turnaround times (compile/restart TomCat) and I can see how it could potentially be bad for the business at early stages especially.
So the bottom line is PHP at this point has a nice balance which is hard to find atm in any other popular languages with evolved ecosystem
Still missing types for object properties :(
We introduced typing later in the project and wow what a step up in safety and productivity.
I've been part of teams that pride themselves on using propel + klein to build an app in a non-framework.
If you want a light framework just go with lumen, flask, sinatra. Ymmv.
All new devs wasted a lot of hours learning propel and klein and it didn't always do what they wanted/needed, and the code wasn't the prettiest, where as most knew laravel, and longed for the comfort/sanity of coding in laravel.
Sure, for a hobby project/side project this is a good practice to become a better dev, and understand how a mvc framework is built, but for a large code/team it's best to go with something established so new devs hit the ground running without having to learn a ton of stuff.
It's easier to find a 'laravel dev', than it is to find a 'custom built mvc dev with extensive experience in x, y, z packages'.
You have to do a big dance of putting files in all right places and implementing all the right interfaces -- as you would with any framework -- but Laravel doesn't seem to give you anything for your trouble. Take, as one example, validation -- you still have to write virtually everything yourself! There are some very basic hooks and some wire but that's all. For all the trouble, I expect more actual features from my framework.
For example laravel defaults to App/ for models.. so App\User which is ugly if you have 50 models. I ALWAYS use App\Models\User instead, so I move it to the App\Models folder, put all my other models there and presto, chango.
Have you looked at validation in laravel 5.6?
Use Validator ...
$v = Validator::make($request->all(), [ 'email' => 'required|unique:users' ]);
if($v->fails()){ return $v->errors()->first(); }
.. continue we passed validation.. If you can't grok the code above in laravel, not sure what you suggest that would be better.
It's a framework, it's job is to make it easy to structure the site and handle common tasks for you. Laravel does have validation built in. You might have to put custom rules to use every now or then, but I don't see a way around that.
But laravel sure has its cost when it comes to performance - it's not possible to have it both ways.
I don't see the value of:
validate('title' => 'required');
vs. if (empty('title')) $errors[] = 'Title is required.'
Especially when this happens at the controller level.That being said... yes, the validation system is lacking. For instance, there's no built-in way to perform sanitization before validation, nor normalization afterwards. Definitely one of the parts that I like less about the framework.
[1] https://laravel.com/docs/5.6/validation#form-request-validat...
Why have a separate class/component that's highly tied to single controller or action? It just seems like dividing non-reusable logic up between files for no good reason.
It doesn't suit Laravel very well because it uses Eloquent and its Active Record approach to models. This means that the model already has way more responsibilities than it arguably should. Now you also want to make it responsible for validations...
In the 20% case, you may have different validation rules for different actions (you can PATCH an object with a subset of fields, or you can set a field during creation but not during update, or whatever you can think of (it happens at some point). Also, validation is oftentimes depending on the user's permission (this field can only be set by supervisors, or only administrators may set a price lower than XXX$, or... you get the point I hope.
Finally, you don't need to do anything to "validate each individual HTTP request" from the coding side. If all your actions that refer to the Thing model have the same validation rules, just create a ThingRequest object with the rules once and inject that as a parameter to your controller methods. Laravel automatically resolves that object, filling and validating it before giving it to you, as in:
class ThingController {
function store(ThingRequest $request) {
$thing = new Thing($request->validated());
$this->save();
flash()->success('Thing saved');
return redirect()->route('things');
}
}
And that's it, this includes validation (as defined in ThingRequest), with the possibility to add custom validation messages and all.That's my point! It seems to be a common refrain for Laravel. A good framework should make the 80% super easy -- that should be the trade off. For the last 20% you can do some easy manual validation in the controller -- hardly a big problem.
> This means that the model already has way more responsibilities than it arguably should.
Not a solid argument for Laravel...
> Now you also want to make it responsible for validations...
If I have a model validator that requires every basket to have at least 3 eggs of different colors then that validates the user-interface and can validate the command line process that adds a purple egg to every basket. With Laravel, validation is far too tied to web requests. And if you have multiple models that work together in different combinations in a controller you're creating different CombinationOfThingRequests to validate it.
> If all your actions that refer to the Thing model have the same validation rules, just create a ThingRequest object with the rules once and inject that as a parameter to your controller methods.
So you have a ThingRequest that exactly validates a ThingModel -- that's not very reuseable. How many different controllers exactly manipulate a ThingModel that the request is identical across them all? How many actions even do that? If this ThingRequest is responsible for validating more complex relationships, like the basket/egg example above, that means my HTTP request object is querying my database! If that's not the wrong level of abstraction, I don't know what is.
When you put validation in the controller, it's not reusable.
Having request classes gives you best of both worlds. I really like the way Laravel does it.
I actually like almost everything the way Laravel does it. Really a lot of thought has went into every design decision.
I get that there are places where you need more specific validation and that can easily go in the controller.
What I can't imagine, and maybe you can help with this, is when you'd need to reuse validation across controllers that doesn't make sense the model?
I absolutely could not understand why Laravel divides things the way it does and I just did not like it. Request classes are a concept that don't even need to exist, and don't, in other frameworks.
validate('title' => 'required|unique');
Laravel validation can accept a callback as well. Laravel offers a lot of helpful small improvements that speed up development overall.
Frameworks are development speed enhancements but frequently are not "better" in terms of mechanical function. Even "major" PHP applications built on frameworks (i.e. Magento 2) frequently lose site of those abstractions and create fundamentally flawed products where the solution is you need a software developer to spend hours on it just to make the thing "work" as advertised.
This rush to abstract everything is good for developer productivity, to a point, but I've rarely seen it result in a better/saner product.
A developer shouldn't have an issue working with either. It's all PHP and all sane PHP frameworks are mostly based on the same concepts, with some tradeoffs here and there...
For many years now, "frameworks" such as Symfony and Zend have been mainly been a set of components with an optional thin framework layer. So, not frameworks in the traditional sense.
For those interested, another great resource is "Create your own framework on top of the Symfony Components":
https://symfony.com/blog/create-your-own-framework-on-top-of...
I'd basically record myself coding for 1h then add a voice over it as I speed up the video 3x.
Although my English falls apart fairly quickly, (if you ignore that) it is a good way to see the entire process of building a framework and understanding what each part does.
A lot of the things in this article are covered there, with a little bit of the why.
[1]: https://www.youtube.com/watch?v=0sYFo92sFLs&index=2&list=PLr...
I encourage people to build their own framework without composer as a learning experience if they're looking to level up their PHP knowledge. I plan on going through it again from scratch but following modern standards: PHP7, PHP-FIG, strict_types
Yet it continued to be used in some of the most scalable and widely used apps of our generation from Wikipedia, Wordpress, Facebook, Slack and more.
It often feels other languages marketing teams have preyed on it in many ways to gain attention. And there has been some kind of war against simplicity that places an unearned respect for 'clever' abstractions under the guise of scalability often in pursuit of resume padding, or 'best practices' entryism and gaining influence in projects via package managers and the like.
- Setup a library tracking system (composer, the autoload stuff)
- Setup basic Dependency Injection (using code in this case, but the same could be done through config files or annotations)
- Parse requests into a request class
- Setup a middleware chain, including a router, and dispatch a request through it
How is this different from flask/django (python), rails (ruby), or gin (golang)? I'd say the basic concepts are pretty much the same, except maybe the library handling part. In other words, I don't think there's enough "meat" in this example to appreciate actual relevant language differences.
Also, you can write "java-like, enterprisey" code (Symfony) that values explicitness over magic, or go the other way around following the "convention over configuration" mantra, and end up at Laravel that is much more Rails-like.
Finally, PHP is still has several advantages over java for many people out there:
- Every request starts from scratch, meaning it is impossible to accidentally provoke side-effects on other requests
- The package system (composer) is just a package management system, and hence MUCH simpler to understand than ant/maven/ivy, that are entire build systems that also want to control testing, deployment, and everything
- Development is faster-paced thanks to the lack of compilation. You can even easily setup an environment with auto-reloading and just "save updated code" -> "see result in less than a second".
- You can deploy your app to any of a horde of (cheap) hosting providers and let others deal with the ops side. The service may exist in java-land, but it certainly isn't as ubiquitous.
- PHP programmers are cheap (yes, cheaper than java-programmers).
On the other hand, things in PHP-land are improving at such a rapid pace (but without the churn of JavaScript) that if you are a PHP developer there isn't much reason to switch to something different but similar.
In a lot of fronts it means mimicking Java, but I think it still is more flexible (and less reliable as the other side of the coin). It’s still very easy to drop down to “quick and dirty” code anywhere it’s needed, and it still benefits from a very low bootstrap cost to deal with single command/requests.
Or would you propose sending a boolean to indicate null as well as a string in a data type?
https://www.lucidchart.com/techblog/2015/08/31/the-worst-mis...
String is not nullable String? Is nullable and the compiler/ide will force you to check for null when you use it.
Which, I agree, can be downsides as well.
I don't know that I've ever encountered something like:
RequestProcessorFactoryFactory.RequestProcessorFactory.getRequestProcessor(XmlRpcRequest)
In any other language. And that's not the most egregious example. It's quite common.There's inheritance, and there's overindulgence.
There is only a choice between using someone else's framework, and inevitably building your own. If you reject all third-party frameworks, you will end up reinventing a bunch of design patterns from other frameworks. You will end up writing your own libraries to do the things the framework would have done. And you will -- no matter how good or fast it feels to you -- end up spending more time in the writing and maintenance of your private "non-framework" than you would have spent in deploying an existing one off-the-shelf.
For large projects, I strongly prefer a frameworkless approach. If you do throwaway apps for an agency, then a framework is probably the better choice.
You don't want to call that combination of libraries and in-house wiring-together code a framework. I get that. But that's what it is, whether you want to call it that or not.
And I'm sure that you don't see it as a maintenance burden right now. It never feels like a framework or a burden at first, because it's all made up of this code that you wrote and that you understand and that you control, and it feels great! For a while, at least.
But sooner or later you will realize you have built your own in-house web framework, and that now part of your business is to be a web-framework developer, instead of a whatever-your-company-actually-needed-to-make developer, and that being a web-framework developer is not a very fun business to be in.
And you will hire more people, and they will have to be trained on this thing-you-refuse-to-call-a-framework, but they'll recognize the patterns sooner or later, and will wonder out loud why it's missing this or that feature that the off-the-shelf frameworks have out of the box, or why this particular part of its API is so weird and cumbersome (it made sense back when the codebase was much smaller and only a couple people worked on it, you'll tell them, sheepishly).
And maybe some of them will roll up their sleeves and try to add what's missing or clean up the historical weirdness, and maybe some of them will get tired of working on this thing that won't add to their résumé and leave, and others will just do their best to get work done, but at a much slower pace because they're working with a framework built by people who hate frameworks, and still others will start to agitate for just going and getting a proper framework off the shelf and using it already, and you will end up having some very interesting and perhaps very heated team meetings, and you'll realize that you're spending way too much time as a team talking about tools and technologies and not nearly enough talking about products and bugs and features and this is not going as well as it was back when everything seemed so simple and easy in the world of "frameworkless" development.
This is the iron-bound web developer's counterpart to Greenspun's Tenth Rule, and in my experience there is no escaping it.
You may be looking for Hack, which supports XHP natively[0].
When you first started with PHP, you may have used includes or requires statements throughout your app to bring in functionality or configuration from other PHP files. In general, we want to avoid that because it makes it much harder for a human to follow the code path and understand where dependencies lie.
Autoloading just means that when your application needs to use a class, PHP knows where to look for it and automatically loads it when it's called for.
How in $deity's name can you say that specifying immediately the exact path to the file included, in the file that includes it, is "much harder to follow" than hiding it behind the indirection of the autoloader and whatever it does!? Honestly, WTF!?
(Maybe the joke here is that everyone is just pretending to admire the emperor's new clothes, in which case I apologise.)
Edit: here's an example of the OOP articles I mentioned: https://csis.pace.edu/~bergin/patterns/ppoop.html
The author recommends building some kind of abstract structure factory which has perhaps five external dependencies per codeblock, a composer file, vague notions like dependency injection, auto-wiring, containers, routing, middleware, request factories, response interfaces, a class hierarchy and an obtuse non-production webserver in mental overhead ... lauding the design because he says it complies with a bunch of numbered standards that allow you to swap out your layers-of-assumption components.
This is blatant false economy. Nobody ever swaps out components. Nobody in fire-and-forget webdev land cares about standards. Just get the thing out the door, keep it simple, and thus keep it maintainable.
As others have commented, explicit code is clearer. Clearer code has less bugs.
Why not save the hassle and just use:
<html><head></head><body>Hello, <?php print anti_xss_function_of_choice($_GET['worldtype']); ?> world!</body></html>
This is the 'new PHP' from ~1995, seamlessly using decades of high efficiency improvements to kernel caches, networking gear, the interpreter itself and webservers. Zero complexity, not a class in sight, and definitely no middleware. It's a great language, when not misused.
But 18 years have passed. Every language now has very good package managers. There is an abundance of dynamic languages, most of whom are more consistent than PHP.
I'm not going to waste your time with a long PHP rant. I just hope that everyone is aware that the huge benefits that PHP offered in 2000 are no longer valid. If you are programming in PHP, you owe it to yourself to explore some of the many options that are out there. There has been incredible innovation among software languages since 2000, but PHP has mostly tended to follow a conservative path, mostly imitating Java.
For anyone who won't be bored by another PHP rant, I wrote my thoughts on this subject back in 2014:
All you do is insult the language, and subsequently, the intelligence of those who continue to use it. Many of us have looked at - and heavily used - other languages, and choose to come back to PHP for many (not necessarily all) projects. Your assumption that we're all morons who program in PHP because we're too stupid to try other options is misplaced. You looked elsewhere and preferred what you found. Good for you. Some of us who looked elsewhere found that every language has its faults, and that PHP does the job just fine. Particularly since the performance improvements in later PHP 5.x versions, and finally the leaps and bounds gained in PHP 7, the language is more than good enough for most tasks.
I've done projects in C, NodeJs, Ruby, and yes, even Python. Python fanboys in particular seem to think their use of the language is an indicator of superior intellect or programming ability. The whole "I am smarter/better than PHP developers" gag is really, really, old.
https://www.techempower.com/benchmarks/
But even if both of your points were true, the point is to look at what is happening in other languages and frameworks. Every day you spend learning about PHP is a day that you are not learning about the languages that are really moving the industry forward to interesting new places. Ask yourself, honestly, how many interesting new ideas will you learn from studying PHP, versus the more versatile (as in more platforms, and also the frontend) languages such as Javascript/Node, or the more innovative such as Go or Rust or Clojure or R or Julia? And I'm not even mentioning the more experimental languages, such as Shen or Lux.
I continue to be baffled by this sort of "novelty-chasing" --- do I really care about "interesting new places" or "interesting new ideas"? No; and in fact I'd rather not, because it takes time and energy away from actually solving useful practical problems, besides the ones you create yourself in chasing after the latest cruft.
PHP is slow on an absolute scale, but chances are that a minimal, straightforward application written with it can perform better than something with a bloated framework on top in a different language. That takes true skill and experience, not something you can get if you're continuously jumping around trying to chase the fashion trends.
As someone who has witnessed several major trends in programming come and fall, the amount of churn (and encouragement thereof) in the web community is astoundingly unsettling.
"It's not about the tool, it's how you use it."
The ecosystem is also smaller and deployment in Elixir is still a work in progress (mismatch between Mix and Erlang releases).
About this:
"because it takes time and energy away from actually solving useful practical problems,"
You can not know that without first studying other languages. You might find that you are 10x more productive in Rust. You can only find out by studying Rust.
But you bound the conversation in ways that I didn't when you write:
"in the web community"
I feel that developers can learn a great deal if they study projects that don't necessarily have anything to do with Web programming. Nothing in the article or on this page necessarily restricts us to only considering Web programming.
"It's not about the tool, it's how you use it."
I wouldn't use a hammer to go fishing. Nor would I use a saw to turn a screw. Nor would I use a frying pan to tune a piano. It helps to learn about a lot of different kinds of tools, because over the course of your career, you might be confronted with diverse tasks.
Is this a troll? No you don't need to care. Feel free to go live off roots and tubers in the forest. Or write some COBOL. Anything is justified if you're putting "interesting new ideas" in quotes.
I worked with PHP full-time for several years and energetically rationalized away the time I invested into building that expertise. I liked it, I was getting things done, it would help me get jobs in the future. Utter nonsense. What a waste of my life.
Eventually I snapped out of it. Turns out you can just move to places Silicon Valley, there are jobs at awesome companies working with the cool modern technologies. You don't need anybody's permission, just do it. Want to write Haskell/Go/Rust/Elixir/Clojure every day? Want to work with ML or robots or big data or whatever floats your boat? Just go do it. You don't need to sit there writing PHP as the world passes you by.
PHP is a backwater. People live there and tell themselves that it's fine, good as any place really, can't imagine anything better, probably wouldn't like it anyway. Or they just stop thinking about it.
Remember java was cool or mongodb or node or ruby on rails. It feels like go is becoming less cool. I wonder what next year will bring? Perhaps php will be rediscovered when SV changes once again.
Taylor Otwell says the same thing about laravel/php.
Mark Zuckerberg says the same thing about React / HHVM / Hack (PHP variant).
I'm sure Steve Jobs said the same thing about Objective C or Swift.
It's called an opinion, just because one person who spends more time investing in VCs, and just because we're debating an issue on a site, doesn't mean we have to take the creator of the site's past essays as the absolute truth...
Go, php, ruby, python, lisp variants ALL are the best script if A. it's the only one you know and you just want to launch something or B. You've done your due dilligence to figure out what you need, is it just a crud app? PHP is fine, is it a chat app? Well you might need erlang or elixir on the backend, or something with concurrency.
If you're spending 100+ hours debating on platform instead of building shit, then that's time you can't get back and that's wasted opportunity acquiring customers to a platform you could've had built last week -- if you weren't stuck in...confusion about what framework to build in, or spending all your time in tutorials because you picked the shiniest framework or language that is above your pay-grade so you have to learn everything.
This is on point! Programming PHP is not about diving deep into interesting language constructs or creating beautifully structured software - PHP is about getting things done, about starting hurdle-free and progressing rapidly. It goes against some "good development practices" but it works, see for example the single-file website remoteok.io from Pieter Levels: https://twitter.com/levelsio/status/938707166508154880
That's "chasing shiny". Shiny is the cancer destroying the computer industry from the inside.
Any day not learning how to do web application development in LISP is a day wasted. The old tech is still cutting edge, way ahead of any new shiny garbage, because that garbage is decidedly not moving the industry forward, just introducing one more variation of making a computer do something. And that's extremely bad.
Which of those languages give you NaCl out of the box?
> There has been incredible innovation among software languages since 2000, but PHP has mostly tended to follow a conservative path, mostly imitating Java.
PHP fans like to point out (tongue slightly in cheek) that PHP is the first major language to add modern cryptography to its standard library (libsodium in php 7.2). We can argue about how important that is, but it's also not wrong. And it certainly didn't copy that from Java. :) Further, I think some of your criticisms in the link have also not aged well.
Not sure if composer is "very good," but it's generally worked out better for me than gem.
(Also, package managers are not an unqualified benefit. I like them for installing things I'm not developing, but as far as I can tell they essentially defer a legibility cost for developers. Sometimes worth it. Not always.)
> There is an abundance of dynamic languages, most of whom are more consistent than PHP.
I think it was Ralph Waldo Emerson who had some choice words about consistency. And this criticism has always rung a little hollow to me. I'd agree that some of PHP's dynamic multi-paradigm scripty siblings feel cleaner, but having written code in many of them, it's never felt like a major difference in productivity to sometimes have to pause and consider argument order or precise names. The lookup cost has always seem cheap on on par.
> I just hope that everyone is aware that the huge benefits that PHP offered in 2000 are no longer valid.
There's definitely a closing in the benefits gap since 2000. PHP is far from my favorite language. PHP is not right for many projects. There are still some benefits that PHP prioritizes that I think arguably aren't done better elsewhere
* Trivial deployment story. Your app is one or more files. Drop it in a directory on a wide variety of hosting environments. You're done! (Buuut, if you want something more involved, lots of choices available there too.
* The language is the template language (though there's other options). Some people consider this an anti-feature because it allows amateurs to build ill-organized applications around a dynamic-document-as-program paradigm. I think that's still a brilliant gateway that makes the marginal cost of moving from a static document to a dynamic one small while still allowing an easy transition to front controllers. And it means the templating performs at parity with the language itself and can do as much as the language can do.
Pair it with a simple routing library and model layer and it's as easy to build something reasonable quickly as it is with comparable Python & Ruby frameworks, often with fewer moving parts and similar magnitude of performance.
So sometimes when I have smallish web-facing projects (or projects I think I might want to distribute for others to run on the web), I still often find myself reaching for it, even with plenty of other options at hand, even with languages I like better.
Generally speaking, while I don't think this is satire, I would hate to see someone code this way, unless the point was simply to learn how the components of a web framework work together. This is not "framework-less PHP", it's just a DIY framework, and not a particularly good one. I think that attempting to create a web application without a framework is at best needlessly duplicative of effort, and at worst an open invitation to security flaws and spaghetti code.
The author says, "This is not an anti-framework screed," and it's certainly not polemical. However, I think it's probably more representative of the Zend-ecosystem way of doing things rather than PHP best practices: accretion, as opposed to design.
Seems we finally broke the fever of infectious imperative spaghetti and are coming around to OOP and patterns by following the trail left by JVM ecosystem and eating its droppings and clothing ourselves in its shed skin.
You are on the mark for Java like OOP becoming popular in PHP. I think there are more people trying to do enterprise level work with PHP and they are taking lessons from existing software engineering practicies.
One example is Magento, and arguably it was the right choice at the time and has given it enduring popularity (if not endearment) for nearly a decade.
As more software engineers took web applications seriously they brought best practices and tech stacks with them, and you can see the PHP community really strive to level up.
But if I were to make a bold statement, it is web development in general that struggles producing solid reusable work. You see it in all languages. The only ecosystem I can think of that isn't disparate islands of unique wheel factories is Ruby, and that seems to be because people learn "rails" not ruby.
You can do it, but it's most likely a bad idea to mess around with it considering namespaces and such.
Disclaimer: I've not written much php nor read the article. Just replying to this one comment because the OOP analogy spoke to me.
There's a number of issues inherent in manual include statements:
- When you include a file, unless you include it using the absolute path, the path will be resolved using the include_path ini setting, which works just like the $PATH environment variable works to resolve the location of binaries. Typically, `include_path` will be set to something like `/usr/share/php:.:/some/other/dir". If I encounter `require_once 'Foo/Bar.php';` in some code, what file will that include? `/usr/share/php/Foo/Bar.php`, but maybe also `./Foo/Bar.php`.
- Relative includes are relative to the current working directory, so a different file may be including (or not found at all) depending on what directory I'm executing my script from. To avoid this, it's good practice to include files using `include __DIR__ . '/Foo.php';` to ensure we know exactly what file is included.
- Prior to autoloading, most dependencies were installed in a global location, e.g. `/usr/share/php`, which would then be part of include_path. Different projects on the same machine therefore had to have the exact same dependencies. Autoloading enables per-project dependencies.
- Using manual includes makes it much easier to have a poorly structured project. With autoloading, if I see `$bar = new Foo\Bar();`, then I know exactly what I'm getting: the class Bar, defined in the file `src/Foo/Bar.php;`. On the other hand, if I see `include 'bar.php';`, I have no idea what I'm getting. What's in the file? Could be class, could be bunch of random functions, could be mish mash of HTML and PHP spaghetti.
Same of these issues are manageable even without autoloading by following best practices, but unfortunately there's a lot of really bad code out there that doesn't. Autoloading (along with composer) completely solves a problem that used to exist in PHP. Anyone that actually uses PHP can attest to this. If you've ever worked on a project with a web of manual includes, it's a total nightmare compared to a properly structured project using autoloading.
Autoloading aside, the point of this article, IMO, is that it's quite easy to set up a well structured, maintainable, easy to understand project architecture without using a major framework. The central abstraction here is that all requests go to a front-controller, which then calls a function that accepts a representation of the HTTP request and returns a representation of the response. Having abstractions for Request and Response is much more manageable (and testable!) than having random print statements everywhere and using global variables like $_POST. This is same idea behind just about all web programming "frameworks" in any language. Of course this would all be over kill if all you needed was a "Hello World" page. But this isn't about hello world pages, it's about real projects that grow over time and need manageable structure.
Furthermore, this article advocates for dependency injection. Again, overkill on a hello world page, but in a real project explicit dependencies makes life so much easier.
[1] This is the de-facto standard. Sure you could technically write an autoloader that does weird things, but nobody--human or automated tools--does this in real code. It's not a problem in practice--autoloading in the real world makes things much more explicit than manual includes.
I'd also like to encourage everyone to strace a php run sometime and then tell me how great autoload is..
stat(path1/Class.php)\ Not found
stat(path2/Class.php)\ Not found
stat(path3/Class.php)\ Not found
stat(path4/Class.php)\ Not found
stat(path5/Class.php)\ found!
open(path5/Class.php)
read(path5/Class.php)If I were to write a website in PHP, I'd use functional programming and certainly no framework. And I'm still scarred from the Autoloader in Perl. Never again!
We have come a long way!
50 lines of code and contrived examples (is HelloWorld class a view or controller? AwesomeClass is a model?) get one line of HTML emitted in a barely readable way.
This article fails to motivate why each layer of complexity is added. It basically starts with the assumption that you want to use a framework and says "hey look you can do the same thing with 50 lines of boilerplate, not really, but kind of."
I would be much more impressed with an article that starts with the obvious way to write a PHP application (.htaccess, index.php, products.php, library/database.php, library/*.php) and then explains the actual problems that would make you want to opt for more complexity/organization/modern techniques.
It has absolutely made me a better developer because I've had to delve into how the system works more than I ever did with Laravel (or Ruby on Rails, in a previous life). Things don't just work (hell, sometimes they don't work at all) and poking around until I really understand why is a healthy thing for any mid-level dev to do.
That being said, I do miss a lot of the modern niceties and it can be really frustrating to work this way. I'm glad for the opportunity, but I would also like to be able to use more modern dev tools in the future.
The major problem over the years of fire-fighting various dubious installs of Drupal is people hack core and contrib modules making it impossible to maintain - if you use the APIs, grow modules & extend instead of hacking then it makes life a whole load easier - all I do to update my sites now is 'composer update --with-dependencies' (well, there's a few more lines to update the db if needed, export config from db to code, etc. but it's super-simple).
I hope you have applied a patch to protect against the latest security issue found, here's a link to an unofficial patch for 5:
https://www.drupal.org/files/issues/2018-03-28/sa-core-2018-...
That being said, I am still forced to use Symfony at my current job. It only gets in the way sometimes, it's not too bad.
On my previous job we went from CodeIgniter to frameworkless and I really liked that. Not having framework constraints working against you was really enjoyable to work with.
Unfortunately I'm the only one who knows Ruby but I have every faith that my colleagues can pick it up.
Uhm .. so ... a framework?
Hardly close the complexity/features of CakePHP or others.
I built a web app back in 2009 in PHP and didn't use Code Igniter or Zend (basically the only two frameworks back then), opting to do it all by hand. And it ended up being a really valuable and formative experience. No matter what language I use, I understand how a web application really "fits together" under the hood and I think there's value there.
As a side-note, compared to Node.js or Rust or what-have-you, PHP just seems needlessly verbose.
I just spl_autoload_register on a controllers directory.
Then based on the url, it creates a class for the first part of the url and passes in the rest... $class = new $class_name(); $class->index($url_parts);
That class always extends an output class which uses a template system to render a component. I also use an ORM with a models folder that has a bunch of static functions to get data in each model class.
I would love to hear from someone more knowledgable about the disadvantages of my approach or why this articles is a better way of doing it. Of course I use composer and use a bunch of libraries for aws, oauth, Google APIs... but for the actual framework I keep it simple and haven't had problems so far.
Probably, not, it was just my sentiment by looking over the code in the article :) I haven't used PHP for almost 10 years, so I have no inkling of what "modern" PHP looks like.
Annotations are ugly as hell in PHP but thats a result of not having annotations supported in the language itself (you need an annotations preprocessor and they're all in comment blocks).
There is an rfc for annotations in php as a language feature, so maybe someday. When that happens, Symfony may as well be Spring for PHP.
wat?!
i coded php in the early/mid 2000's and had at least a half dozen frameworks available to choose from even then. for example, i used fusebox after starting to create my own framework and finding that it already (mostly) did what i had wanted to do.
To me, it's no worse than any other dynamically typed language now. And arguably better than similar peers because the barrier to entry is so low.
FastCGI and user space caching like apcu mitigate the old "one process per request" complaint.
It's a true workhorse. Sure, node.js and Golang have advantages, but so does modern php.
Someone starting now would be splitting attention between “modern” concepts like presented in the OP, and the hacky/broken standard commands and quirks that persisted through the revisions.
I’d guess Ruby or Python would be easier for someone starting from scratch.
FastCGI sometimes scales better but also has increased latency because it adds a proxy layer vs mod_php where you sacrifice memory for more "direct" access.
Somewhere I can keep cached computation.
- Large stdlib that, while inconsistent, frees you from searching packages for everything (vs nodejs).
- I'm sure some will disagree, but php's documentation is top notch, easy to navigate, and with nice completion from user examples (you could say it is a mini-stackoverflow by itself). Golang's docs are good, python's are good but hard to navigate, node's are typically worse.
- PHP's ordered, possibly hash-backed possibly plain arrays are an abomination from a data structures point of view, but god are they handy! Golang is terrible here with its static types (fine) but lack of generics (just check how to sort a list of a non-primitive type).
2. I'd say Python is an easy match in this regard
3. Agreed about the docs - I still find the organisation of Python's docs a bit peculiar.
4. Handy data structures, you say? I think Python might have the edge here.
It's strange you don't mention PHP's biggest strengths: deployment and learning curve: You can begin on almost any commodity hosting with a simple text editor and no knowledge of Unix internals. The amount of extra stuff I had to learn to be productive in Python was huge at the time. It's got a bit better now with various simple deployment options but PHP still wins out for the beginner.
To this day there is no generic list sorting function in PHP proper.
Traditionally, a major pro for PHP was also its broad availability for web space but that doesn't seem to be so important nowadays anymore.
Putting node, python and Go in the same place against PHP is kinda hard as those three are very distinct from each other already ;-)
What many people miss is that very few projects ever cross that threshold.
Kind of like a table saw vs a CAD router.
With php, I have to do stuff like caching responses with apcu to get something similar, but inferior. Which doesn't scale beyond a single server host.
I can serve a lot of requests with apcu, but the curve does drop off at some point.
Both should be able to scale well in a "create a website and return the HTML"-scenario, and it's not unlikely that a PHP setup would be faster.
In a single page app thats mostly based on fetching data from an API nodejs would probably be faster.
About APCu: It's damn fast and very unlikely to slow you down, other stuff will probably bottleneck you way before APCu becomes a problem.
Use PHP as PHP, and nodejs as nodejs.
If you really need async in PHP or want to use PHP somewhat like nodejs then you should probably go for an extension and serve requests directly from PHP. I've had great experiences with that, serving over 500K simple HTTP req/s on a low end server.
But again, if max performance on a single server is so important then you should probably go for a compiled language anyways.
Often the answer is "it depends". Which is usually unsatisfactory for the HN crowd.
I just hate uninformed and unnuanced PHP hating.
There are use cases where it's actually the best answer by a large degree.
It's not that bad anymore. We do have fcgi, fpm, and also user space caches like apcu. You would have to try pretty hard to find a default set up that resulted in process per request these days.
It scales very well on a single host. Which has limits, but limits that exceed some percentile that most projects need. And things like redis take it even further. If you're pushing the limits of php, you probably have a nice-to-have problem tied to lots of incoming revenue. Similar to FB and HHVM.
I agree there's a scaling issue, but nobody is doing process per request anymore. Process pooling is a decade old or more.
I guess the objection to "spinning up a process" is actually a problem with slow PHP initialisation, though? A dinky server can spin up many thousands of processes a second. I'm pretty sure OS process overhead is nothing compared to whatever PHP or Rails or whatever else puts into your critical path, but the language/library/parsing requirements could kill you. (Though people still make this objection to CGI itself, not just PHP...)
Buzzword bingo and all...
Can anyone share their insight? Either in the context of Python or PHP.
I shiver thinking about trying to add good tests to a project in any language without good di/ioc.
In Python, it is common practice to patch objects, which means that even though the code calls `requests.get()`, the call will actually be patched through to a mock. (in the Java world, this is possible with e.g. PowerMock, but generally frowned upon)
Been trying to learn more about the Composer/PHP ecosystem and am pretty confused.
In contrast, I was able to create and publish packages in Python and Node with relative ease with little to no experience with these languages.