[RFC] Primary Constructors

php.internals

Rob Landers

64 days ago
Hello internals, I'd like to put forward Primary Constructors <https://wiki.php.net/rfc/primary-constructors> for comment. An implementation PR will be opened later today (UTC), and the RFC updated with this discussion thread. — Rob

Nick

64 days ago
On 29.06.26 08:17, Rob Landers wrote:
> Hello internals, > > I'd like to put forward Primary Constructors > <https://wiki.php.net/rfc/primary-constructors> for comment. > > An implementation PR will be opened later today (UTC), and the RFC > updated with this discussion thread. > > — Rob
Hey Rob, thanks again for the work you did put into this! Unfortunately you are not allowing bodies for primary constructor. This means there will be no way to work around the "`readonly` classes don't allow property hooks" problematic; except using conventional constructors (`private(set)` is not the same; hence my previous "readonly hooks" RFC). If "use conventional constructors, if you need `readonly` classes" stands, then the same can be argued for everything else in the RFC. Why would we introduce a whole new syntax, but then only make it work for limited use-cases such as "trivial validation or normalization"? Your example: ``` class Money( public readonly int$amount, public readonly string$currency = 'USD', ) {} class Money{ public function __construct( public readonly int$amount, public readonly string$currency = 'USD', ) {} } ``` Pro: - two lines less - construction at top of class (love it!) Con: - primary constructor body not supported - new syntax with gotchas to keep in mind - for readonly classes it adds two property `readonly` in exchange to writing two lines less - requires refactoring to conventional constructor as soon as it gets "non-trivial" (I'd argue a lot of things that do not work are actually still trivial). Given the limitations, is this alone adding enough value? What is the actual gain? Adding a new syntax to the language but then lock it down as proposed here does not feel right. As is, I feel the proposal is too thin, it would unfortunately be yet another feature that feels mid, incomplete, and introduces new gotchas. Sorry, IMO, not worth it. If we would say we want new and neat syntax sugar to write constructors at the top of the class, I am all for it! But then they would need to work like actual constructors (otherwise it's not just sugar), without accepting yet another set of limitations and weirdness's in a different place. What I roughly have in mind: ``` readonly class Point(public int $x, int $id = 0) extends Base($id) implements Foo {    // normal constructor body behaviour } => {   // class body } ```
-- Cheers Nick

Rowan Tommins [IMSoP]

64 days ago
On 29/06/2026 07:31, Nick Sdot wrote:
> > If "use conventional constructors, if you need `readonly` classes" > stands, then the same can be argued for everything else in the RFC. >
For the record, my view is completely the opposite: if the new syntax allows everything a normal constructor does, just in a slightly different position, it will lead to endless style discussions of which to use. In fact, I don't think even property hooks belong in a primary constructor; they're ugly enough in a constructor promoted property. I think it's perfectly fine to have short-hands that only cover specific use cases, and longer forms that let you express more complex use cases. It's one of the reasons I voted against the recent RFC to allow an extra reassignment of properties in the constructor [https://wiki.php.net/rfc/promoted_readonly_constructor_reassign].
-- Rowan Tommins [IMSoP]

Nick

64 days ago
Hey Rowan, On 29.06.26 16:00, Rowan Tommins [IMSoP] wrote:
> On 29/06/2026 07:31, Nick Sdot wrote: >> >> If "use conventional constructors, if you need `readonly` classes" >> stands, then the same can be argued for everything else in the RFC. >> > > For the record, my view is completely the opposite: if the new syntax > allows everything a normal constructor does, just in a slightly > different position, it will lead to endless style discussions of which > to use.
That's a fair opinion, and not unexpected. Personally, I oppose introducing more inconsistencies to PHP. Looks like that we need to have these discussions then?
> In fact, I don't think even property hooks belong in a primary > constructor; they're ugly enough in a constructor promoted property.
It's a feature that was accepted to PHP and exists already. So it doesn't really matter whether we (I agree -- shouldn't be used for anything more than eg a neat callback) find it ugly and we have to deal with its existence. You are basically making a point for allowing primary constructor bodies. Having them allows to have a readonly classes with primary constructors, and assign properties in their body. Neat.
> I think it's perfectly fine to have short-hands that only cover > specific use cases, and longer forms that let you express more complex > use cases. It's one of the reasons I voted against the recent RFC to > allow an extra reassignment of properties in the constructor > [https://wiki.php.net/rfc/promoted_readonly_constructor_reassign].
For the record, my view is completely the opposite: without allowing primary constructor bodies the added value of adding the new syntax is nearly non-existent. Quoting from your message in the first thread to this:
> I don't think we should add extra syntax to the language just to > change people's habits. If you want a constructor body as the first > thing in the class, you can do that right now.
...because the exact argument can be made from either point of view. To quote you again: "all or nothing"; bodies should be allowed.
-- Cheers Nick

Rob Landers

64 days ago
Hi, Nick, On Mon, Jun 29, 2026, at 10:47, Nick Sdot wrote:
> Hey Rowan, > > On 29.06.26 16:00, Rowan Tommins [IMSoP] wrote: > > On 29/06/2026 07:31, Nick Sdot wrote: > >> > >> If "use conventional constructors, if you need `readonly` classes" > >> stands, then the same can be argued for everything else in the RFC. > >> > > > > For the record, my view is completely the opposite: if the new syntax > > allows everything a normal constructor does, just in a slightly > > different position, it will lead to endless style discussions of which > > to use. > > > That's a fair opinion, and not unexpected. Personally, I oppose > introducing more inconsistencies to PHP. Looks like that we need to have > these discussions then?
There's no inconsistency here. This syntax doesn't prevent you from using readonly classes. It only prevents you from initialization/validation in a body because there are no bodies. If you need a body, use a constructor -- that's what they're for. The RFC makes it plain that bodies are rejected and why. If you can provide a concrete reason why they should be allowed with a completely new syntax, that would be a good follow-up RFC; or even a competing one. If you disagree with the reasoning, I'd love to hear it. — Rob

Nick

64 days ago
Hey Rob, On 29.06.26 19:03, Rob Landers wrote:
> Hi, Nick, > > On Mon, Jun 29, 2026, at 10:47, Nick Sdot wrote: >> Hey Rowan, >> >> On 29.06.26 16:00, Rowan Tommins [IMSoP] wrote: >> > On 29/06/2026 07:31, Nick Sdot wrote: >> >> >> >> If "use conventional constructors, if you need `readonly` classes" >> >> stands, then the same can be argued for everything else in the RFC. >> >> >> > >> > For the record, my view is completely the opposite: if the new syntax >> > allows everything a normal constructor does, just in a slightly >> > different position, it will lead to endless style discussions of which >> > to use. >> >> >> That's a fair opinion, and not unexpected. Personally, I oppose >> introducing more inconsistencies to PHP. Looks like that we need to have >> these discussions then? > > There's no inconsistency here.
The inconsistency is that we will have two kind of constructors. One that has a body, one has not. It's a completely new concept, but you talk about syntax sugar.
> It only prevents you from initialization/validation in a body because > there are no bodies.
Right. Which makes them useless in endless situations.
> If you need a body, use a constructor -- that's what they're for.
But then I cannot benefit from the position (at the top of the class) of the primary constructors, which I really would like. Shouldn't we try to make each new feature as useful as possible? What are the actual blockers to not support primary constructor bodies?
> The RFC makes it plain that bodies are rejected and why.
It's possible that I missed where the "why" was explained, but I don't see explanations apart from "expressed in property hooks"; which I am here pointing out will not work in readonly classes. Could you please make the "why" more clear in the RFC?
> If you can provide a concrete reason why they should be allowed with a > completely new syntax, that would be a good follow-up RFC; or even a > competing one. If you disagree with the reasoning, I'd love to hear it.
The concrete reason is what I already mentioned: it will not work with readonly classes and `private(set)` is not the same. It's not that readonly for value projects is a rare thing.
> — Rob
-- Cheers Nick

Rob Landers

64 days ago
On Mon, Jun 29, 2026, at 13:42, Nick Sdot wrote:
> Hey Rob, > > On 29.06.26 19:03, Rob Landers wrote: >> Hi, Nick, >> >> On Mon, Jun 29, 2026, at 10:47, Nick Sdot wrote: >>> Hey Rowan, >>> >>> On 29.06.26 16:00, Rowan Tommins [IMSoP] wrote: >>> > On 29/06/2026 07:31, Nick Sdot wrote: >>> >> >>> >> If "use conventional constructors, if you need `readonly` classes" >>> >> stands, then the same can be argued for everything else in the RFC. >>> >> >>> > >>> > For the record, my view is completely the opposite: if the new syntax >>> > allows everything a normal constructor does, just in a slightly >>> > different position, it will lead to endless style discussions of which >>> > to use. >>> >>> >>> That's a fair opinion, and not unexpected. Personally, I oppose >>> introducing more inconsistencies to PHP. Looks like that we need to have >>> these discussions then? >> >> There's no inconsistency here. > > The inconsistency is that we will have two kind of constructors. One that has a body, one has not. It's a completely new concept, but you talk about syntax sugar.
There's only one constructor; PHP doesn't allow you to have different constructors. You choose the spelling that makes the most sense for your class and objectives. This is just declaration, not logic.
>> It only prevents you from initialization/validation in a body because there are no bodies. > > Right. Which makes them useless in endless situations. >
Of all the PHP code on my machine (production application code, frameworks like symfony/laravel/doctrine) ... it's about 30-40% of all constructors that are completely empty. I also sent an LLM to look further at things that could benefit from this (ie, promote properties on older code instead of just empty constructors). It came back with 71% of Laravel and 61% of Symfony that could use Primary Constructors. I'm not saying they should go rewrite their code or anything like that, but that basically means at least 1-2 out of 3 classes written in a given codebase could use primary constructors to simplify their construction and make it easier to skim. My 2¢: I don't think this is useless.
>> If you need a body, use a constructor -- that's what they're for. > > But then I cannot benefit from the position (at the top of the class) of the primary constructors, which I really would like. Shouldn't we try to make each new feature as useful as possible? What are the actual blockers to not support primary constructor bodies?
There is nothing preventing a follow-up RFC from being proposed, but as well as anonymous classes, it deserves its own RFC. There's a lot of syntax to bike-shed, as well as questions like "if I give a body, can I still use a parent constructor shortcut?" It is not straightforward and nor should it be. I fully explored this in the Records RFC, and many people pushed back on the logic, even pointed out unsoundness in what seemed like an otherwise sound design.
>> The RFC makes it plain that bodies are rejected and why. > > It's possible that I missed where the "why" was explained, but I don't see explanations apart from "expressed in property hooks"; which I am here pointing out will not work in readonly classes. Could you please make the "why" more clear in the RFC?
Will do.
>> If you can provide a concrete reason why they should be allowed with a completely new syntax, that would be a good follow-up RFC; or even a competing one. If you disagree with the reasoning, I'd love to hear it. > > The concrete reason is what I already mentioned: it will not work with readonly classes and `private(set)` is not the same. > It's not that readonly for value projects is a rare thing.
Where do you see in the RFC that it won't work with readonly classes, because that would be an error on my part? It explicitly does not prevent you from using any type of class except enums. If you're referring to the usage of primary constructors in combination with hooks and readonly, that's an orthogonal issue that belongs to the interaction between hooks and readonly and is far out of scope from this RFC. That is a limitation that exists today and this RFC does not strive to address it.
> >> — Rob > -- > > Cheers > Nick >
— Rob

Nick

63 days ago
Hey Rob, On 29.06.26 21:35, Rob Landers wrote:
> On Mon, Jun 29, 2026, at 13:42, Nick Sdot wrote: >> >> Hey Rob, >> >> On 29.06.26 19:03, Rob Landers wrote: >>> Hi, Nick, >>> >>> On Mon, Jun 29, 2026, at 10:47, Nick Sdot wrote: >>>> Hey Rowan, >>>> >>>> On 29.06.26 16:00, Rowan Tommins [IMSoP] wrote: >>>> > On 29/06/2026 07:31, Nick Sdot wrote: >>>> >> >>>> >> If "use conventional constructors, if you need `readonly` classes" >>>> >> stands, then the same can be argued for everything else in the RFC. >>>> >> >>>> > >>>> > For the record, my view is completely the opposite: if the new >>>> syntax >>>> > allows everything a normal constructor does, just in a slightly >>>> > different position, it will lead to endless style discussions of >>>> which >>>> > to use. >>>> >>>> >>>> That's a fair opinion, and not unexpected. Personally, I oppose >>>> introducing more inconsistencies to PHP. Looks like that we need to >>>> have >>>> these discussions then? >>> >>> There's no inconsistency here. >> >> The inconsistency is that we will have two kind of constructors. One >> that has a body, one has not. It's a completely new concept, but you >> talk about syntax sugar. > > There's only one constructor; PHP doesn't allow you to have different > constructors. You choose the spelling that makes the most sense for > your class and objectives. This is just declaration, not logic.
Dude...
> >>> It only prevents you from initialization/validation in a body >>> because there are no bodies. >> >> >> Right. Which makes them useless in endless situations. >> > > Of all the PHP code on my machine (production application code, > frameworks like symfony/laravel/doctrine) ... it's about 30-40% of all > constructors that are completely empty. I also sent an LLM to look > further at things that could benefit from this (ie, promote properties > on older code instead of just empty constructors). It came back with > 71% of Laravel and 61% of Symfony that could use Primary Constructors. > I'm not saying they should go rewrite their code or anything like > that, but that basically means at least 1-2 out of 3 classes written > in a given codebase could use primary constructors to simplify their > construction and make it easier to skim. > > My 2¢: I don't think this is useless.
I did not say it is useless, I did say it is useless in endless situations. And it is avoidable, by allowing a body.
>>> If you need a body, use a constructor -- that's what they're for. >> >> But then I cannot benefit from the position (at the top of the class) >> of the primary constructors, which I really would like. Shouldn't we >> try to make each new feature as useful as possible? What are the >> actual blockers to not support primary constructor bodies? > > There is nothing preventing a follow-up RFC from being proposed, but > as well as anonymous classes, it deserves its own RFC. There's a lot > of syntax to bike-shed, as well as questions like "if I give a body, > can I still use a parent constructor shortcut?" It is not > straightforward and nor should it be. I fully explored this in the > Records RFC, and many people pushed back on the logic, even pointed > out unsoundness in what seemed like an otherwise sound design.
Well, this is going to be more of a general thing, nothing exclusive to your RFC... But this is how we end up with more and more half finished and awkward (to not say broken) features in PHP. While I understand and generally support "get the minimum in and iterate later" from the perspective of RFC authors, reality shows that "later" can be either years later or very likely never at all. Though, we will have to deal with the unfinished/broken "minimum" solutions. How many more of these do we want to add? To name three from the top of my mind: 1/ we still don't have stringable enums 2/ we still don't have a solution for widening the type of a promoted property's set hooks (hi #16638), and of course 3/ my beloved readonly hooks themselves. ;) You know, I don't argue for the sake of it. It's just too often that my concerns turn out to be valid, eventually. See my comment to #16638; we probably should just have skipped hooks on promoted properties unless there would have been a solution before release. Now we have to deal with this inconsistent and broken behaviour for forever (?), because people are already busy with the next features. If we already not get such features right (no blame to anyone -- it's complex!), imagine what a disaster introducing native inline generics (with all its "good follow up RFC") could have lead to, if they would have made it in the language before PHP even has the will to agree on an internals approved official PHPDoc types spec that can prove itself over a few years without breaking code. Point is, I care. That's why I voice my concerns.
> "if I give a body, can I still use a parent constructor shortcut?"
My opinion: usage of parent constructor shortcut forbids manual parent constructor calling, and vice versa. Makes sense to me, and sounds easy to reason about. Other people could see that different, yes. But that's why I think it should be discussed *now*, instead of postponing to likely never, and then again have a "half nice" feature to deal with. Will the discussions be more complex? Yes. Will they take much longer? Yes. Could it lead to that the feature doesn't get in at all, not even in the minimum version? Yes. But I am honestly at a point where I think that's a good thing. Better nothing than yet another "mid" one that is annoying.
>>> The RFC makes it plain that bodies are rejected and why. >> >> It's possible that I missed where the "why" was explained, but I >> don't see explanations apart from "expressed in property hooks"; >> which I am here pointing out will not work in readonly classes. Could >> you please make the "why" more clear in the RFC? > > Will do. >>> If you can provide a concrete reason why they should be allowed with >>> a completely new syntax, that would be a good follow-up RFC; or even >>> a competing one. If you disagree with the reasoning, I'd love to >>> hear it. >> >> The concrete reason is what I already mentioned: it will not work >> with readonly classes and `private(set)` is not the same. >> It's not that readonly for value projects is a rare thing. > > Where do you see in the RFC that it won't work with readonly classes, > because that would be an error on my part? It explicitly does not > prevent you from using any type of class except enums. If you're > referring to the usage of primary constructors in combination with > hooks and readonly, that's an orthogonal issue that belongs to the > interaction between hooks and readonly and is far out of scope from > this RFC. That is a limitation that exists today and this RFC does not > strive to address it.
Respectfully, your answer is misleading. The limitation exist, yes. But with your proposal you introduce a new layer to the problem that your proposal of course should strive to address. Conventional constructors do have a body that allow working around the limitation. You intend to add new syntax that deliberately prevents a workaround by not allowing a body. So, please, "not my beer" is a little too blunt here. How the feature is proposed makes it less adaptable, and again introduces new gotchas. Bad for us, bad for the language. Should harder discussions, caused by body behaviour, really be a reason to add another "well, just good enough" in?
-- Cheers Nick

Tim Düsterhus

63 days ago
Hi Am 2026-06-29 21:20, schrieb Nick Sdot:
> Well, this is going to be more of a general thing, nothing exclusive to > your RFC... But this is how we end up with more and more half finished > and awkward (to not say broken) features in PHP. While I understand and > generally support "get the minimum in and iterate later" from the > perspective of RFC authors, reality shows that "later" can be either > years later or very likely never at all. Though, we will have to deal > with the unfinished/broken "minimum" solutions. How many more of these > do we want to add? > > To name three from the top of my mind: > 1/ we still don't have stringable enums > 2/ we still don't have a solution for widening the type of a promoted > property's set hooks (hi #16638), and of course > 3/ my beloved readonly hooks themselves. ;)
I consider (1) and (3) to be fundamentally wrong from a semantic perspective, i.e. a “Won’t fix”, not a missing feature. For (2) I agree that the situation is unfortunate, but as you say:
> […] See my comment to #16638; we probably should just have skipped > hooks on promoted properties unless there would have been a solution > before release.
… here the correct fix would've been making the feature *smaller* rather than *larger*, which is the opposite of what you are proposing here. And for this RFC I agree with Rob that not allowing an explicit body with “primary constructors” is the correct choice to prevent confusion: The limitations are a feature that makes classes using “Primary Constructors” easy to reason about. “Reduce typing” alone is not a sufficiently strong argument in favor of a new feature. As an example, promoted properties are not just “fewer characters to type”, but have the added benefit of “keeping data types in sync between parameter and property”, thus reducing mistakes. I don't see such a benefit for primary constructors if they are capable of doing everything a regular constructor can. Best regards Tim Düsterhus

Nick

63 days ago
Hey Tim, On 30.06.26 03:37, Tim Düsterhus wrote:
> Hi > > Am 2026-06-29 21:20, schrieb Nick Sdot: >> Well, this is going to be more of a general thing, nothing exclusive >> to your RFC... But this is how we end up with more and more half >> finished and awkward (to not say broken) features in PHP. While I >> understand and generally support "get the minimum in and iterate >> later" from the perspective of RFC authors, reality shows that >> "later" can be either years later or very likely never at all. >> Though, we will have to deal with the unfinished/broken "minimum" >> solutions. How many more of these do we want to add? >> >> To name three from the top of my mind: >> 1/ we still don't have stringable enums >> 2/ we still don't have a solution for widening the type of a promoted >> property's set hooks (hi #16638), and of course >> 3/ my beloved readonly hooks themselves. ;) > > I consider (1) and (3) to be fundamentally wrong from a semantic > perspective, i.e. a “Won’t fix”, not a missing feature. > > For (2) I agree that the situation is unfortunate, but as you say: > >> […] See my comment to #16638; we probably should just have skipped >> hooks on promoted properties unless there would have been a solution >> before release. > > … here the correct fix would've been making the feature *smaller* > rather than *larger*, which is the opposite of what you are proposing > here. > And for this RFC I agree with Rob that not allowing an explicit body > with “primary constructors” is the correct choice to prevent > confusion: The limitations are a feature that makes classes using > “Primary Constructors” easy to reason about. “Reduce typing” alone is > not a sufficiently strong argument in favor of a new feature. As an > example, promoted properties are not just “fewer characters to type”, > but have the added benefit of “keeping data types in sync between > parameter and property”, thus reducing mistakes. I don't see such a > benefit for primary constructors if they are capable of doing > everything a regular constructor can.
Yes, as I proposed back then. However, I disagree. Making it "smaller" is not always automatically the solution. Here it is making it bigger. Because making it bigger makes primary constructor behave the same way as conventional constructors (with the benefit to be in the class header, which I find awesome). I don't see why I should not be allowed to write something like this: ``` final readonly class Foo(Collection|array $value) implementsBar { $this->items = $value instanceof Collection ? $value : new Collection($value); }=> { // class body private Collection $items } ``` How is this not easy to reason about? How is this not *less* confusing? It offers the same functionality as a normal constructor. It also would not prevent parent class constructor shortcuts or whatever. It in fact would not change/remove anything the RFC wants, and it is purely additive. If my understanding of Rob is correct, we just would need to find an agreement on how parent constructor stuff works.
> “Reduce typing” alone is not a sufficiently strong argument in favor > of a new feature.
Not sure how reduce typing is related to the argument I am making? My point is that I *want* primary constructors, but that they should allow bodies to have the same workarounds at hand to deal with readonly and hook issues as with conventional constructors.
> Best regards > Tim Düsterhus
-- Cheers Nick

Rob Landers

63 days ago
On Mon, Jun 29, 2026, at 21:20, Nick Sdot wrote:
> Hey Rob, > > On 29.06.26 21:35, Rob Landers wrote: >> On Mon, Jun 29, 2026, at 13:42, Nick Sdot wrote: >>> Hey Rob, >>> >>> On 29.06.26 19:03, Rob Landers wrote: >>>> Hi, Nick, >>>> >>>> On Mon, Jun 29, 2026, at 10:47, Nick Sdot wrote: >>>>> Hey Rowan, >>>>> >>>>> On 29.06.26 16:00, Rowan Tommins [IMSoP] wrote: >>>>> > On 29/06/2026 07:31, Nick Sdot wrote: >>>>> >> >>>>> >> If "use conventional constructors, if you need `readonly` classes" >>>>> >> stands, then the same can be argued for everything else in the RFC. >>>>> >> >>>>> > >>>>> > For the record, my view is completely the opposite: if the new syntax >>>>> > allows everything a normal constructor does, just in a slightly >>>>> > different position, it will lead to endless style discussions of which >>>>> > to use. >>>>> >>>>> >>>>> That's a fair opinion, and not unexpected. Personally, I oppose >>>>> introducing more inconsistencies to PHP. Looks like that we need to have >>>>> these discussions then? >>>> >>>> There's no inconsistency here. >>> >>> The inconsistency is that we will have two kind of constructors. One that has a body, one has not. It's a completely new concept, but you talk about syntax sugar. >> >> There's only one constructor; PHP doesn't allow you to have different constructors. You choose the spelling that makes the most sense for your class and objectives. This is just declaration, not logic. > Dude... >> >>>> It only prevents you from initialization/validation in a body because there are no bodies. >>> >>> Right. Which makes them useless in endless situations. >>> >> >> Of all the PHP code on my machine (production application code, frameworks like symfony/laravel/doctrine) ... it's about 30-40% of all constructors that are completely empty. I also sent an LLM to look further at things that could benefit from this (ie, promote properties on older code instead of just empty constructors). It came back with 71% of Laravel and 61% of Symfony that could use Primary Constructors. I'm not saying they should go rewrite their code or anything like that, but that basically means at least 1-2 out of 3 classes written in a given codebase could use primary constructors to simplify their construction and make it easier to skim. >> >> My 2¢: I don't think this is useless. > I did not say it is useless, I did say it is useless in endless situations. And it is avoidable, by allowing a body. >>>> If you need a body, use a constructor -- that's what they're for. >>> >>> But then I cannot benefit from the position (at the top of the class) of the primary constructors, which I really would like. Shouldn't we try to make each new feature as useful as possible? What are the actual blockers to not support primary constructor bodies? >> >> There is nothing preventing a follow-up RFC from being proposed, but as well as anonymous classes, it deserves its own RFC. There's a lot of syntax to bike-shed, as well as questions like "if I give a body, can I still use a parent constructor shortcut?" It is not straightforward and nor should it be. I fully explored this in the Records RFC, and many people pushed back on the logic, even pointed out unsoundness in what seemed like an otherwise sound design. > Well, this is going to be more of a general thing, nothing exclusive to your RFC... But this is how we end up with more and more half finished and awkward (to not say broken) features in PHP. While I understand and generally support "get the minimum in and iterate later" from the perspective of RFC authors, reality shows that "later" can be either years later or very likely never at all. Though, we will have to deal with the unfinished/broken "minimum" solutions. How many more of these do we want to add? > > To name three from the top of my mind: > 1/ we still don't have stringable enums > 2/ we still don't have a solution for widening the type of a promoted property's set hooks (hi #16638), and of course > 3/ my beloved readonly hooks themselves. ;) > > You know, I don't argue for the sake of it. It's just too often that my concerns turn out to be valid, eventually. See my comment to #16638; we probably should just have skipped hooks on promoted properties unless there would have been a solution before release. Now we have to deal with this inconsistent and broken behaviour for forever (?), because people are already busy with the next features. If we already not get such features right (no blame to anyone -- it's complex!), imagine what a disaster introducing native inline generics (with all its "good follow up RFC") could have lead to, if they would have made it in the language before PHP even has the will to agree on an internals approved official PHPDoc types spec that can prove itself over a few years without breaking code. > > Point is, I care. That's why I voice my concerns. >
The irony here is that this is a follow-up RFC born weeks after someone reminded me about this part of it. It was abandoned due to comments like this. I do think I have a track record of attempting interesting RFCs and then abandoning them because it's clear nobody wants them. And that's fine. As I've stated multiple times in this thread, it would explode the complexity of the RFC. Probably half of it would just be about constructors. That's clear it should be a separate RFC, and if I did present it, you (or someone else) would argue that it needs to be broken up -- just like the records RFC. I show up, because I care as well; but damn, everyone can't always have cake. — Rob

Nick

63 days ago
On 30.06.26 03:47, Rob Landers wrote:
> > > On Mon, Jun 29, 2026, at 21:20, Nick Sdot wrote: >> >> Hey Rob, >> >> On 29.06.26 21:35, Rob Landers wrote: >>> On Mon, Jun 29, 2026, at 13:42, Nick Sdot wrote: >>>> >>>> Hey Rob, >>>> >>>> On 29.06.26 19:03, Rob Landers wrote: >>>>> Hi, Nick, >>>>> >>>>> On Mon, Jun 29, 2026, at 10:47, Nick Sdot wrote: >>>>>> Hey Rowan, >>>>>> >>>>>> On 29.06.26 16:00, Rowan Tommins [IMSoP] wrote: >>>>>> > On 29/06/2026 07:31, Nick Sdot wrote: >>>>>> >> >>>>>> >> If "use conventional constructors, if you need `readonly` >>>>>> classes" >>>>>> >> stands, then the same can be argued for everything else in the >>>>>> RFC. >>>>>> >> >>>>>> > >>>>>> > For the record, my view is completely the opposite: if the new >>>>>> syntax >>>>>> > allows everything a normal constructor does, just in a slightly >>>>>> > different position, it will lead to endless style discussions >>>>>> of which >>>>>> > to use. >>>>>> >>>>>> >>>>>> That's a fair opinion, and not unexpected. Personally, I oppose >>>>>> introducing more inconsistencies to PHP. Looks like that we need >>>>>> to have >>>>>> these discussions then? >>>>> >>>>> There's no inconsistency here. >>>> >>>> The inconsistency is that we will have two kind of constructors. >>>> One that has a body, one has not. It's a completely new concept, >>>> but you talk about syntax sugar. >>> >>> There's only one constructor; PHP doesn't allow you to have >>> different constructors. You choose the spelling that makes the most >>> sense for your class and objectives. This is just declaration, not >>> logic. >> Dude... >>> >>>>> It only prevents you from initialization/validation in a body >>>>> because there are no bodies. >>>> >>>> >>>> Right. Which makes them useless in endless situations. >>>> >>> >>> Of all the PHP code on my machine (production application code, >>> frameworks like symfony/laravel/doctrine) ... it's about 30-40% of >>> all constructors that are completely empty. I also sent an LLM to >>> look further at things that could benefit from this (ie, promote >>> properties on older code instead of just empty constructors). It >>> came back with 71% of Laravel and 61% of Symfony that could use >>> Primary Constructors. I'm not saying they should go rewrite their >>> code or anything like that, but that basically means at least 1-2 >>> out of 3 classes written in a given codebase could use primary >>> constructors to simplify their construction and make it easier to skim. >>> >>> My 2¢: I don't think this is useless. >> I did not say it is useless, I did say it is useless in endless >> situations. And it is avoidable, by allowing a body. >>>>> If you need a body, use a constructor -- that's what they're for. >>>> >>>> But then I cannot benefit from the position (at the top of the >>>> class) of the primary constructors, which I really would like. >>>> Shouldn't we try to make each new feature as useful as possible? >>>> What are the actual blockers to not support primary constructor bodies? >>> >>> There is nothing preventing a follow-up RFC from being proposed, but >>> as well as anonymous classes, it deserves its own RFC. There's a lot >>> of syntax to bike-shed, as well as questions like "if I give a body, >>> can I still use a parent constructor shortcut?" It is not >>> straightforward and nor should it be. I fully explored this in the >>> Records RFC, and many people pushed back on the logic, even pointed >>> out unsoundness in what seemed like an otherwise sound design. >> >> Well, this is going to be more of a general thing, nothing exclusive >> to your RFC... But this is how we end up with more and more half >> finished and awkward (to not say broken) features in PHP. While I >> understand and generally support "get the minimum in and iterate >> later" from the perspective of RFC authors, reality shows that >> "later" can be either years later or very likely never at all. >> Though, we will have to deal with the unfinished/broken "minimum" >> solutions. How many more of these do we want to add? >> >> To name three from the top of my mind: >> 1/ we still don't have stringable enums >> 2/ we still don't have a solution for widening the type of a promoted >> property's set hooks (hi #16638), and of course >> 3/ my beloved readonly hooks themselves. ;) >> >> You know, I don't argue for the sake of it. It's just too often that >> my concerns turn out to be valid, eventually. See my comment to >> #16638; we probably should just have skipped hooks on promoted >> properties unless there would have been a solution before release. >> Now we have to deal with this inconsistent and broken behaviour for >> forever (?), because people are already busy with the next features. >> If we already not get such features right (no blame to anyone -- it's >> complex!), imagine what a disaster introducing native inline generics >> (with all its "good follow up RFC") could have lead to, if they would >> have made it in the language before PHP even has the will to agree on >> an internals approved official PHPDoc types spec that can prove >> itself over a few years without breaking code. >> >> Point is, I care. That's why I voice my concerns. >> > > The irony here is that this is a follow-up RFC born weeks after > someone reminded me about this part of it. It was abandoned due to > comments like this. > > I do think I have a track record of attempting interesting RFCs and > then abandoning them because it's clear nobody wants them. And that's > fine. As I've stated multiple times in this thread, it would explode > the complexity of the RFC. Probably half of it would just be about > constructors. That's clear it should be a separate RFC, and if I did > present it, you (or someone else) would argue that it needs to be > broken up -- just like the records RFC. > > I show up, because I care as well; but damn, everyone can't always > have cake. > > — Rob
I loved the Records RFC -- see my comments on Twitter about it -- and I would not have asked you to break it up. But I would have asked why we need both inline and conventional constructors in the same time, instead of just the genius inline (now primary) constructors with a body. ;) My intention is not to demotivate you, fwiw. Keen to talk about the complexity.
-- Cheers Nick

Lynn

64 days ago
On Mon, Jun 29, 2026 at 2:20 AM Rob Landers <rob@getswytch.com> wrote:
> Hello internals, > > I'd like to put forward Primary Constructors > <https://wiki.php.net/rfc/primary-constructors> for comment. > > An implementation PR will be opened later today (UTC), and the RFC updated > with this discussion thread. > > — Rob >
I don't see any mention of anonymous classes, I assume they aren't compatible syntax wise?

Rob Landers

64 days ago
On Mon, Jun 29, 2026, at 12:27, Lynn wrote:
> > > On Mon, Jun 29, 2026 at 2:20 AM Rob Landers <rob@getswytch.com> wrote: >> __ >> Hello internals, >> >> I'd like to put forward Primary Constructors <https://wiki.php.net/rfc/primary-constructors> for comment. >> >> An implementation PR will be opened later today (UTC), and the RFC updated with this discussion thread. >> >> — Rob > > I don't see any mention of anonymous classes, I assume they aren't compatible syntax wise?
Hi Lynn, That's a good observation! Originally, I had a whole section and decided to strip it, then forgot to add it back as non-supported and future scope. FWIW, I was originally going to support them with something like: $arg = 'foo'; $class = new class(public int $x = $arg) {} Which desugars to: $arg = 'foo'; $class = new class($arg) { public function __construct(public int $x) {} } There are a lot of caveats, which is why I dropped it for future scope: 1. it would require types on the input parameters to disambiguate between arguments and properties 2. it gets weird if you don't specify a default value for the property and makes it ambiguous as a constructor 3. which begs the question of always having a default In essence, it deserves its own RFC because there is a lot of subtlety and only makes sense in the context that primary constructors exist in the first place. I'll address it in the RFC, thanks! — Rob

Lynn

64 days ago
On Mon, Jun 29, 2026 at 12:56 PM Rob Landers <rob@getswytch.com> wrote:
> > > On Mon, Jun 29, 2026, at 12:27, Lynn wrote: > > > > On Mon, Jun 29, 2026 at 2:20 AM Rob Landers <rob@getswytch.com> wrote: > > > Hello internals, > > I'd like to put forward Primary Constructors > <https://wiki.php.net/rfc/primary-constructors> for comment. > > An implementation PR will be opened later today (UTC), and the RFC updated > with this discussion thread. > > — Rob > > > I don't see any mention of anonymous classes, I assume they aren't > compatible syntax wise? > > > Hi Lynn, > > That's a good observation! Originally, I had a whole section and decided > to strip it, then forgot to add it back as non-supported and future scope. > FWIW, I was originally going to support them with something like: > > $arg = 'foo'; > $class = new class(public int $x = $arg) {} > > > Which desugars to: > > $arg = 'foo'; > $class = new class($arg) { > public function __construct(public int $x) {} > } > > > There are a lot of caveats, which is why I dropped it for future scope: > > 1. it would require types on the input parameters to disambiguate > between arguments and properties > 2. it gets weird if you don't specify a default value for the property > and makes it ambiguous as a constructor > 3. which begs the question of always having a default > > In essence, it deserves its own RFC because there is a lot of subtlety and > only makes sense in the context that primary constructors exist in the > first place. > > I'll address it in the RFC, thanks! > > — Rob >
I think the only thing that needs to be solved for anonymous classes would be the syntax. Seeing there's always a visibility/type in property promotion, I think it should be clear it's going to be style (A) or (B) based on the syntax as you can't mix. I can't see a scenario where you would write: $class = new class(public int $x) {} Where as the following would be just fine: $class = new class(public int $x = 0) {}; // construct with property $x being 0 $class = new class(public int|null $x = null) {}; // construct with property $x being null $class = new class(public int $x = $x) {}; // construct with $x from local scope into property $x $class = new class(public int $x = $this->x) {}; // construct with $this->x from the instance scope $class = new class(public int $x = self::$x) {}; // construct with $this->x from the static self scope Bonus points if the body becomes optional: $class = new class(public int $x = 0, public int $y = 0); This would be the existing style and remains unchanged $class = new class($localScope) {}; Mixing would error as you can't pick both: $class = new class(public int $x = 0, $y) {}; // Nothing handles $y as no body is allowed $class = new class($x, public int $y = 0) {}; // Can't allow the latter as $x requires a body to do something Perhaps this isn't as simple as I think it is, but this would be a nice + to have in this RFC. I think it's fine to keep the initial implementation rather restrictive, it can always be improved upon in the future.

Rowan Tommins [IMSoP]

64 days ago
On 29 June 2026 11:52:45 BST, Rob Landers <rob@getswytch.com> wrote:
> > FWIW, I was originally going to support them with something like: > >$arg = 'foo'; >$class = new class(public int $x = $arg) {} > >Which desugars to: > >$arg = 'foo'; >$class = new class($arg) { > public function __construct(public int $x) {} >}
I actually have a draft RFC kicking around for this, but using a different syntax inspired by closures: $arg = 'foo'; $class = new class() use (public int $arg) {} This allows a very concise version for very simple cases: $class = new class() use ($arg) {} I even had a working implementation, but paused it to work out if a constructor could be allowed as well. If the syntax allowed for a parent constructor call, though, that might be enough for most use cases. Although it's not obvious, anonymous classes are actually compiled once, and then constructed multiple times, which means the syntax is doing quite a lot more than constructor promotion. I agree that it should be left as future scope to pin down exactly how it should look and work. Rowan Tommins [IMSoP]

Larry Garfield

64 days ago
On Mon, Jun 29, 2026, at 5:52 AM, Rob Landers wrote:
> On Mon, Jun 29, 2026, at 12:27, Lynn wrote: >> >> >> On Mon, Jun 29, 2026 at 2:20 AM Rob Landers <rob@getswytch.com> wrote: >>> __ >>> Hello internals, >>> >>> I'd like to put forward Primary Constructors <https://wiki.php.net/rfc/primary-constructors> for comment. >>> >>> An implementation PR will be opened later today (UTC), and the RFC updated with this discussion thread. >>> >>> — Rob >> >> I don't see any mention of anonymous classes, I assume they aren't compatible syntax wise? > > Hi Lynn, > > That's a good observation! Originally, I had a whole section and > decided to strip it, then forgot to add it back as non-supported and > future scope. FWIW, I was originally going to support them with > something like: > > $arg = 'foo'; > $class = new class(public int $x = $arg) {} > > Which desugars to: > > $arg = 'foo'; > $class = new class($arg) { > public function __construct(public int $x) {} > } > > There are a lot of caveats, which is why I dropped it for future scope: > 1. it would require types on the input parameters to disambiguate > between arguments and properties > 2. it gets weird if you don't specify a default value for the property > and makes it ambiguous as a constructor > 3. which begs the question of always having a default > In essence, it deserves its own RFC because there is a lot of subtlety > and only makes sense in the context that primary constructors exist in > the first place. > > I'll address it in the RFC, thanks! > > — Rob
For anon classes, I would think the best approach is to have a compact, non-redundant syntax for "take this value from current scope and assign it to a property in this class." Basically combining promotion and closure. Doing that right now is horribly ugly. That may well be out of scope for this RFC, and that's fine, but I wanted to put that out there. --Larry Garfield

Rowan Tommins [IMSoP]

64 days ago
On 29 June 2026 17:40:29 BST, Larry Garfield <larry@garfieldtech.com> wrote:
>For anon classes, I would think the best approach is to have a compact, non-redundant syntax for "take this value from current scope and assign it to a property in this class." Basically combining promotion and closure. Doing that right now is horribly ugly.
Yeah, that was precisely my starting point. The "use" syntax I came up with felt pretty clear and flexible, but some off-list review suggested that fabricating a constructor behind the scenes was not ideal, because there are use cases for combining it with a real constructor. I was looking into alternative ways to inject the values into the instance, such as an extra opcode like the one closures use; but Real Life intervened and I haven't got back to it for a while. I would need to look back at notes and discussions to see if there were use cases mentioned other than parent constructor calls. Maybe the "parameter forwarding" syntax from this RFC could be combined with what I had somehow. Rowan Tommins [IMSoP]

Rob Landers

64 days ago
On Mon, Jun 29, 2026, at 02:17, Rob Landers wrote:
> Hello internals, > > I'd like to put forward Primary Constructors <https://wiki.php.net/rfc/primary-constructors> for comment. > > An implementation PR will be opened later today (UTC), and the RFC updated with this discussion thread. > > — Rob
I've updated the RFC with an implementation URL, the link to the mailing list thread, and information regarding anonymous classes. — Rob

Larry Garfield

64 days ago
On Sun, Jun 28, 2026, at 7:17 PM, Rob Landers wrote:
> Hello internals, > > I'd like to put forward Primary Constructors > <https://wiki.php.net/rfc/primary-constructors> for comment. > > An implementation PR will be opened later today (UTC), and the RFC > updated with this discussion thread. > > — Rob
I support this feature, and am glad to see it. It will make struct-objects that much easier to write, as well as more interesting value objects. To the objections about readonly... Seriously, readonly is broken. :-) Use private(set) instead and you get essentially the same effect, unless you're doing something screwy inside your class. And if you are, then you already know it. As Rob already noted, there's a massive number of existing classes where it's not even an issue: There's no hooks or need for them, and readonly works fine then. Or switching them over to private(set) if adding a hook in the future is not hard. (Unless there's inheritance involved, and then the problem is inheritance. :-) ) We can't remove readonly from the language, but we should not allow its limitations and flaws to keep us from making our lives easier. The RFC has no mention of attributes. An example of that would be helpful, even though I presume it's exactly what one would expect it to be. The one addition I would ask for is to note that in Kotlin and Swift, there is a post-primary-constructor initializer, called an `init` block. It has no body, but lets you do post-property-assignment stuff. It's basically an argument-free constructor. I think that would go a long way toward smoothing the concerns people have about this feature being too "locked down." If we don't want a new keyword for it, we could allow a __construct method that has NO arguments if there is a primary constructor. It would get called last, after everything else (including parent constructors if appropriate) has run. (Whether it's spelled `__construct() { ]]` or `__construct { }`, I don't have a strong opinion.) A __construct that does take arguments would still be a compile-time conflict with a primary constructor. --Larry Garfield

Eric Norris

64 days ago
On Mon, Jun 29, 2026 at 2:01 PM Larry Garfield <larry@garfieldtech.com> wrote:
> > On Sun, Jun 28, 2026, at 7:17 PM, Rob Landers wrote: > > Hello internals, > > > > I'd like to put forward Primary Constructors > > <https://wiki.php.net/rfc/primary-constructors> for comment. > > > > An implementation PR will be opened later today (UTC), and the RFC > > updated with this discussion thread. > > > > — Rob > > I support this feature, and am glad to see it. It will make struct-objects that much easier to write, as well as more interesting value objects. > > To the objections about readonly... Seriously, readonly is broken. :-) Use private(set) instead and you get essentially the same effect, unless you're doing something screwy inside your class. And if you are, then you already know it. > > As Rob already noted, there's a massive number of existing classes where it's not even an issue: There's no hooks or need for them, and readonly works fine then. Or switching them over to private(set) if adding a hook in the future is not hard. (Unless there's inheritance involved, and then the problem is inheritance. :-) ) > > We can't remove readonly from the language, but we should not allow its limitations and flaws to keep us from making our lives easier.
I'm afraid of going off-topic, but I disagree that "readonly is broken." In what way is it broken? A large part of the disagreement in the previous "readonly hooks" RFC was over the *meaning* of readonly. I'd have to fully go back to remind myself of the nuance, but my recollection is that it works exactly how I (and others, per the vote outcome) expect. I recall that it seemed like what people wanted was to have something only *publicly* readable, but internally writable, which is in fact what you're suggesting as an alternative - asymmetric visibility (private(set)) - and is not what readonly is for, even if the name lends itself to confusion. I think you had even described it as "write-once", at one point, which more closely matches my expectation and desire of the keyword. I recall that I (and someone else?) had thrown out a suggestion for "init" hooks, that I think would have addressed what some people were looking for (having a hook with a readonly property for initialization), but not others (get hooks for readonly, since that breaks the "main" interpretation of the readonly RFC). Again, this was a while ago so I'm a little fuzzy, but I would absolutely consider voting yes for an "init" hook RFC; I could arguably see that as compatible with the readonly keyword, and thus, readonly would be compatible with (a subset of) hooks. This is not meant to be a combative email by the way, I just want to make sure it's clear that I believe readonly is not something that we should remove or consider baggage.

Tim Düsterhus

63 days ago
Hi Am 2026-06-29 20:44, schrieb Eric Norris:
> I'm afraid of going off-topic, but I disagree that "readonly is > broken." In what way is it broken? A large part of the disagreement in > the previous "readonly hooks" RFC was over the *meaning* of readonly. > I'd have to fully go back to remind myself of the nuance, but my > recollection is that it works exactly how I (and others, per the vote > outcome) expect. > > […] > > This is not meant to be a combative email by the way, I just want to > make sure it's clear that I believe readonly is not something that we > should remove or consider baggage.
I'm in full agreement on the quoted sections. Best regards Tim Düsterhus

Tim Düsterhus

63 days ago
Hi Am 2026-06-29 18:59, schrieb Larry Garfield:
> The RFC has no mention of attributes. An example of that would be > helpful, even though I presume it's exactly what one would expect it to > be.
I second that the RFC needs to specify this, *because* I don't consider this obvious at all and I'm positive that the expectations from users differ wildly. What do your expectations look like? Best regards Tim Düsterhus

Nick

63 days ago
Hey Larry, On 30.06.26 00:59, Larry Garfield wrote:
> On Sun, Jun 28, 2026, at 7:17 PM, Rob Landers wrote: >> Hello internals, >> >> I'd like to put forward Primary Constructors >> <https://wiki.php.net/rfc/primary-constructors> for comment. >> >> An implementation PR will be opened later today (UTC), and the RFC >> updated with this discussion thread. >> >> — Rob > I support this feature, and am glad to see it. It will make struct-objects that much easier to write, as well as more interesting value objects. > > To the objections about readonly... Seriously, readonly is broken. :-) Use private(set) instead and you get essentially the same effect, unless you're doing something screwy inside your class. And if you are, then you already know it.
I know you will not get tired trying to sell us private(set) -- but it's not the same; one prevents a programmer error, the other not. We cannot one time argue we need guardrails and don't hand out foot guns, and when it doesn't suit the argument we ignore it.
> As Rob already noted, there's a massive number of existing classes where it's not even an issue: There's no hooks or need for them, and readonly works fine then. Or switching them over to private(set) if adding a hook in the future is not hard. (Unless there's inheritance involved, and then the problem is inheritance. :-) ) > > We can't remove readonly from the language, but we should not allow its limitations and flaws to keep us from making our lives easier.
Bud, you make it sound as if there would be no other solution. Simply allowing a body for primary constructors *is* the solution. And it doesn't require readonly to be removed from the language. ;)
> The RFC has no mention of attributes. An example of that would be helpful, even though I presume it's exactly what one would expect it to be. > > The one addition I would ask for is to note that in Kotlin and Swift, there is a post-primary-constructor initializer, called an `init` block. It has no body, but lets you do post-property-assignment stuff. It's basically an argument-free constructor. I think that would go a long way toward smoothing the concerns people have about this feature being too "locked down." > > If we don't want a new keyword for it, we could allow a __construct method that has NO arguments if there is a primary constructor. It would get called last, after everything else (including parent constructors if appropriate) has run. (Whether it's spelled `__construct() { ]]` or `__construct { }`, I don't have a strong opinion.) A __construct that does take arguments would still be a compile-time conflict with a primary constructor. > > --Larry Garfield
-- Cheers Nick

Rob Landers

63 days ago
On Mon, Jun 29, 2026, at 18:59, Larry Garfield wrote: [snip]
> The RFC has no mention of attributes. An example of that would be helpful, even though I presume it's exactly what one would expect it to be.
This was an oversight. I'll add the logic there. This is how they work: 1. Attributes on promoted primary-constructor params are reflected on both (just like promoted properties): 1. ReflectionParameter 2. ReflectionProperty 2. Attributes on bare params remain parameter-only. 3. Target validation is context-sensitive, matching existing constructor promotion behavior: 1. `#[ParamAttr] public int $x` appears on the property too, but newInstance() from ReflectionProperty errors. 2. `#[PropAttr] public int $x` appears on the parameter too, but newInstance() from ReflectionParameter errors. 4. Attributes before a class remain class attributes and are not copied to the synthesized constructor. The last one is debatable, but from what I can tell from grepping, putting attributes on constructors is a very rare thing (39 out of 56,546 constructors). However, class-level attributes are a more common thing. That being said, I'd be open to changing (4) to behave like properties/parameters where it gets attached to the class and the constructor.
> The one addition I would ask for is to note that in Kotlin and Swift, there is a post-primary-constructor initializer, called an `init` block. It has no body, but lets you do post-property-assignment stuff. It's basically an argument-free constructor. I think that would go a long way toward smoothing the concerns people have about this feature being too "locked down." > > If we don't want a new keyword for it, we could allow a __construct method that has NO arguments if there is a primary constructor. It would get called last, after everything else (including parent constructors if appropriate) has run. (Whether it's spelled `__construct() { ]]` or `__construct { }`, I don't have a strong opinion.) A __construct that does take arguments would still be a compile-time conflict with a primary constructor. > > --Larry Garfield >
I believe this should be a follow-up RFC. In PHP, the child decides when to call the parent constructor — so "runs last, after parent constructors" requires defining a new sequencing contract, which is a real design decision that deserves its own discussion rather than a rider on this one. Beyond that, it also requires defining a syntax (you've sketched one approach, Nick another -- both are interesting and that's exactly why it needs its own discussion), rules (whether property lists should exist), and semantics. In essence, the description would be a large (probably half) of the current RFC. — Rob

Tim Düsterhus

63 days ago
Hi Am 2026-06-29 22:22, schrieb Rob Landers:
> This was an oversight. I'll add the logic there. This is how they work: > 1. Attributes on promoted primary-constructor params are reflected on > both (just like promoted properties): > 1. ReflectionParameter > 2. ReflectionProperty > 2. Attributes on bare params remain parameter-only. > 3. Target validation is context-sensitive, matching existing > constructor promotion behavior: > 1. `#[ParamAttr] public int $x` appears on the property too, but > newInstance() from ReflectionProperty errors. > 2. `#[PropAttr] public int $x` appears on the parameter too, but > newInstance() from ReflectionParameter errors. > 4. Attributes before a class remain class attributes and are not > copied to the synthesized constructor. > The last one is debatable, but from what I can tell from grepping, > putting attributes on constructors is a very rare thing (39 out of > 56,546 constructors). However, class-level attributes are a more common > thing. That being said, I'd be open to changing (4) to behave like > properties/parameters where it gets attached to the class and the > constructor.
Thank you for confirming my assumption that expectations likely differ (https://news-web.php.net/php.internals/131610). I would expect #[Attr] to be applied to both `Foo` and `Foo::__construct()` for consistency with promoted properties, since “Primary constructors” are effectively “Promoted Constructors”: #[Attr] class Foo(public int $x) { } The error on the `newInstance()` will only be an issue for code that tries to unconditionally instantiate unknown attributes, which is bound to fail already since attributes do not need to be backed by a class - and arguably doing so is not super useful, since you don't know the semantics of unknown attributes. Code that knows what a given attribute means will not try to find it on mismatching targets and thus will not try to instantiate them either. Best regards Tim Düsterhus

Seifeddine Gmati

64 days ago
On Mon, 29 Jun 2026 at 01:18, Rob Landers <rob@getswytch.com> wrote:
> > Hello internals, > > I'd like to put forward Primary Constructors for comment. > > An implementation PR will be opened later today (UTC), and the RFC updated with this discussion thread. > > — Rob
Hi Rob, Personally, I really dislike this feature. It adds a new syntax that increases the complexity of the PHP grammar, which is already quite complex, for no real advantage. It's just a slightly different way to write something the language already expresses. It's also confusing to read. A line like: ``` class Foo(public int $a, public string $b) extends Base($a, $b) { ... } ``` jumps from class definition, to constructor definition, to class definition again (inheritance), to what is effectively a `parent::__construct()` call, and then back into the class body. That's a lot of distinct concepts packed into a single line, with no syntactic separation to signal the shift from one to the next. Cheers, Seifeddine.

Rob Landers

63 days ago
On Mon, Jun 29, 2026, at 20:54, Seifeddine Gmati wrote:
> On Mon, 29 Jun 2026 at 01:18, Rob Landers <rob@getswytch.com> wrote: > > > > Hello internals, > > > > I'd like to put forward Primary Constructors for comment. > > > > An implementation PR will be opened later today (UTC), and the RFC updated with this discussion thread. > > > > — Rob > > Hi Rob, > > Personally, I really dislike this feature.
Sincerely, thank you for your opinion.
> > It adds a new syntax that increases the complexity of the PHP grammar, > which is already quite complex, for no real advantage. It's just a > slightly different way to write something the language already > expresses. > > It's also confusing to read. A line like: > > ``` > class Foo(public int $a, public string $b) extends Base($a, $b) { ... } > ``` > > jumps from class definition, to constructor definition, to class > definition again (inheritance), to what is effectively a > `parent::__construct()` call, and then back into the class body. > That's a lot of distinct concepts packed into a single line, with no > syntactic separation to signal the shift from one to the next. > > Cheers, > Seifeddine.
It's also the entire point of the feature ;) You open the file, and in two seconds, you know: It defines two properties, type int and string, it calls parent::__construct with those two parameters. Currently, you open the class, seek to the constructor, read it to understand if it even calls the parent constructor or not. Just like promoted properties, the cost is nominal, but now it is nearly ubiquitous in usage and saves a large amount of time in reading and writing classes. This feature seeks to do the same. — Rob

Seifeddine Gmati

63 days ago
> It's also the entire point of the feature ;) You open the file, and in two seconds, you know: > > It defines two properties, type int and string, it calls parent::__construct with those two parameters. > > Currently, you open the class, seek to the constructor, read it to understand if it even calls the parent constructor or not. Just like promoted properties, the cost is nominal, but now it is nearly ubiquitous in usage and saves a large amount of time in reading and writing classes. This feature seeks to do the same.
> It's also the entire point of the feature ;) You open the file, and in > two seconds, you know: > > It defines two properties, type int and string, it calls > parent::__construct with those two parameters. > > Currently, you open the class, seek to the constructor, read it to > understand if it even calls the parent constructor or not. Just like > promoted properties, the cost is nominal, but now it is nearly > ubiquitous in usage and saves a large amount of time in reading and > writing classes. This feature seeks to do the same.
I don't think the "seek to the constructor" problem is real in practice. By convention the constructor goes first in a PHP class, so the code you'd be reading is simply: ``` class Foo extends Base { public function __construct(public int $a, public string $b) { parent::__construct($a, $b); } } ``` That's seven lines that fit comfortably on any screen, and each concept, the class, the constructor, the promoted properties, the parent call, sits on its own line where it's immediately recognizable. Packing all of it onto a single line saves nothing and makes the code harder to read, not easier. I also disagree with comparing this to promoted properties. Promoted properties removed genuine boilerplate: the property declaration, the constructor parameter, and the `$this->x = $x` assignment collapsed into one place because they were the same information repeated three times. Primary constructors don't remove repetition; they just relocate the constructor signature into the class declaration line. There's no duplicated information being eliminated. Cheers, Seifeddine.

Tim Düsterhus

63 days ago
Hi Am 2026-06-29 21:21, schrieb Seifeddine Gmati:
> I don't think the "seek to the constructor" problem is real in > practice. > By convention the constructor goes first in a PHP class, so the code > you'd be reading is simply: > > ``` > class Foo extends Base > { > public function __construct(public int $a, public string $b) > { > parent::__construct($a, $b); > } > } > ``` > > That's seven lines that fit comfortably on any screen, and each > concept, > the class, the constructor, the promoted properties, the parent call, > sits on its own line where it's immediately recognizable. Packing all > of > it onto a single line saves nothing and makes the code harder to read, > not easier. > > I also disagree with comparing this to promoted properties. Promoted > properties removed genuine boilerplate: the property declaration, the > constructor parameter, and the `$this->x = $x` assignment collapsed > into > one place because they were the same information repeated three times. > Primary constructors don't remove repetition; they just relocate the > constructor signature into the class declaration line. There's no > duplicated information being eliminated.
And even though I just argued in favor of the current proposal in a reply to Nick (https://news-web.php.net/php.internals/131614), I also agree with Seifeddine, that I don’t think that the value provided by this proposal is sufficiently strong to get a “Yes” from me - but I know that allowing arbitrary bodies would be a “No”. Best regards Tim Düsterhus

Tim Düsterhus

63 days ago
Hi Am 2026-06-29 02:17, schrieb Rob Landers:
> I'd like to put forward Primary Constructors > <https://wiki.php.net/rfc/primary-constructors> for comment.
The RFC currently does not specify the interaction with visibility. It is currently implied that the constructor will be `public`, but the RFC doesn't spell that out and doesn't specify whether or not it is possible to make such a constructor non-public (e.g. to actually treat it as a “primary constructor” in a sea of multiple named constructors). Best regards Tim Düsterhus

Tim Düsterhus

62 days ago
Hi Am 2026-06-29 21:07, schrieb Tim Düsterhus:
> Am 2026-06-29 02:17, schrieb Rob Landers: >> I'd like to put forward Primary Constructors >> <https://wiki.php.net/rfc/primary-constructors> for comment. > > The RFC currently does not specify the interaction with visibility. It > is currently implied that the constructor will be `public`, but the RFC > doesn't spell that out and doesn't specify whether or not it is > possible to make such a constructor non-public (e.g. to actually treat > it as a “primary constructor” in a sea of multiple named constructors).
The RFC also doesn't specify whether the parent constructor call supports named arguments: class P(public string $foo, public string $bar) { } class C(string $bar, string $foo) extends P(bar: $bar, foo: $foo) { } Best regards Tim Düsterhus

tag Knife

62 days ago
On Mon, 29 Jun 2026 at 01:18, Rob Landers <rob@getswytch.com> wrote:
> Hello internals, > > I'd like to put forward Primary Constructors > <https://wiki.php.net/rfc/primary-constructors> for comment. > > An implementation PR will be opened later today (UTC), and the RFC updated > with this discussion thread. > > — Rob >
I think this would be a great addition to PHP. There has been a few times in my career where I have needed to extend a 3rd party library and calling `parent::__construct($params);` at the end of my extending class always felt dirty. Being able to use ``` class MyClass(int arg1, string $arg2) extends Base(string $arg2) {} ``` Is much better, and clearer. It can avoid mistakes where a developer extends a class, doesnt call the parent constructor, it works but the base class might not be correctly initialized and might break down the line in production. Having an explicit "this get passed to the class we are extended" in the definition would wipe out that headache area.