As long as you do not use the new functionality, I'd claim that a new PHP version is no less stable than an older one. And on a contrary you would be missing out on some bugfixes that were deemed too risky to apply to an existing version.
Disclosure: I've got a commit-bit for PHP, so we had in-house expertise to diagnose issues ourselves.
It also often fit with the current Laravel version (11).
I still have a PHP5 project that somehow still runs.
I enjoy the PHP 8 new features such as named arguments, constructor arguments promotion and nullsafe operators. Made my new code a lot more readable.
I am not that sure about 8.4 yet, though the new array_ functions and new DOM look interesting.
Yeah, this is a huge problem, but also, in the long run, inevitable.
Plenty of Kernighan & Ritchie C code still out there...
But K&R C is now so dated, protoize was removed from GCC 4.5 and onwards. When (in a bout of idiosyncrasy) I wanted to convert some ancient K&R C to new style a couple of years back, I ended up putting GCC 4.4 in a Docker container to make it easier: https://github.com/skissane/protoize
We upgrade the development environment around the time when our tooling (phpstan, cs-fixer) runs on the next version, but doesn't necessarily support new language features.
We upgrade the production environment when our tooling supports most of the new language features and at least all of the ones we want to make use of.
This usually happens within 3-6 months of the release. By that time the version has stabilized enough to be safe for production use.
I wait 1-3 months, but then update. It used to take way longer, because Amazon's Elastic Beanstalk platform would take longer to update, but I've now changed to Platform.sh and the transition should be easier.
It has been very backward-compatible (i.e. stuff that works in 8.n also works in 8.n+1; and unless you use exotic functions or are relying on special functionality, it should work for you, too).
Once I'm at 8.4, I would slowly update the code / syntax with rector and the assistance of phpstan.
For framework updates I wait 1-2 patch versions before updating, because of composer dependency problems and sometimes bugs do still find themselves into new releases (e.g. I would wait at least until Symfony 7.2.1 before upgrading from Symfony 7.1.x).
I guess depending on the app that might not really matter, but that seems like it could be an issue at some point.
The licensing is on the order of thousands or low tens of thousands per year. Worth it for a 40-person business that has a 20-year-old legacy codebase supporting a few million dollars a year revenue. It's not what we think of as a standard "tech" company but it describes an absolutely ridiculous number of businesses.
Also external tooling, like for example NewRelic extension, takes some time to release a version that supports the new release.
PHP:
- Easy to deploy: Upload files, done.
- Easy to develop: Reload page, see changes.
- Lots of HTTP tooling built in.
- Fast.
Python: - Great language.
- Great code reuse system: Modules.
- Nice framework: Django.
- Less breaking changes in recent years.
I found myself getting sucked in into a complex project structure and dealing with all kinds of strangeness like "artisan commands", "the autoload cache" etc.
With Django, I can build a web application in a single file that has "import django" on top and take it from there:
This should work in Python, too. With Django, I think you need to use https://pypi.org/project/django-browser-reload/, and rith most other frameworks / WSGI servers just try adding --reload flag.
Edit: Django should wor out of rhe box actually – that package is for refreshing the page in browser.
> Easy to deploy. Upload files, done.
I can see the appeal, but generally you’d want to avoid that (for anything other than quick prototypes, maybe). Set up a CI, push to deploy. This goes for modern PHP as well.
If you want something simple to deploy webapps and know a little Docker, I’ve built a deployment tool you can run on your own VPS: https://lunni.dev/. (Feel free to reach out if you need any help!)
But in PHP you have it out of the box. Faster, with less complexity and less resource consumption. And you can use the same setup in development as you can use in production.
When you load a Python page, it is served by an in-memory process. If code is updated, the process is restarted, parsing the code and initialising the application, but it is done only once.
When you load a PHP page, it parses the code and initializes the app for each request. It then tears everything down after the request. There are less wasteful ways (most recent are app servers, just like in Python or other langs), but I'm not sure if those are widely used in local development. Even then, it’s same as Python, Node or pretty much anything else.
And of course, I’m not trying to diss on PHP here – it’s gotten pretty good recently. But reload on change is built in pretty much every app framework nowadays. It works out of the box and is negligibly fast. It’s not a good point to compare.
PHP is always blazing fast, with no additional setup to make "Reload page, see results" work.
Python needs additional setup. Either by monitoring the filesystem and pessimistically recompiling the application every time a file changes. Causing resource consumption every time you hit save in your editor. Or by having the web workers that serve the requests die after 1s of inactivity or so. Which makes the next pageview slow again, even if no code changed.
I think PHP's advantage is because of 3 things:
1: Recompiling changed files is built in.
2: With PHP you can set your project up in a way that only the code necessary to answer a single request is updated. With Python and Django, all the code for all requests is updated and even code paths that are actually never used.
3: PHP keeps each file compiled to bytecode in memory and only recompiles files that have been changed. Although you might accomplish something similar in Python if you let it pollute your filesystem with cached bytecode files.
There are things like opcode caches that make PHP more efficient transparently, while still feeling to the developer like a reload each time.
There are reliability and development advantages to starting each request from scratch. NO leaks, no state you need worry about.
PHP is very much like serverless.
But when u are using FCGI or PHP-FPM then you have running process where opcode is cached except on first request where opcode cache is build.
Initializes for each request is standard with php-fpm, but swoole/workerman/etc are rapidly gaining popularity and run as a more typical app server with coroutines.
But who cares, we are literally talking millisecond differences between them all. Throw a reverse proxy, DB into the mix and a few packages and they are all slow.
[0]: https://medium.com/@dimdev/9-php-runtimes-performance-benchm...
You sure about that? Not what I would have expected. Source?
Sure, it works for simple/less important cases. But it also means that your application code is inconsistent while the files are uploading.
Stop your service, upload the files, start the service: safer.
For a Django app you would upload files and ask Gunicorn to graceful reload... similar, just cleaner.
In practice, any serious project is likely to be version-controlled. Git pull is generally fast enough that it behaves like an atomic change. (By default, opcache will not reload a file that's less than 2 seconds old.)
Git pull to deploy is something I avoid.
Having said that, the "most modern" deployment strategy is to spin up a new container with your new code already loaded, point your LB at the new container, and scrap the old container. No opcache issues. No race condition.
How’s php’s?
It is? I never had problems with poetry. Though I agree that there are mroe options than necessary.
Python has the worst packaging ecosystem i've ever seen
In python managing packages is a pain and there are too many package manager options, but for the most part there are good libraries, and chances are you don't even need one because the standard libraries are so good and mature.
In Javascript NPM is really all you need (even if yarn is a bit nicer), but you're gonna need to install 50 packages just to get a basic boiler plate app going and the quality of said packages is not always great.
Slim is a pretty nice framework: https://www.slimframework.com/
I know people love to say this, but does anyone realistically make websites or web apps that way? No, not really. Even with PHP there are frameworks, there is a package manager, there is version control, and there are deployment systems.
Pretending that PHP developers are uploading a .php file to a shared hosting server (like in 2002) to suit the narrative feels disingenuous to me as it doesn’t align with what I see PHP developers doing at all.
I actually like PHP a lot, and it's amazing how far it has come in the past 10 or so years, I just think way too many people assume you get the 2004-era PHP simplicity with all of the 2024-era PHP refinements, and you really don't. There's tradeoffs.
You are correct in that a lot of PHP use now is larger frameworks with asset compilation and cache clearing etc, but even when developing on large systems like that it is nice to sometimes be able to just manually tweak a file and refresh.
For R&D and quick tests, just uploading a quick & dirty php file to the server is a very useful language feature to have IMO.
Right, but unless you have an ftp server or quick ssh access and PHP isn’t doing any code caching that feature isn’t an advantage, how many developers are in that situation? Is this something you do?
If you’re running locally PHP spawns its own server which other runtimes have. If you’re running this on a server you’re most likely going to have app/code caching (apc Or opcache) switched on so you’ll need to restart the server anyway, in which case it’s not more advantageous than uploading a js file and restarting node.
These days all of that is built right into IDEs
> Is this something you do?
Yes. After linking my IDE to a remote location I can then noodle around with scripts to test whatever. The immediate nature of PHP means the instant you hit ctrl-s your changes are live online.
> If you’re running this on a server you’re most likely going to have app/code caching (apc Or opcache) switched on so you’ll need to restart the server anyway
In prod yes, but in dev environments all that is switched off as its not needed.
In PHP, I do. In other platforms, no. My personal PHP site is published by a git push to the server.
Of course there are viable strategies to avoid this specific issue, but they all introduce complexity of themselves.
The thing (most) other languages have is unicode support and concurrency. In PHP there is basically none of these.
Fast? You mean fast as in CPU bound tasks? 99.99% of PHP code is slow because of IO, and without any concurrency all the other languages beat PHP easily. If you need CPU bound work, you would not pick PHP (or any other scripting language) anyway.
In most benchmarks PHP (with Laravel/Symfony) is barely doing 500req/sec. Compare this to languages in the same space (like nodejs/python) and they run the same program and can serve 10K-30K req/sec.
Having said that python (a slow langauge) is still capable of doing heavy CPU bound tasks with libraries like numpy. Im not aware if PHP can install C dependencies with composer, like you can with pip.
- https://www.php.net/manual/en/book.ffi.php
While I'm at it:
PHP usually is doing poorly, even with its "cache", and when you realize that PHP devs actually are not PHP devs, but framework (like laravel/symfony) devs you need to account for the overhead of the framework too.
But you can have a look at the framework benchmarks game:
https://www.techempower.com/benchmarks/#section=data-r21&hw=...
No it’s not: it’s a MINOR update.
Another pet peeve of me is that the global namespace is littered with these utility functions that should be easily composable or maybe be methods on the objects themselves - and looks like PHP 8.4 adds four more `array_*` functions. For comparison, Python's builtin namespace has a total of 71 functions and a couple of exception classes. PHP's builtin namespace has more functions for dealing with arrays and now 58 of those are prefixed with `array_`.
It’s hard to build on a language used by so many, when you can’t modify the base. Python decided to do 2.7 vs 3 and fragmented the eco system terribly.
Adding any globals should be a carefully-considered change. User-defined functions are global by default, and although there are (now) much better ways to write PHP libraries, I can absolutely see some old library defining array_find (one of the new global functions in 8.4) in an incompatible way and breaking new code that attempts to use the built-in function.
Sure, you can’t touch the existing pile of globals, but at least stop pouring fuel on that particular fire…
Surrendering the global namespace to the language is not so bad an idea.
Again, an ancient enough codebase which contains a library using array_find will need enough upgrades to run on PHP8 much less PHP8.4 the change from array_find to something else is the least of your worries.
First page hits https://github.com/hawind/gdoo/blob/master/app/Support/helpe..., a PHP app last updated just two years ago (140 stars, 63 forks) which only supports PHP 8.x. Implementation is thoroughly incompatible with 8.4's array_find.
There are so many more examples. Lots of the hits are from codebases that have seen updates in the last few years. Many more are plugins or other addons for PHP frameworks or apps which are still widely used (WordPress, phpBB, etc.).
https://github.com/hawind/gdoo/blob/master/app/Support/helpe...
Is namespaced. You need to realize that \array_find is different from \Illuminate\Support\Arr\array_find.
There is zero chance of collision here. Totally compatible.
In any case, it wouldn't make sense for that file to be namespaced under Illuminate\Support\Arr, as most of those functions have nothing to do with arrays.
Anyway, it _could_ have been namespaced. It _should_ have been namespaced. I'm sad for the app author, but he should have seen that coming. His app was developed well after namespaces were introduced.
You're making impossible demands. PHP has chosen to be backwards compatible _to a certain point in the past_, and it is generous in what it decides to keep working.
To expect that 8.4 will care about not breaking some code that was written with 5.2 style is unrealistic.
You're making a time travel judgement. You're saying that PHP should have renamed everything back then to categorized namespaces and broken compatibility _way_ earlier, which is actually a much worse break than the break that actually happened.
"The author uses Composer! And Laravel"
So what? Can you explain what this means in this context?
To me, it looks like you're defending shitty code just to be able to attack PHP.
Someone else creates a function named array_something in the namespace. Maybe it already exists in earlier versions, maybe it happens to collide with one of the four introduced in 8.4. This function is accessible to you in the current scope. Now, you try to call the function like the way it is defined in the standard library. You get a very confusing error and spend 10 minutes trying to figure if it is you or PHP that is hallucinating. Turns out you have been inadvertently calling that other user defined function. The other user may be completely unaware of the fact that they created a function with naming collision.
To combat this, you need strong IDE help including static type checking, which is not always there for everybody. And it still doesn't help with cases where the user defined function collides with a new standard function.
Most "modern" languages have very few built-in functions in the global namespace. Another example is Go. Correct me if I am wrong, but I believe there is 0 function in global namespace in Rust. println! exists but that's a macro. In other words, the example I mentioned just never happens with these languages.
Not to mention the long list of junk you see in IDE when you type "array_".
I guess you haven't written much MATLAB.
However, it does implicitly include the entirety of the std::prelude namespace (https://doc.rust-lang.org/std/prelude/index.html) into every source file, as well as including every macro directly exported under std:: (including println!). This enables the unprefixed use of things like Result, Option, Some, Send, etc.
The prelude and std:: macros are the closest thing that Rust has to a global namespace, and even they can be disabled in crates that specifically request it.
PHP functions can be namespaced. I can just write myLib\array_find, otherLib\array_find. You choose what implementation you want when importing. IDE will pick the correct one.
So, zero chances of collision.
Don't write such meaningless words and waste your and other people's time when you don't even understand what people are talking about.
A tool to avoid collision was introduced decades ago. This tool was made proeminent by the language and the ecosystem (PHP-FIG, frameworks, books, popular PHP celebrities).
You literally have to had stopped programming PHP more than a decade ago to not understand namespaced functions.
https://phptherightway.com/#namespaces
This problem of collision was seen miles ahead, and people were gently introduced to the idea that the global namespace belongs to PHP builtins and you should not pollute it even further.
I would say it is consensus for the PHP community that if your code broke because you defined array_filter globally before 8.4, then your code sucks and you don't know PHP.
The design philosophy of PHP is to include whatever common methods would otherwise be in a popular library. (PHP actually began more as a library than as a language.) This differs from, eg, Python, but doesn't hurt.
The decision not not to make methods on the objects, but to include everything in the main namespace (so array_walk instead of Array.walk or Array()->walk etc) is another function of the same philosophy. It may not fit your idea of cosmetics, but there is nothing wrong with it.
On the other hand, I would love if PhP gained chainability ([].array_map.array_find()) and then the names would be a pain. If that ever happens there are solutions.
Property hooks mean that some language magic will turn a property access into a call to methods. It implies that `$this->x` has a different meaning if it's inside a hook or outside hooks. I've used this kind of feature (getters/setters) with JS code (and with Moose/Perl decades ago), and I wasn't convinced. Plain methods are more explicit, have less cognitive charge, and are easier to extend.
On the bright side, I'm glad that the language is still thriving. In 2021, I was worried when the foundation was created, especially as I read that Nikita Popov had left. He was the creator of PHP's JIT code, and at the time the only developer who could fully understand it. But it seems there was no need to worry. PHP is now longer "the elephant in the room" of web programming, but it's still a good language, with many active developers at its core.
__get / __set was doing that already and some frameworks very heavily rely on those.
> It implies that `$this->x` has a different meaning if it's inside a hook or outside hooks.
this is a valid critique but hopefully hooks will be super short and this won't be a major issue. Indeed, if your get is not an arrow function -- which only allows one statement -- then it needs a good thinking over whether this is indeed the best solution. Ie if your get is so complicated then perhaps a helper method is best and then you have get => $this->foo($this->thing) and that's the only place where $this->thing is special.
Even if a PHP project has a policy of short hooks, I think hooks impede clarity.
public string $countryCode
{
set (string $countryCode) {
$this->countryCode = strtoupper($countryCode);
$this->country = nameCountry($this->countryCode);
}
get => ...
In this short hook, the first line of the setter obviously uses the underlying property. But the second line of the setter...Does `$this->country =` use the setter even if it's in a hook (but not a `country` hook)?
Does reading `$this->countryCode` use the getter hook, even it's from a `countryCode` hook?
If not, is there a way to call the `countryCode` getter from this setter?
If quickly parsed the doc and the RFC, so I don't have answers (I suppose it's yes, no, no). But even if I knew how this code behaved, I would still think it's much more complex than plain methods.
To me it is obvious hooks won't use other hooks because that could lead to an infinite loop in a hurry
> Does reading `$this->countryCode` use the getter hook, even it's from a `countryCode` hook?
same
> If not, is there a way to call the `countryCode` getter from this setter?
There is although it's a bit tricky and not intuitive but I feel this falls under the "it is enough this is possible, there's no need for it to be easy": "Be aware, the detection logic works on $this->[propertyName] directly at compile time, not on dynamic forms of it like $prop = 'beep'; $this->$prop. That will not trigger a backing value." Using dynamic properties in what should be simple code should be rare enough this is not a problem. It's like a bridge convention, the benefits vastly outweigh the drawbacks.
I would expect hooks for properties other than `$this->contryCode` to be called inside of a `$this->contryCode` hook.
Btw the docs that are linked in the post[0] seem to be clearer than the example in the announcement. Reads less ambiguously to me.
[0]: https://www.php.net/manual/en/migration84.new-features.php#m...
Yet in Javascript:
test = { set a(x) { this.b = x }, get a() { return 2 }, set b(x) { this.c = 10 }}
Object { a: Getter & Setter, b: Setter }
test.a = 10
10
test
Object { a: Getter & Setter, b: Setter, c: 10 }
and yes it's possible to make infinite loops.
public string $countryCode { set (string $countryCode) { $this->_countryCode = strtoupper($countryCode); $this->country = nameCountry($this->_countryCode); }
echo $obj->prop
$obj->prop = 'bar';
not echo $obj->getProp();
$obj->setProp('bar');
Previously, it was very common to not expose a public property directly, even if it required no setter logic, because any future change to add setter logic would mean it has to become a setter method.
This results in potentially dozens of boilerplate getter/setter methods, that provide no actual benefit, but are necessary to avoid potential BC breaks in a future version of the library.
With property hooks, the property doesn't need to define any getter/setter logic initially - and adding a hook later to the `set` action doesn't change the way other code calls it.
So no, it isn't just "new syntax" now - it's a case of largely not needing to write/generate any of that boilerplate any more - it's just not needed, regardless of how that property's behaviour changes in future.
$this->foo
would look for a getFoo() method, and execute if it existed, or not if not. Felt like that was easier to reason about, fwiw, but I couldn't get it off the ground. Even then, there were multiple C#-style get/set proposals floating around, so this style seems to be the one more people like. Not a fan of the style, personally, and probably won't use these much directly any time soon. If it helps people maintaining libraries that I use to deliver code that is cleaner and more productive to them... I'm OK with that.
E.g. if a base class declares a variable it can potentially break its children. Whose at fault here?
I agree with your original comment though. And if the bypass of exisitng fields is badly wanted, somehow marking __get to disregard them makes more sense to me.
It also gives static analysis tools semantic information about the structure of your classes. It can group pairs of methods that deal with the encapsulation of fields.
An argument could be made for adding a sigil so the class user knows this isn't a dumb field, but then if someone wants to upgrade a dumb field to this, as Python encourages, they would need to modify every use.
But as you said — it’s been there in C# for a while and imho it’s a good abstraction over getters and setters. Even 2005-era IDEs could manage it fine, making it easy to access the property’s get/set code, so that it wasn’t really magical.
Maybe it’s a culture thing — most C# devs use IDEs. Not sure what PHP devs use, but I suspect tools like PhpStorm will make this easy to work with somehow. Devs using no-LSP editors will likely have a different view.
This is probably one of the big factors. I am also not a huge fan of “magic” even though I use IDE (vscode). I started off as a PHP dev, directly editing files on production server using vi. Any “magic” simply slowed me down.
Years later, now I can simply cmd+click to anywhere in code but it feels a bit off to me. Perhaps, I still miss my old days of dev.
The second job introduced PHPStorm, and a single page load can bounce through dozens of files and classes; it would probably be untenable without modern tooling.
The argument that C# choosing to offer features that lead to terser implementation seems orthogonal to where you write it. Behavior in property getters and setters may as well be completely hidden from the caller. But the standard expectation is that accessing and/or setting a property should be cheap and not involve much logic, which most of the code out there adheres to.
(and personally I'm finding it to be a more productive experience than any dynamically typed language which always feels like stone age regardless of the environment)
An IDE can highlight non-basic properties in a different colour from basic properties, or underline them or something. That would be difficult for an editor to do as part of normal syntax highlighting.
Why is it that after the human life span expires all of the code should be thrown away?
I remember when my host updated and php could no longer be opened with <? and my websites spilled their guts all over the screen.
Worse than that, it is possible to read it wrongly which is going to cause many nasty headaches for amateur developers of the future trying to debug PHP code.
I'm not bringing this up as a particular criticism of the language, I think it's fine. It is also an experience I have with lisp, where it is fun and easy to write but hard to read when coming back to it after a while away. I just don't think php is a simple language, on several levels. The semantics that I mentioned, combined with the mixing of paradigms, and the large and inconsistent standard library. You can write simple php but it takes a lot of discipline.
I guess thats a result of such wildly changing features in each version change. Each major version of PHP has brought in such drastic changes of concept and semantic that it is easy to start mixing them together and get confused looking code.
However I find this in a lot of other systems. Look at node.js, every release changes things so much that people regularly rewrite their entire code base multiple times to take advantages of the new features. Do a google search for guides on node programming a specific issue, and depending on how old it is it you will get wildly differnt approches and results. Popular and highly developed languages change often, and this will always cause issues between old and new.
I also think possibly node is our generation's php, with it being so fast-changing and there not being a community consensus about framework. So every complex node project is, like pre-laravel php, essentially a totally unique ad-hoc framework composed of a mix of libraries. Ruby and python aren't better languages than php or node, and rails and django aren't perfect, but to a large extent those languages avoid this problem just because everyone is using the same solutions.
Blame MySQL for that name, not PHP.
https://dev.mysql.com/doc/c-api/8.4/en/mysql-real-escape-str...
(And, if you're doing modern PHP, it's just PDO->quote().)
Are you thinking of stuff like magic quotes? mysql_real_escape is not part of an automatic anything. You manually use it to quote each value.
In hindsight I think it's a good thing, it's a pea that keeps princesses at a distance from the language.
Property hooks look awesome, they fix something that's my main pain point in PHP nowadays.
All these getters and setters manually coded make it feel like Java. Just completely boring and unusable without some fancy IDE that types all that boilerplate.
It is one great feature of C# that I'm glad PHP is adopting. This code is also easier to extend than the Java-like sea of getters and setters.
(I don't consider any mention of JS code as a valid comparison, if anything we are better ignoring JS existence unless forced to do some frontend)
"All these getters and setters manually coded make it feel like Java."
Project Lombok has solved that issue of manual boiler-plate getters and setters in Java. If you program regularly in Java it's worth having in your toolbox.Admittedly they come with some restrictions, but also some additional benefits.
Why? Property hooks aren't mandatory. If you want to keep using explicit getter/setter methods, you can do that. If you want to keep using implicit getter/setter hooks via __get/__set, you can do that. If you want to keep using plain property access, you can do that.
All this does, is allow features that previously relied on the black box of __get/__set to be exposed as real properties. This massively improves the scenario for anything that works via reflection, and makes a whole suite of bugs related to unintended behaviour, simply impossible.
From the outside it's just a property access. The whole point is you don't need to worry about whether it's a direct access or a getter/setter with logic, even if it changes from one to the other between versions.
> Get and set magic methods were a mistake too.
They're definitely not ideal, and thanks to this change they're no longer required for the vast majority of cases.
> Same with reflection.
Reflection is a mistake?
Yes, reflection is a mistake. It's a hotfix for problems caused by OOP. It's not actually necessary.
TypeScript is a good example. Some people write it like Java/C#, with classes, inheritance, reflection, dependency injection. Usually I can get that code down to 20-30% of original size after I cut out the last class (then I forbid the keyword in linter), and none of that is actually necessary or improved the situation.
Worry about? Or understand the fine minutia of how every property you fetch or store is handled?
> It makes my job much harder to do, much more hidden and arcane
I don't know how you do your job, but seeing a real (i.e. not a faux property handled by __get/__set) class member's implementation is literally 1 click away in any decent IDE. Clicking to see the potential hooks of a field vs clicking to see the getter or setter (which may or may not even be defined in the same class) is no different.
> Yes, reflection is a mistake. It's a hotfix for problems caused by OOP. It's not actually necessary.
Right. I guess that's why it's applicable to functional programming. To be a hotfix for OOP?
https://www-master.ufr-info-p6.jussieu.fr/2007/Ajouts/Master...
This way, at least, it's much more explicit. And this should probably only be used inside frameworks anyway, and not in "user-land" code.
For a long time now, PHP has been on a trajectory of trying to be everything to everyone, constantly bolting on features from every language that happens to drift by.
My observation has been that the people who are deeply invested in PHP are tired of being hazed online for using a "toy" language, so they're trying to adopt all of the complexity and problems of other languages, rather than just keeping things simple, which is what used to be PHP's primary strength.
It's not for people that compulsively talk about category theory and lenses five minutes into every programming conversation.
PHP to me, professionally, is nothing without Laravel. So as long as Laravel doesn't become more obtuse than it already is, it's all good.
I really dislike getters and setters, particularly when they allow async code. Now all the sudden you have a massive performance risk, it's all too typical to see junior devs doing expensive stuff in getters and now the whole application, exponentially, becomes slower.
Anything that _may_ involves magic is dangerous in large code bases.
Not only is it yet another global function in a namespace already chock full of random array helpers, it is extremely similar in both name and usage to array_search - a global function since PHP 4. Except, of course, that in typical PHP fashion, array_find’s argument order is ($array, $filter_callback) while the older array_search is ($search_value, $array).
There are literally hundreds of hits for existing, global, functions named array_find. If these are loaded from a library, they will break uses of the new built-in function in exciting ways. Yet, even with this mentioned in the RFC (https://wiki.php.net/rfc/array_find) it seems to have been no obstacle whatsoever to its inclusion - despite the fact that the implementation is literally three lines of code.
I have to question if the benefits of this global function really outweigh the benefits. PHP devs claim that other languages have these functions so PHP should too - but neglect to note that most languages don’t make them global functions (rather, they’re usually array methods or in some utility module).
Also people should be coding defensively with things like “if not defined” when implementing their own global helper functions (or avoid doing that at all)
Screwed? Just rename or move the function to a namespace in an IDE and it will update all your references?
The backward compatibility section found only 200-ish projects where array_find() was defined in global name space. For me that's a small price to pay for introducing the new function, and refactoring should BE easy when upgrading a project to PHP 8.4.
Adding array_find() to a namespace would be inconsistent. All other array_*() functions are global.
But I agree, there is already some inconsistency, and when adding a new function you have to choose which function to make it consistent and which one to make it inconsistent with.
My personal motivation was always to finally put an end to the getters/setters boilerplate.
With predictable results.
The difference between languages like PHP and more modern languages, is that the more modern languages have more airbags, for the bad code. It's still bad code, but it won't do as much damage.
PHP is likely to be around for a long time: https://w3techs.com/technologies/history_overview/programmin...
People building and creating awesome new things? Increase in happiness and empowerment? More efficient processes and increase in productivity? New businesses being born? Wealth and value being added to society?
I'd have to see the numbers on those ones.
But I am not a "gatekeeper" type, maybe you are confusing me for one. I'm a high school dropout with a GED, and have been staring up people's noses, all my life.
I just believe that any job we do, should be a good job.
Build on a sand foundation, and you'd better not go too high, or Bad Things Happen.
A good friend of mine has founded such a company, and boy, I loathe his code. But at the end of the day, he’s incredibly good at getting something out of the door now, which is working. A language that makes this possible, while still scaling to fully type-safe and optimized bytecode with a JIT compiler if needed is a good one.
For a lot of folks (especially hereabouts), “makes money” is the only valid metric.
That’s not my experience or personal PoV. I’ve been shipping (as opposed to “writing”) code, for over 30 years. Pretty much everything I’ve written, has made it out the door (although not always to subsequent success). I’m fairly familiar with what it takes to ship. I agree that it’s a relatively uncommon skillset, but I’ve also learned to ship stuff with a future. One of the projects I developed, took ten years to come into its own, and it had to stay solid for all that time, until the right team could take it over. The fact that the code is well-written, well-supported, and well-documented, is the main reason that it finally took off.
I don’t like PHP, and one reason, has been alluded in other comments. It’s a sand foundation. Over the years, it has been massively backfilled, and is now pretty damn robust, but the sand foundation is still visible.
But it is a useful tool, if wielded correctly. If used incorrectly, though, that’s another story, and there’s a lot of bad PHP out there.
I have heard a quote, attributed to Stroustrop, that goes “With C, you can shoot yourself in the foot. With C++, you can blow your whole leg off.” I’m skeptical he’s the one that said it, but it’s a truism.
I have a friend that is an arborist (tree guy). He’s really experienced, and really good at it, and makes great money.
Watching him use chainsaws, though, is kinda terrifying. For one thing, every one of his saws has the safety guard removed, and he’ll go monkeying up a tree, with three running chainsaws, hanging off him. He uses them like you’d use a fork at the dinner table, and can take down a huge locust tree, in an hour or so, alone.
He makes it look easy, but I’m not so idiotic, as to think I could emulate him.
We have a running joke, every time he sees me, he holds up his hand, showing that he still has all his fingers, because I told him that he would end up chopping off a finger or two.
There now exists a huge base of very good PHP programmers. It took quite a while to coalesce, but it’s here. I don’t claim to be one of them, but I have used the language to ship some fairly ambitious stuff, for the last quarter century or so, so I can wrangle it reasonably well.
The front end can have very little js and still be fully functional (like this website for example).
I am, however, pretty against selling crap. If people pay for my work, or even if they don't (most work I do, these days, is free), they have the right to expect that I did good work.
I do run into quite a few folks that write crap, know they write crap, but don't care, and expect people to pay top dollar for their crap.
If that's a "condescending" attitude, then guilty as charged.
(Neutral) PHP news -> PHP bad -> PHP not bad
Even if people didn't actually say "PHP bad"
I just hold my nose, write my backend, then get back to my frontend, as soon as possible.
Separately, IMO, languages should always be designed with bad programmers in mind - good programmers are going to figure out the right way to do things in any language but bad programmers are going to fire off every foot gun they can find. PHP has made efforts to remove or de-emphasize foot guns and it has evolved into a much safer tool to give someone.
From what I can see it's some pretty unbiased conclusion that's quite reflective of the truth.
Great metaphor. I might steal it for some model contract terms I'm doing (for an expanded version of my contract-drafting course materials). I'd been thinking of "guardrails," but "airbags" is far better.
So it developed features and functionalities as needed with a very pragmatic focus [0][1]
[0] the most clear example to me is that since functions are not values and cannot be assigned to variables the way to store a function in a variable is to simply assign its name as a string so $callable = "array_map" works. Or that the way to create a pointer to a method is to create a 2 element array of the object and the method name. I like this example because the way method pointers work in JavaScript is objectively worse as the "this" is easily lost. Some languages are built on CS principles and PL theory, others like Bash or PHP are just built to get shit done. I am glad that recent versions are fixing some of the oversight of this approach while keeping the pragmatic culture
[1] I am not sure of how true it is, but some claim that some of the standard functions have irregular names because the interpreter used the name length as hash key
Security: 31 Dec 2028
Bug Fixes: 31 Dec 2026
webRTC is a beast with the STUN server needs better handled elsewhere.
variable-length lookbehind assertions are now supported.
yay! I needed that so many times.
But I agree, a great feature!
PHP lovers generally don't like acknowledge that, but the PHP we've learned back-end development two decades ago is no more and that it's now as beginner unfriendly as Java was when we picked it.
It's a pity because there's nothing as beginner-friendly anymore. I think the blame is on people calling themselves “PHP developers” who never bothered learning more advanced languages after PHP and instead pushed PHP to reach parity with those, at the expanse of losing its soul.
I've done so periodically, and every time I go "this sucks, gimme the tooling".
What's sad is that it's not how people use and teach PHP today.
PHP's magic really is that you can write small scripts super easily to do small tasks, but somehow most PHP developers insist that “no PHP isn't for building small scripts” but “a real, professional back end language ready for mission critical enterprise requirements blah blah blah”, because surely that make them sound more serious as programmers, missing the point entirely…
(And sorry to all insecure PHP programmers out there, but for serious stuff using PHP is still equivalent to coding with handcuffs and you should really learn a second programming language at last because being a one trick poney really isn't as cool as you think it is).
The difference between it and Java is you’re not forced into the ClassObjectGetterSetterPropertyHookFactoryBean paradigm with PHP. You can continue to write concise, simple, elegant scripts that read from top to bottom and serve one page and serve it well. You don’t have to use any of these crazy newfangled features - I, for one, will be using none of them, and will be sticking to my if-else blocks and nested for loops that will be easy to read and grok when I (or someone else!) come back to the code two months down the line.
While PHP can still be uses like that today (and is still unmatched in terms of ease of use for simple stuff when used like that) it's been a long time since the PHP project and developer community stopped caring about this use-case.
If you write JSP, you don't even need that.
In my projects I sometimes emulate getters and setters using `__get()` and `__set()` but that's heavy-handed and requires lots of PHPDoc annotation for type checking. Property hooks look awesome!
I know the textbook answer is so that every single possible property can become some mutable chain of events so you can swap names or side-effects without the caller knowing, but I've yet to find a use for that in real life.
It just leads to builder patterns and other oddities like forced decorators like Java has everywhere. I felt like beans were the realization that perhaps we shouldn't be doing this.
But you might want to support string date times and the PHP object DateTime as well. So a typical well designed ORM converts multiple PHP types into a SQL string for saving. That's what the historical __set and __get are all about. This is called "mapping" by most ORMs and a very well designed modern one like Doctrine will actually have mappings for different SQL storage engines available.
Obviously it also has to handle the reverse.
I agree! That's why it's wild to allow a setter to do literally anything.
You aren't just setting a property, there is a conversion happening under the hood.
And the reason I hate it is that code that appears to be infallible, isn't. It can have arbitrary side effects, raise exceptions, have unbounded runtime, etc.
I sometimes use getters and setters to provide backwards compatibility. What was just maybe a simple data field a decade ago doesn't even exist anymore because some major system has changed and we aren't rewriting every single application when we can provide the values that they need easily enough.
If you know that setters exist then you already know that the code can do more. It's not a huge mental leap from property setting to method calls. You should never assume anything is infallible. I don't think classes should even expose raw fields.
In a lot of contexts you have something that requires a bit of code but really does behave like a property access, where it's more misleading to make it look like a method call than to make it look like a data access. E.g. using an ORM hooked up to SQLite embedded in the program. Or accessing properties of objects that you're using an EAV system or array-of-structs layout to store efficiently in memory. Or a wrapped datastructure from a C library that you're binding.
Of course if you make something look like a property that doesn't behave like a property then that's confusing. Programmers have to exercise judgement and only make things look like properties when they behave like properties. But that's not really any different from needing to name methods/objects/operators in ways that reflect what they do.
It makes for a cleaner OOP-y interface in which the caller only cares about actually exposed properties and not methods to get and manipulate hidden properties.
IMHO using properties directly is a much more natural way to talk about objects, than having a bunch of methods to get properties. Getters and setters also help ensure that methods are only for changing an object's state, not getting an object's state. For example:
class User{
public ForumsPost $LastForumsPost{
get{
if(!isset($this->LastForumsPost)){
$this->LastForumsPost = <get from database...>;
}
// Return the cached property so we don't keep hitting the database.
return $this->LastForumsPost;
}
}
}
print($user->LastForumsPost->Title);
print($user->LastForumsPost->PostId);
// instead of...
print(($user->GetLastForumsPost())->Title);
print(($user->GetLastForumsPost())->PostId);
Putting it behind a getter doesn't "hide" control flow. It just makes for a cleaner interface from the caller's perspective.
FWIW, I almost never use setters. Getters are are much more useful, especially for lazy-loading basic properties.
Then again, I'm not a fan of code that does magical things that aren't apparent. Makes it a lot harder to 1) reason about and 2) solve performance issues. I also don't want the overhead of function lookups for every property access.
Very often for a typical CRUD app, I as the caller don't really care how we got the `LastForumsPost`. It's just an object mapping that comes from some data source like the database. And if I do care, I could get it outside of the object, and set it myself.
It sounds like you got the gist but somehow you are in an area of programming where getter/setters aren't useful.
That's fine and okay. Part of growing up as a programmer is realizing your niche.
Most of the advice out there is deep, not broad. It's deeply connected with our niche and not necessarily broadly applicable.
I've been programming for 30 years, and getters/setters are both pointless and anti-productive.
They hide (potentially a lot of) code from people reading the program, which leads to a lot of "wtf" moments later.
Magic in general is bad, and it's the kind of "look how concise and clever I am" thinking that leads to unmaintainable software.
If setting a property isn't straightforward, make it private/protected and add explicit getter/setter methods. It's more work today and much less work later on.
That's fine and okay.
The only sibling comment of yours that bothered to even show code just demonstrated how they turned user.lastPost into a method that makes a hidden DB query.
Yet the benefits are supposedly so nuanced and hard to get across that merely asking about it gets a "you'll understand when you get some real experience ;)" comment from you.
I wrote about it here https://technex.us/2023/07/php-attributes-are-so-awesome-i-j...
seemed like a way to backport poor librar/framework choices to have IDE support but not for new code
Readonly has been discussed but it wasn't part of the property hooks RFC and is a separate issue/feature. This is just a natural consequence of having private setters.
In case people aren’t aware, DOMDocument is dangerous. You can’t parse HTML with an XML parser; so everyone currently using DOMDocument for HTML would benefit by replacing that with DOM\HTMLDocument immediately, eliminating both security and corruption issues.
Have you tried using an HTML parser?
When a hook is called, inside that hook $this->[propertyName] will refer to the “unfiltered” value of the property, called the “backing value.” When accessed from anywhere else, $this->[propertyName] calls will go through the relevant hook. This is true for all hooks on the same property. This includes, for example, writing to a property from the get hook; that will write to the backing value, bypassing the set hook.
A normal property has a stored “backing value” that is part of the object, and part of the memory layout of the class. However, if a property has at least one hook, and none of them make use of $this->[propertyName], then no backing value will be created and there will be no data stored in the object automatically (just as if there were no property, just methods). Such properties are known as “virtual properties,” as they have no materialized stored value.
Be aware, the detection logic works on $this->[propertyName] directly at compile time, not on dynamic forms of it like $prop = 'beep'; $this->$prop. That will not trigger a backing value.
Feels like too much magic to me that a property access can mean different things depending on context, but I'm not a PHP user, so I don't get a vote.I've never been a fan of this kind of magic, and I wonder how other languages deal with this case.
self.__dict__['property_name']
And if those PHP rules were in python you could just write: @property
def property_name(self):
return self.property_name
In actual python though, that would infinitely recurse.var milliseconds: int = 0
var seconds: int:
get:
return milliseconds / 1000
set(value):
milliseconds = value * 1000
https://learn.microsoft.com/en-us/dotnet/csharp/programming-...
edit: the properties reference now documents the 'field' keyword too
It's pretty similar to what PHP provides here, except that PHP uses "$this->propname =" and C# uses "field =".
Edit: As someone involved in the RFC, it's somewhat funny, because we considered a special variable like "$field" or "$value" too magic, and C# does just that with a field keyword.
I used it in Unity projects to have serialized/inspectable values exposed through properties:
[field: SerializeField]
public int MyProperty { get; private set; }
In fact, you can imagine what their argument WOULD have been if setting `$this->countryCode` inside the setter for `countryCode` DID result in infinite recursion.
When it comes to PHP, whatever it does, on HackerNews and in the rest of the industry, it just results in a bunch of people complaining about it. Disappointing, and unprofessional.
Though what would you personally call "the thing that turns it into bytecode"?
Conversely php already has other places where context affects property accesses, and IME it's not the problem you make it out to be.
The backing value is effectively private to everything but its own get/set methods, that seems fairly straightforward to me.
I didn’t know what they were, so I clicked. I was bewildered that I had never run into them before, used them in my own code, or seen them used by others. Come to find out they’ve only been around for about a day!
Reminds me of some of the lovely expressibility and syntactic sugar that’s pulled me to other languages lately. Glad to see it make its way into PHP.
$foo[1:-3:2]
Instead of the usual array functions, which could stick around for BC. That would be amazing> but you’ll never have to use them
But i do. I now cant tell if im accessing a property or a getter. With custom functions (like getFoo()) it was annoying, but still obvious, now its just magic and library authors will 100% start to abuse this feature allover.
But you never could have been sure for the last 20-ish years either? What if my library you're using (and you can be certain at least one of them does) includes a class like this:
class Foo
{
public function __get($prop) {
return match ($prop) {
'bar' => $this->doSomeHeaveIoOp(),
'baz' => query_db()
default => throw new RuntimeError('Invalid property ' . $prop)
};
}
}
The difference to a proper getter is that you never knew Foo supported the virtual properties "bar" and "baz"; the only chance you'd have was if I added doc comments, or a note in the documentation; otherwise, good luck finding it in the source code. Compare to: class Foo
{
public string $bar
{
get => $this->doSomeHeaveIoOp();
}
public string $baz
{
get => query_db();
}
}
This is definitely better. It is discoverable by both you and your IDE; it allows proper documentation; it is not magic, but code; it won't go out of sync with the __call handler; it allows static analysis and typing.> I really dont want to access a property and have it do any sort of magic behind the scenes. This is just bad. Now a property lookup could do some IO, or even throw an exception. PHP should not aim to please the framework of the month, but it in fact seems like Laravel lobbied this into PHP core.
Without any kind of property overloading, you're missing out on a lot of API simplification that other languages have long taken for granted. You may not like it, by stuff like pandas in Python wouldn't be possible at all without heavy overloading, and I prefer a world with pandas over one without it. Ergonomics are important; not writing tons of useless boilerplate code is, too.
I think this very much depends on your perspective. "Simplicity" isn't a single dimension, you'll always face trade-offs at some point.
While the language did get slightly more complicated with the introduction of property hooks, they also simplify a lot of real-world code. Instead of handling all getter/setter logic inside two big functions, there's now a direct relation between each property and its getter/setter logic. There's a lot of value in that!
Of course you can say that's not good practice and therefore shouldn't be made simpler, but then you're still offloading complexity onto the individual program and developer. They are using these features in production code, and keeping the language simpler means that every class using getters/setters has its own custom logic. That's not simple at all!
To give a real-world example, Typescript supports many things that probably wouldn't be necessary if the language were designed from the ground up. But it's meant to add type safety to Javascript, so it has to support adding types to patterns that aren't best practice. But this also simplifies adding types to the existing ecosystem by a large margin, which is arguably the reason it has become the de-facto best practice for writing frontend apps.
Really good thing when doing some operations on monetary values. No need to use bc_* functions anymore it seems.
get => \sprintf("%s_%s", $this->languageCode, $this->countryCode);
in the first code example? Is it a lambda?
https://wiki.php.net/rfc/property-hooks
It follows the syntax of arrow functions
https://github.com/rectorphp/rector/issues/8701
https://github.com/nikic/PHP-Parser/commit/7b0384cdbe03431c4...