I'm just a PHP programmer for work, but I worry about the orientation PHP has chosen. As French people say: better is the enemy of good (Le mieux est l'ennemi du bien). The two new language features bring a higher language complexity for dubious gains. I hope I won't have to work with these.
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.
This feature has been in C# since about 2.0, and it's been an overall positive. It reduces boilerplate and inconsistencies in different programmers doing the boiler-plate differently.
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.
I am also in favour of the change. I would argue though that because it's been in C# for so long, then yes it reduces inconsistencies across programmers/codebases, but introducing it to a language as mature as PHP is now though, might not have the same outcome.
It’s interesting to consider the “magic” criticism in the context of languages like Zig, where devs actively want no hidden control flow. And Properties beyond simple { get; set; } are definitely hidden control flow.
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.
> 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.
How do you expect properties to interact with IDEs?
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)
> How do you expect properties to interact with IDEs?
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.
Properties have been in C# since the very first version. And those, in turn, were largely inspired by Delphi, with the honorable mention of Visual Basic.
This encapsulation style for OOP is standard; the language should support it and not require additional design patterns or IDE tools.
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.
And yet in this wonderful laboratory of a multitude of languages the growth of a language doesn't destroy as much as it offers a larger variety of choice - with good features being stolen by other languages and less good features remaining niche and unique.
Look at C++ or Perl, languages with many features that are quite frequently hated. When you add features, you force users to learn those features. It is not just a "variety of choice" because you know that other people will also take some of those choices and you'll probably have to edit their code at some point. Features are not just added to the language, but imposed on its users. The need to add features to a language is a sign that what you already have is insufficient. Features should be added to the bottom of a language, increasing its expressive power, rather than to the top of the language forming cruft around the edges. It would be much more productive for us to collectively abandon PHP in favour of sane languages, ones which don't require the continual addition of more features to cover their foundational shortcomings.
The imposed part was funny. I hope I wont have to use anything new. I only had a mild panic attack reading they changed exit() but it was a false alarm.
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.
I just checked the code samples from the article/post.
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.
While these are certainly a better option automatically generated default getters and setters have been pretty do-able through magic methods for a while now - and with the more robust reflection we now have access to they can be implemented in a safe manner. I'm still pretty happy to hear we're getting it as a baked in feature.
I feel the opposite: this brings simplicity and pragmatism back to PHP. Gone are the years of bowing to the verbosity of Java, sacrificing a dynamic powerful language at the altar of 1995's OOP paradigms.
How? It just got way more invested into OOP... It's now much harder to understand my code at a glance. The Java feeling isn't because I have to write a lot of code, it's how the code works.
> It's now much harder to understand my code at a glance.
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.
Others will use them and I will need to use their libraries. Get and set magic methods were a mistake too. Same with reflection. Again and again, deeper into the Java 6-land, while Java itself went away a decade ago.
> Others will use them and I will need to use their libraries.
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.
I need to worry about it if I'm using it in an app. In what job you don't need to worry about what code are you executing? This is the whole point. It makes my job much harder to do, much more hidden and arcane - not worth it to save few characters.
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.
Just to set the record straight, Nikita is not the creator of the PHP JIT code, that is Dmitry and he is employed by Zend owned by Perforce working mostly on this.
I'd tried to put together an RFC years ago to introduce groovy-style accessors in PHP.
$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.
I'm not a fan of that kind of magic in my languages but such a thing was already easily doable in PHP. You could just have a base class that implements __get and __set so that $this->foo automatically calls $this->getFoo().
That advice doesn't always work in real life; otherwise for every compiler or linter check in any language, we could drop all those checks and tell the programmers not to do 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.
I agree with what you wrote and I am familiar with that saying.
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.
A big part of this is about code that's consumed by third parties: i.e. library code.
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 comment is referring to the practice of always writing getters and setters even when they are not needed, then feeling frustrated by the amount of boilerplate.
I agree. PHP is such a simple language to follow but now with these property hooks, if you dont fully understand how they work then the code becomes unreadable due to the magic.
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 wrote php exclusively only for about two years early in my career but have had to come back to it periodically every once in a while. I find it one of the most difficult languages to be a visitor in. The way variable, array, and class semantics mix can make it hard to decipher the exact behavior of a chunk of code. Especially if you have a mix of "old" procedural and modern OO php, which the projects I work on do.
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.
> Especially if you have a mix of "old" procedural and modern OO php
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 agree. I think for largely cultural and timing reasons, and also its success, there are a lot more long-lived php codebases for this to play out in compared to most languages.
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.
I'm not certain about OP's objection but for me it's less the function name and more the terrible history of how PHP tried to automatically fix SQL injection and instead made everything a thousand times worse. If you're not using bound parameters for user data you're taking a huge risk and making your life multitudes more difficult. PHP's PDO is by far the better option at this point but it suffers from poor enough usability that I've built my own wrapper for it at two different companies.
Why should I blame MySQL? It's the PHP developers that decided to introduce it and not change the name, plus have several functions that don't do the right thing as well.
> Property hooks mean that some language magic will turn a property access into a call to methods.
__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.
> hopefully hooks will be super short and this won't be a major issue.
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.
> Does `$this->country =` use the setter even if it's in a hook (but not a `country` hook)?
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.
> Does `$this->country =` use the setter even if it's in a hook (but not a `country` hook)?
To me it is obvious hooks won't use other hooks because that could lead to an infinite loop in a hurry
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.
The two new language features bring a higher language complexity for dubious gains.
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.
PHP was never particularly simple, it has always had a diverse standard library and lots of similar but subtly different builtins and a rather funky type system and so on.
It's not for people that compulsively talk about category theory and lenses five minutes into every programming conversation.
Of course it's not required but when you start pushing the boundaries of a language with the goal of achieving a clean interface, obscure features you wouldn't normally resort to become appealing. I dislike all of the magic around Laravel's Eloquent ORM - model relationships, query builder, abuse of ForwardsCalls trait, etc, but at the same time I can appreciate how "clean" it all looks once it's put together.
I was curious about why setting `$this->countryCode` inside the setter for `countryCode` didn't result in infinite recursion. Turns out this is spelled out in the RFC, but not in the docs:
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.
What's magic is that `$this->foo` refers to different entities in different contexts despite the same value of `$this`, not that one of the entities to which it refers can be private.
The dynamic form is really not needed very often. Should it ever become a problem triggering a loop you can just set and get from a private property with a different name. But then also 90% of getters and setters are really simple so it will feel as magic as gravity.
Whenever I read about a new PHP release on HackerNews I always come away disappointed with the discussion. You are right, the "magic" that the top comment is complaining about is a non-issue, and I say that as someone who really strongly dislikes "magic" behavior.
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.
Funny world where a feature to prevent infinite recursion -- at a moment in the runtime lifecycle where that would make sense -- is somehow viewed as dangerous footgun magic (addresed to: several replies under this comment).
There are many times I would love to have an automatic backing field in C#. C# will do that if you use the default setter but in cases where that's not possible I dislike having to both declare a separate field and having it available to the rest of the class outside of the setter.
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.
The `field` keyword also already existed in C#, to add attributes to the backing field of automatic properties, so I think the argument was easier there.
I used it in Unity projects to have serialized/inspectable values exposed through properties:
[field: SerializeField]
public int MyProperty { get; private set; }
I think it being a keyword does change how magic the feature feels. "$field" and "$this->name" make me think that this behavior is playing by the normal rules of the language but is implemented using some trickery to edit the local scope of this function to add or modify variables. When it's a keyword it says this is a special form and that this is a feature of the language itself.
.NET 9 has so much good stuff in it. Thing that I randomly realized I wanted only to find out it works in 9 is dictionary access with "related" types (so like if you have a ReadOnlySpan<char> you can use that to find a value from a string in the dictionary via a value type created off the dict).
Lua has rawget() and rawset() to bypass the magic methods, which is used a lot inside/outside the metatables (objects with magic methods) to avoid magic loops I guess.
The original RFC that led to this feature originally used a special variable to indicate the backing store; that was overwhelmingly disliked, as it was "magic".
Conversely php already has other places where context affects property accesses, and IME it's not the problem you make it out to be.
I'm most excited for property hooks. Having getters and setters as part of the language syntax was something I dearly missed from my C# days, nearly two decades ago.
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.
They are very useful when modeling CRUD-dy objects, because you can lazy-load infrequently-accessed child object using getters.
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);
Your example seems like an argument against getters. In the case of fetching posts like this I always want to know whether some logic is running. What if the operation is really heavy? When it's a getter you would think it's already loaded.
I appreciate the detailed answer, thanks. However, It feels like $a->foo vs $a->foo() is a personal preference that hides the fact you're doing work behind the scenes.
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.
Getters aren't for everything. But for a basic CRUD case like above, they're a nice way of having a clean OOP contract by demarcating a clear difference between properties, which are pieces data attached to the object that the caller can read and write, and methods, which are the caller can change the object's or app's state, or perform actions with possible side-effects.
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's a matter of OOP modeling. Object methods are better reserved for performing actions with side effects, or complex logic or calculations, and not for getting state or simply setting public properties; and as a caller, I don't really care about the implementation details of (in my above example) getting the last forums post, I just care that it's a property I can access. (Maybe it came from a cache and not the database? Maybe it was set earlier in the script? I don't care.)
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.
I've always been a fan because if a property turns from a simple value to a more complex interaction that might involve DB operations or other side effects then if a getter is in place you can modify the logic without needing to update widespread code.
If you are replacing a performant property with a slow network call, you are being negligent if you aren't reviewing all the callers to make sure that is okay.
In a typical ORM when you do say $Model->DateTime = 1732046457; the __set is actually checking the value and seeing oh it's an integer. Treat this as a unix timestamp. But when you run save() the query is actually converting that to a Y-m-d H:i:s (for MySQL/MariaDB). This doesn't actually happen until you run save() when it makes the query and runs the network call. Most of that time it's actually storing everything in memory.
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.
Some light weight ORMs do not require you to define all the properties ahead of time. They pull the field names from the table and generate the model record on the fly for you. This generally lets you prototype really fast. Laravel's Eloquent is known to do this. It's also useful for derived SQL fields when you use custom queries or join from other tables. Also kinda fun to do it for properties backed by a method. As in $record->isDirty will run isDirty() under the hood for you. All these things can be documented with PHPDoc and static analyzers handle it just fine.
A method is supposed to be an action and a property is supposed to be data. So I don't see the desire to disguise setting data as a "setting method" rather than using the syntax of assignment.
> A method is supposed to be an action and a property is supposed to be data.
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.
There are many ways to make code misleading. You can write a method called plus and have it do multiplication. You can write a method that sounds safe but looks dangerous. Every language relies on programmers exercising judgement when writing the program.
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's abstraction. Your not supposed to care that the setter is doing anything. The class is providing you an interface -- what it does with that interface is not your concern. I hate to quote Alan Kay but all objects should just respond to messages. Whether that message corresponds to a method or a property is pure semantics.
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.
Getters are great for manipulating data on the way out. For instance, inside the class you might store a raw representation of the data like a json string, but when reading an attribute you will decode that json string into a datastructure and pluck a certain item. I use dynamic getters often to wrap a raw value with some kind of fallback: do this if no data is present, do this if the data is present but is ancient (ie a seamless upgrade of a v1 internal datastructure to a v2 output), etc. For setters, I try not to have implicit side effects but in certain cases where it makes sense, it is nice to be able to centralize that logic: "if this value changes, ensure x and y occur" which you get "for free throughout your codebase when you use a setter, versus having to go thru the entire codebase and sprinkle the do_x() and do_y() calls (which then becomes additional footprint to maintain and worry about)
Getters/setters are just as easily associated with hidden behavior that makes systems worse.
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'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.
I've written some auto-getter and setter spawners and with reflection it's pretty do-able to implement proper type checking as long as the type you want to check for matches the type declared on the property. PHP meta-coding is actually quite advanced and pretty accessible compared to what I've done in other languages.
Great PHP release. Better stack traces for closures, performance improvements (always nice), HTML5 support, lazy objects and much more. Great work and a big thanks to everyone involved!
Am I the only one excited about bcmath objects? Not so much because of arithmetic operators, but mostly because now we can do data types checks without resorting to wrapper classes. Nice!
I went to look at array accessor overloading today and saw “Property Hooks” in the sidebar (under “Classes and Objects”).
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.
Glad to see PHP still chugging along after all these years. It's the language I started with as a freelancer more than a decade ago and I still remember having books on my desk learning the proper way to do things, as you had to work around all the unsafe things the language would let you do and which unfortunately led to it getting a bad rep.
One of the issues with creating a language that is easy to use (PHP, BASIC, and many modern languages), is that people who aren't good at programming, will use it.
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.
My understanding is that PHP was sort of started by people that did not plan for it like a "serious" programming language, but rather as a tool for specific applications.
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
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.
I dunno, we just about got rid of the whole "cosmic rays" discussions for computers and finally are getting realistic about the bugs that occur in computers are from bad C code and bad hardware. Neither of those things are easy to use and they have done probably more damage than crappy php code does.
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?
> More efficient processes and increase in productivity
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.
I agree that it’s especially sad then to see someone with your background taking that position then. While we’re busy discussing inelegant abstraction patterns in code, a bunch of founders have written the foundations of a successful startup with horrible code, creating work for better programmers (but worse founders) in the process.
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.
So you know someone that is an expert at building sand foundations, and selling them? That’s a good thing, and something to emulate?
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.
I don't think OP was trying to be condescending. Even if they were, they're still right. A lot of modern languages are designed with bad programmers in mind.
One thing I'd like to highlight though is that there were mountains of terrible advice out there around PHP from novices who had figured out a way to make things work - around the time of PHP 5.3 the community ran an ambitious initiative to crawl through sites like Stack Overflow and replace bad answers with correct ones. The community realized how dangerous it was for novices and how much all the bad advice was hurting the reputation of the language and they took decisive action to fix it.
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.
Wasn't trying to be, but it's my experience that people will deliberately assume the very worst intentions, behind whatever I write, so it's a losing proposition, trying to be circumspect.
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.
I use PHP for my backend work. I am not an outstanding PHProgrammer, because, quite frankly, I don't like the language, but I'm good enough to write fairly robust, secure, and performant backends.
I just hold my nose, write my backend, then get back to my frontend, as soon as possible.
readonly is rather a misnomer for "writeonce" in PHP. And as such it also disallows repeated assignment in class scope, while private(set) has no such restriction.
Readonly might have different semantics (as it does in C#).
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.
Property hooks are the headline feature, but they seem like something I'd rarely use in practice. It is nice to have the option available though, in case I need to add extra logic to a property without breaking everywhere that it's accessed.
Looks like PHP is going even further towards Java. I really loath getters/setters with passion. I really wish PHP instead focused on getting some sort of concurrency builtin and proper unicode string literals. That should be the main focus, instead of copying various features from Java.
Look at it this way: getters/setters are the only conceivable way Laravel could ever get something like static analysability, and I wouldn’t call that framework particularly Javaian. What they do is make a whole sleuth of magic code actually discoverable and understandable, but you’ll never have to use them if you don’t want to. And even if a library under the hood implements a getter, all you’ll see of that is $foo->bar. There really have been both more invasive and more Java-style features in the past than this one.
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.
> 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.
> I now cant tell if im accessing a property or a getter.
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.
Its obvious that this has been possible before. It always was bad practice to do so, no matter of the language. I for see people starting returning `$this` and having these really complex chains of getters. Its going to turn into even more spaghetti, because now there is two syntaxes for basically doing the same thing. It seems simplicity is just something everyone wants to abandon.
> It seems simplicity is just something everyone wants to abandon.
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.
If you want any evidence that terrible language design is alive and well in PHP, look no further than the new array_find function.
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).
Your IDE should help you with the argument order if that's an issue for you. It has never been a problem for me and I don't understand the argument. I get that it might be confusing the few first times you use these functions, but if you use them every day you just remember the argument order.
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.
Can you elaborate? I'm not sure what you mean, but IDEs definitely make reading code easier. IDE does code formatting, highlighting, argument hints, jumping to methods, declarations, and various hover effects on variables, methods, and classes. Some IDEs offer call graphs and other more advanced tools. Not to mention static analysis that works out of the box in PHPStorm (and I'm sure it can be set up in VSCode).
I mean that no matter what you do, the arguments will always be shown in an inconsistent order and you’ll have to mentally swap them around. You probably could configure your editor to visually swap them, but that sounds like a really bad idea.
array_search() has the same needle, haystack order as in_array() or array_key_exists().
array_find() has a callback and takes the same order as array_walk() or array_filter().
It's just array_map() that's different, but that's because it can take multiple arrays.
And writing your own function with a prefix already used by PHP in the global namespace is just not a very good idea.
array_find parameter ordering is modeled after other non-variadic array functions, e.g. array_filter also having ($array, $callback) order.
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.
I had similar thoughts, but do appreciate the additional mb_ functions bringing multi byte support to some remaining functions.
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)
"if not defined" doesn't help, if your own `array_find` doesn't have the same signature and semantics than the new global then you're screwed. You'd want the opposite: overwrite it if it already exists in the global scope (dunno if that's easy / how that'd work in PHP)
even though it's a much criticized language I still build my applications using it. for example the site https://chat-to.dev was written entirely in php
I have a question to the PHP-in-production crowd: how long do you wait before migrating to higher version of PHP? Is the first release usually already fine, or is it better to wait for a few months and let someone else catch the early errors?
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 usually wait 6mo to a year in order for composer dependencies I use to get updated. Then its usually a trivial upgrade. I've upgraded sooner before for simpler projects though and things are usually pretty stable upon release.
For a project like Wikipedia, stability and continuity are far more important than latest and greatest feature support; in fact, there's an argument to be made to avoid newer language features to ensure consistency, that is, if new features are used, they should be used everywhere at once. Else you end up with frankenstein code where every file could use a different style.
It helps there are automated tools for converting old style K&R function declarations to the new ANSI/ISO style, like protoize.
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
The last few point releases have been very stable, so normally within a week or so. Obviously this relies on having a pretty good test suite (we run ~8,000 tests in ~60 seconds so not too bad for us).
We start making sure our application runs on the next version around the time Beta 1 comes out.
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.
For PHP 8.3, we upgraded production during the RC phase, but were prepared to revert back to an older one by not relying on the new functionality. Docker makes this easy.
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.
There's an entire ecosystem of companies doing support and proprietary security patches and even framework-bearing polyfills for php 5. So much of the web runs on php 5 that is never ever going to be updated. It's a mini-industry unto itself.
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.
I find it pretty fascinating that what used to be a beginner-friendly language, with limited capabilities but that is very easy to get started with, has now evolve to a bloated monster full of advanced features that you can't expect to know entirely, with a complex framework and tooling ecosystem to support it.
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 picked up php a couple of months back after 30 years of ignoring it, mostly after reading a comment somewhere about using php for shell scripting. Very easy to read the language documentation on php.net and get moving. Powerful standard library. I haven't found another language (not an exhaustive claim) that offers language documentation as helpful as php's for a beginner.
Sure, and I'm still using PHP every once in a while for personal needs (I have a handful of small personal websites built this way: PHP has this insanely cool capability of being just a scripting language for a basic dynamic website).
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).
PHP is still definitely a beginner-friendly language that is still very easy to get started with. Setup is as trivial as it always has been, copy a .php file onto a server and you’re good to go. No complicated frameworks or “deployment process” needed if you don’t want them (and most people don’t need them).
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.
I still do, every once in a while when I'm using PHP for my personal websites, but it's been a very long time since I've updated the (very basic) dependencies I'm using since they all migrated to the “enterprise-grade tooling of the day” after 2010 or something.
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.
You trade complexity in source code for complexity in language. For example, property hooks will make code that currently uses other methods to accomplish that task much easier to read and write.
DOM\HTMLDocument is a huge boon. Niels Dosche incorporated lexbor into PHP to do so, and it maintains the same interface as DOMDocument once it’s instantiated.
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.
If needed you can get LTS from Zend. They still offer security fixes for 7.2 until end of 2026 and 8.3 is supported until end of 2029. But that's just if upgrading every second or third year is too expensive to you.
Oftentimes many of the significant new PHP features are to fix the shortsighted implementation in the previous ones - for example this method chaining with `new` - there was precedent already,
C++ got it right well before PHP even existed and with the very same arrow operator that PHP borrowed (and so did Java and JavaScript with .), so the question is why did PHP have to get it wrong at first and for so long.
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_`.
The global namespace change would break everything. It’s unlikely they would ever do something like that.
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.
They could help by not adding any more cruft to the global namespace.
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…
That would be some really old library. Already in 2012 when Composer was released there was PSR-0 and today almost all libraries are Composer managed and using a namespace following PSR-4 which itself is ten years old. A library that old would almost surely not run on PHP 8 unchanged anyways.
Surrendering the global namespace to the language is not so bad an idea.
Suppose I want to add some new code to an old website? Or I want to gradually upgrade an ancient code base - twelve years is not so old for PHP, when ancient frameworks like Wordpress are still alive and kicking.
If we hitched language development on Wordpress we would still be on PHP4 as they refused to join gophp5 some seventeen years ago.
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.
Seriously? 2k results for array_find in PHP on GitHub: https://github.com/search?q=array_find++language%3APHP&type=.... RFC authors (https://wiki.php.net/rfc/array_find) explicitly noted over 600 hits for definitions of array_find, around 30% of which are not false positives - that is, there's a good possibility that there are 200+ implementations of global array_find in just open-source projects.
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.).
IIUC the `use` declaration is including the target namespace (https://laravel.com/api/8.x/Illuminate/Support/Arr.html) in the current scope, not declaring a namespace itself. If you wanted to create a namespace you need the `namespace` declaration, which this file lacks. `helpers.php` is directly included by both `index.php` and `artisan` and the functions are thereby in the global scope of the entire app.
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.
8.4 is not even backwards compatible with perfectly working code written to target 8.x. It doesn't materially matter that the code is written in "5.2" style if it was meant to run on only 8.x. Even that style point is arguable. The author uses Composer! And Laravel! And mostly does use namespaces, outside of a few useful helper functions that are reasonably global so they don't keep needing to import them or use namespace prefixes.
I think the bigger issue is that these lessons have already been learned. However, PHP (Personal Home Page) was not created carefully by someone to become a fully fledged language. It grew into that role organically which is why it has so many warts in hindsight.
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.
Whats the problem with global namespace littered with utility functions. Do they get in the way? They hurt you? They whisper in your ear? Or with the badly named functions? Or the type juggling? Do they eat your soul?
It could be much easier for user defined functions to collide with standard functions, especially when it happens unintentionally.
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_".
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.
Your previous comment makes little sense and your reply is just angry and explains nothing.
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.
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.
Rust namespaces everything in the standard library to std:: or core::, and has a clear distinction between them.
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.
Does anyone really FTP files like its 2003? Even in PHP you deploy with some sort of tool, like docker etc. The reload page thing is pretty much solved in all languages with a watcher. entr is a universal one, thats written in C and fast as anything.
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.
Seems like that requires some sort of ad-hoc PHP extension. These are not in the standard PHP distribution? I would not want to rely on some random PHP extension that maintained by a single guy for anything production grade. Compare that to something like tensorflow thats backed by a HUGE community of maintainers.
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!)
There are of course ways to get "reload page, see results" to work even with Python. After all, computers are touring complete.
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.
Maybe I'm missing something, but PHP is not more efficient in that regard.
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.
> 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.
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 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.
With standard opcaching and jit enabled, the reparsing step doesn’t happen.
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.
To get to know Laravel, I forced myself to build a project with it for a few weeks and I did not like it.
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:
It’s difficult for me to trust a framework backed by venture capital (https://blog.laravel.com/accel-invests-57m-into-laravel). There are too many incentives to prioritize making money, which makes it easy to overlook developer experience. I rather use Symfony instead.
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 do it all the time, thank you very much. For a simple web app I don't even need a framework - you can literally create a simple files/folder structure, include files, include folders and get stuff done. An yeah, you can also upload the files by FTP.
For something more complicated I'd use WP or Code Igniter depending on the specific project. And then you can again just SFTP those files to the server.
That only works if your application has reasonably few users. Otherwise, your upload will result in some requests hitting a partially completed code base overwrite, that is, only some of your changes have been uploaded at request time, possibly leading to an error. This is a nice strategy for a hobby project, but it plain does not work for a business.
That's not true. For mission-critical apps we route the traffic to the stable version while uploading the new version. This may be similar to what most CI tools do but it doesn't change the fact that we just upload the files and can do it as we see fit. Another good solution is to simply switch off a feature for maintenance while uploading it's files. If your software is well planned, that's a breeze and still keeps the whole process very simple.
>but does anyone realistically make websites or web apps that way?
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.
> For R&D and quick tests it 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.
> Right, but unless you have an ftp server or quick ssh access
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.
.. and as soon as you stop just uploading files (which, as you say, nobody does), a lot of the other advantages go away also. Laravel and Symphony both make PHP much slower. If you are going to have sane routing, you're probably going to need to tweak your websever to behave, well, less like a web server. There's a decent chance you'll need to clear a cache after you upload your changed files if you want to actually see those changes.
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.
Python’s packaging and dependencies system is lacking, but trying to get better. But too many choices not always compatible nor working perfectly right (should you use pip, poetry or pipenv? Well, you see…)
Python's packaging system is worse, but Javascripts packages / standard libraries are far worse than Python.
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.
Node:
- easy to deploy:
If you dont complicate yourself, with cpanel or plesk hosting is just matter of copy the files.
- Terabytes of npm packages (even too much :))
- A non bloated and nice language (JavaScript)
- Optional type checking with typescript if you want.
- Non blocking IO
Most PHP apps use a deployment method where a symlink gets set to a directory with a new version of the code. Because of how opcache works this has no impact on running requests, while new requests get handled with the new code.
You can use the same strategy with PHP. Preload all your scripts in opcache. Once you're done making changes, reset your opcache.
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.)
I'm pretty sure many PHP dev don't know about OPCache. Many of my colleagues don't know for sure. My point is "be aware of the state of your app code and what you execute", and so be aware of the shortcomings of this deployment "strategy". It's sure perfectly fine and easy for small apps / low traffic / not critical apps. I just want to point that it's not inherently good enough and definitely not the universal way to deploy a PHP app.
Yeah, I've seen cases where even git is not fast enough. That's when opcache preload comes into play. It's still better than manually uploading files over FTP.
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.
Not sure where you got that from. It’s equally if not faster then python, faster then Java, slower then compiled language, faster then ruby. Loses to NodeJS most of the time.
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.
My point was less about the leaderboards and more about it being pointless comparison. But I was looking at https://web-frameworks-benchmark.netlify.app/result?l=java,p... though I don't really know what people consider "best benchmark" for raw vs raw. Once you start throwing in apache/nginx/proxies/what ever it really starts to balance itself.
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.
reply