Re: Primary Constructors
| From: | Larry Garfield | Date: | Mon, 22 Jun 2026 15:15:29 +0000 |
| Subject: | Re: Primary Constructors | ||
| References: | 1 | Groups: | php.internals |
| Request: | Send a blank email to internals+get-131456@lists.php.net to get a copy of this message | ||
On Wed, Jun 17, 2026, at 4:32 PM, Rob Landers wrote:
> Hello internals,
>
> I was reminded of my records RFC today, and one of the features of the
> RFC was "short constructors" generally called "primary constructors" in
> C#/Kotlin.
>
> They would look like this:
>
> class Point(public int $x, int $id = 0) extends Base($id);
>
> Which is just sugar for this:
>
> class Point extends Base {
> public function __construct(public int $x, int $id = 0) {
> parent::__construct($id);
> }
> }
>
> or this:
>
> class Point extends Base {
> public int $x;
> public function __construct(int $x, int $id = 0) {
> $this->x = $x;
> parent::__construct($id);
> }
> }
>
> A class with a primary constructor *may not* have a defined
>
__construct function. Any special initialization must be done
> with
> hooks:
>
> class Temperature(
> public float $celsius {
> set {
> if ($value < -273.15) {
> throw new ValueError('below absolute zero');
> }
> $this->celsius = $value;
> }
> }
> ) {}
>
> new Temperature(20.0); // ok
> new Temperature(-300.0); // ValueError: below absolute zero
>
> I'm sending this email to the list to gather additional feedback before
> pursuing a formal RFC proposal.
>
> — Rob
I would support this, and I agree that KISS is the best way to go for now.
I don't know if it makes sense to include in the initial design, but for reference here's
how Kotlin handles construction:
class Person(val name: String, val age: Int) { // Primary constructor, equivalent to promotion and
what's proposed here.
init {
// Argument-less code block/method that runs after the primary constructor is assigned.
// This could be useful to include for more complex cases, like where hooks are useful.
}
// Secondary constructor
constructor(name: String): this(name, 0) {
// This constructor gets called first, but is forced to call the primary constructor
syntactically.
// Then it can do other stuff if it wants.
}
}
cf: https://kotlinlang.org/docs/classes.html#constructors-and-initializer-blocks
I don't think we should include the "secondary constructor calls primary constructor and
you have to make sure it works" logic. However, I do think we can/should consider an init
block. Though how that interacts with inheritance I'm not sure yet.
Putting non-trivial property hooks inside a constructor promotion is already strongly discouraged,
and I would recommend continuing to discourage them even with this syntax. However, that does raise
a concern that we would really benefit from some way to allow property hooks to be defined "on
their own" on a property, AND link the property to a promoted constructor argument. I'm
not sure off hand how we would do that, and it's somewhat tangential to this syntax, but I am
pointing it out for completeness.
As for the readonly/hooks incompatibility, honestly, I don't care. :-) Readonly is frequently
problematic, Internals has already rejected making readonly and hooks compatible, and
protected(set) gets you almost the same rules with none of the conflicts. So I'm
good there.
--Larry Garfield