Hi internals,
I would like to submit the following RFC for your consideration:
https://wiki.php.net/rfc/constructor_promotion
This is based on one off the suggestions made in
https://externals.io/message/109220, and some existing discussion on the
topic can be found in that thread.
The primary motivation for this feature is to close the currently rather
wide gap between the use of ad-hoc array structures, and the use of
well-defined and type-safe value objects. The latter is what we want people
to use, but the former is, unfortunately, what is easy to use.
Regards,
Nikita
Hi internals,
I would like to submit the following RFC for your consideration:
https://wiki.php.net/rfc/constructor_promotion
Big fan. Thanks for this.
--
Paul M. Jones
pmjones@pmjones.io
http://paul-m-jones.com
Modernizing Legacy Applications in PHP
https://leanpub.com/mlaphp
Solving the N+1 Problem in PHP
https://leanpub.com/sn1php
Hi internals,
I would like to submit the following RFC for your consideration:
https://wiki.php.net/rfc/constructor_promotionBig fan. Thanks for this.
I like this too. One question (and apologies if I overlooked this in the
RFC), but is type-hinting required, or would the following be accepted:
class bar {
public function __construct(public $foo){}
}
--
Paul M. Jones
pmjones@pmjones.io
http://paul-m-jones.comModernizing Legacy Applications in PHP
https://leanpub.com/mlaphpSolving the N+1 Problem in PHP
https://leanpub.com/sn1php--
--
Chase Peeler
chasepeeler@gmail.com
Am 26.03.2020 um 14:30 schrieb Nikita Popov:
I would like to submit the following RFC for your consideration:
https://wiki.php.net/rfc/constructor_promotion
Looks good to me! Thanks.
Hello!
With all respect, seems a bit confuses to me, without in fact offer a new
feature, just a shortcut. I hope it's me seeing it the wrong way.
What happen if I rename the constructor parameters on a child class
constructor?
Example (https://3v4l.org/VqQde):
class A {
public function __construct(public int $x) { ... }
}
class B extends A {
public function __construct(public int $y) { ...;
parent::__construct($y); }
}
Atenciosamente,
David Rodrigues
Em qui., 26 de mar. de 2020 às 12:45, Sebastian Bergmann sebastian@php.net
escreveu:
Am 26.03.2020 um 14:30 schrieb Nikita Popov:
I would like to submit the following RFC for your consideration:
https://wiki.php.net/rfc/constructor_promotionLooks good to me! Thanks.
On Thu, Mar 26, 2020 at 5:02 PM David Rodrigues david.proweb@gmail.com
wrote:
Hello!
With all respect, seems a bit confuses to me, without in fact offer a new
feature, just a shortcut. I hope it's me seeing it the wrong way.What happen if I rename the constructor parameters on a child class
constructor?Example (https://3v4l.org/VqQde):
class A {
public function __construct(public int $x) { ... }
}class B extends A {
public function __construct(public int $y) { ...;
parent::__construct($y); }
}
Constructors (in general, independent of this feature) are per-class and do
not participate in classical signature-based inheritance (the exception
being abstract constructors, but those aren't relevant here). The notion of
"renaming" a constructor parameter doesn't make sense in that regard,
because the parameters of the child constructor have absolutely no relation
to the parameters of the parent constructor.
The way to write your example would be:
class A {
public function __construct(public int $x) {}
}
class B extends A {
public function __construct(int $x) {
parent::__construct($x);
// Your extra logic here.
}
}
Regards,
Nikita
I would like to submit the following RFC for your consideration:
https://wiki.php.net/rfc/constructor_promotionThis is based on one off the suggestions made in
https://externals.io/message/109220, and some existing discussion on the
topic can be found in that thread.Similar to an RFC you submitted six years ago:
https://wiki.php.net/rfc/automatic_property_initialization
I think the availability of typed properties makes the syntax for this much
more sensible and I look forward to +1ing it.
-Sara
czw., 26 mar 2020 o 14:31 Nikita Popov nikita.ppv@gmail.com napisał(a):
Hi internals,
I would like to submit the following RFC for your consideration:
https://wiki.php.net/rfc/constructor_promotionThis is based on one off the suggestions made in
https://externals.io/message/109220, and some existing discussion on the
topic can be found in that thread.
As I already said in linked topic. IMHO this is wrong and can be easily
overused.
A list of properties is changed into a list of constructor method arguments
which
can be easily overused on long argument lists and when the time comes to
have attributes then the list of promoted arguments with their annotations
and some extra blank lines then to improve readability could lead to
long constructor signature list on a different level of indentation which
also then always has to be un-collapsed in IDE and on the top of a class to
keep the track of properties where usually list of properties exists with
additional named constructors (always at the top).
Not even mentioning that mix of promoted and un-promoted argument list
would just feel awkward and that kind of mixed constructor where some of
arguments are assigned magically and some of them not can make it harder to
read.
And I'm not saying this cause I'm preparing another proposal for filling
the gap
between the use of ad-hoc array structures, and the use of well-defined and
type-safe value types.
I'm saying this because PHP was always verbose and easy to read/decipher
this solution IMHO has more drawbacks regarding readability than benefits.
Cheers,
Michał Brzuchalski
Hi internals,
I would like to submit the following RFC for your consideration:
https://wiki.php.net/rfc/constructor_promotionThis is based on one off the suggestions made in
https://externals.io/message/109220, and some existing discussion on the
topic can be found in that thread.The primary motivation for this feature is to close the currently rather
wide gap between the use of ad-hoc array structures, and the use of
well-defined and type-safe value objects. The latter is what we want people
to use, but the former is, unfortunately, what is easy to use.
Will this cause any problems with ReflectionClass? (I’m assuming not, but wanted to ask anyway.)
Cheers,
Ben
Hi!
I would like to submit the following RFC for your consideration:
https://wiki.php.net/rfc/constructor_promotion
I like this shortcut. I am not sure though I understand why callable is
not allowed? Can't it just create a non-typed property?
--
Stas Malyshev
smalyshev@gmail.com
Hi,
I would like to submit the following RFC for your consideration:
https://wiki.php.net/rfc/constructor_promotionThis is based on one off the suggestions made in
https://externals.io/message/109220, and some existing discussion on the
topic can be found in that thread.The primary motivation for this feature is to close the currently rather
wide gap between the use of ad-hoc array structures, and the use of
well-defined and type-safe value objects. The latter is what we want people
to use, but the former is, unfortunately, what is easy to use.
The amount of boilerplate this would cut down for e.g. projects using DI
and DTOs is unfathomable.
How does this work together with reflection? Will reflection see the
properties and the extended constructor definitions?
thanks,
- Markus
Hi,
I would like to submit the following RFC for your consideration:
https://wiki.php.net/rfc/constructor_promotionThis is based on one off the suggestions made in
https://externals.io/message/109220, and some existing discussion on the
topic can be found in that thread.The primary motivation for this feature is to close the currently rather
wide gap between the use of ad-hoc array structures, and the use of
well-defined and type-safe value objects. The latter is what we want
people
to use, but the former is, unfortunately, what is easy to use.The amount of boilerplate this would cut down for e.g. projects using DI
and DTOs is unfathomable.How does this work together with reflection? Will reflection see the
properties and the extended constructor definitions?
Reflection will see the state after desugaring. That is, it just sees
normal properties and normal constructor args. I've made this more explicit
in the RFC now: https://wiki.php.net/rfc/constructor_promotion#reflection
Nikita
Him
Reflection will see the state after desugaring. That is, it just sees
normal properties and normal constructor args. I've made this more explicit
in the RFC now: https://wiki.php.net/rfc/constructor_promotion#reflection
Thanks!
Another one, also related a bit to your PHP-Parser (?): will the
tokenization process "see" the code before desugaring?
I'm wondering how automated translation tool can detect that a certain
code is not "sugared" and promote this?
thanks!
- Markus
Him
Reflection will see the state after desugaring. That is, it just sees
normal properties and normal constructor args. I've made this more
explicit
in the RFC now:
https://wiki.php.net/rfc/constructor_promotion#reflectionThanks!
Another one, also related a bit to your PHP-Parser (?): will the
tokenization process "see" the code before desugaring?I'm wondering how automated translation tool can detect that a certain
code is not "sugared" and promote this?
Tokenization and parsing both happen before the desugaring, they see the
code in its original form. They would detect promoted properties by
checking whether a constructor parameter has a visibility modifier. Of
course, tooling might want to perform the desugaring transformation
internally, so it sees a uniform representation of everything.
Regards,
Nikita
Question: Will parse errors in the __construct() be reported as all being on the same line, or will the parser report each as a distinct line of code when reporting parse errors?
-Mike
Him
Reflection will see the state after desugaring. That is, it just sees
normal properties and normal constructor args. I've made this more
explicit
in the RFC now:
https://wiki.php.net/rfc/constructor_promotion#reflectionThanks!
Another one, also related a bit to your PHP-Parser (?): will the
tokenization process "see" the code before desugaring?I'm wondering how automated translation tool can detect that a certain
code is not "sugared" and promote this?Tokenization and parsing both happen before the desugaring, they see the
code in its original form. They would detect promoted properties by
checking whether a constructor parameter has a visibility modifier. Of
course, tooling might want to perform the desugaring transformation
internally, so it sees a uniform representation of everything.Regards,
Nikita
I would like to submit the following RFC for your consideration:
https://wiki.php.net/rfc/constructor_promotion
I love it :)
Just one question:
Shouldn't default values be copied on the signature? That would be expected
for me.
(and that would make the properties still initialized when child classes
don't call the parent constructor for whatever reasons.)
class Point {
public function __construct(
public float $x = 0.0,
<==>
class Point {
public float $x = 0.0;
public function __construct(
float $x = 0.0,
Nicolas
On Wed, Apr 8, 2020 at 1:56 PM Nicolas Grekas nicolas.grekas+php@gmail.com
wrote:
I would like to submit the following RFC for your consideration:
I love it :)
Just one question:
Shouldn't default values be copied on the signature? That would be
expected for me.
(and that would make the properties still initialized when child classes
don't call the parent constructor for whatever reasons.)class Point {
public function __construct(
public float $x = 0.0,<==>
class Point {
public float $x = 0.0;public function __construct( float $x = 0.0,
Nicolas
Some reasons why the default value is not duplicated into the property are
discussed in https://wiki.php.net/rfc/constructor_promotion#desugaring.
It's primarily about forward-compatibility concerns.
But apart from that, I strongly believe that you should not, ever, specify
a default value on a property that is initialized in the constructor. You
can have a default, or you can have explicit initialization in the
constructor -- both together do not make sense.
Regards,
Nikita
Hi internals,
I would like to submit the following RFC for your consideration:
https://wiki.php.net/rfc/constructor_promotionThis is based on one off the suggestions made in
https://externals.io/message/109220, and some existing discussion on the
topic can be found in that thread.The primary motivation for this feature is to close the currently rather
wide gap between the use of ad-hoc array structures, and the use of
well-defined and type-safe value objects. The latter is what we want people
to use, but the former is, unfortunately, what is easy to use.
As the reception has been positive, I plan to open voting on this proposal
soon. Please tell me if you have further concerns or the RFC needs
additional clarification.
Regards,
Nikita
Hi internals,
I would like to submit the following RFC for your consideration:
https://wiki.php.net/rfc/constructor_promotionThis is based on one off the suggestions made in
https://externals.io/message/109220, and some existing discussion on the
topic can be found in that thread.The primary motivation for this feature is to close the currently rather
wide gap between the use of ad-hoc array structures, and the use of
well-defined and type-safe value objects. The latter is what we want people
to use, but the former is, unfortunately, what is easy to use.
As it looks like the Attributes RFC is going to be accepted, we need to
consider how it will interact with this proposal. I've added an extra
section for this: https://wiki.php.net/rfc/constructor_promotion#attributes
The problem is that attributes are allowed both on properties and on
parameters. For promoted properties, what does the attribute apply to? My
original suggestion was to make it apply to both the parameter and the
generated property. However, Benjamin pointed out that an attribute on a
promoted property is almost certainly intended to apply to the property
only, especially because parameter attributes have not been supported
historically (in phpdoc). Applying it to both the property and the
parameter may cause issues for attributes that want to strictly validate
where they are used.
I think the best answer to this question may be to forbid the use of
attributes on promoted properties entirely, because there is no unambiguous
interpretation for them. I also think that using attributes pushes this
"syntax sugar" to its limit, as you can easily end up with a 50 line
constructor signature that way, at least if I'm going by some of the more
involved uses of annotations...
Does anyone have thoughts on this?
Regards,
Nikita
Hi internals,
I would like to submit the following RFC for your consideration:
https://wiki.php.net/rfc/constructor_promotionThis is based on one off the suggestions made in
https://externals.io/message/109220, and some existing discussion on the
topic can be found in that thread.The primary motivation for this feature is to close the currently rather
wide gap between the use of ad-hoc array structures, and the use of
well-defined and type-safe value objects. The latter is what we want
people
to use, but the former is, unfortunately, what is easy to use.As it looks like the Attributes RFC is going to be accepted, we need to
consider how it will interact with this proposal. I've added an extra
section for this:
https://wiki.php.net/rfc/constructor_promotion#attributesThe problem is that attributes are allowed both on properties and on
parameters. For promoted properties, what does the attribute apply to? My
original suggestion was to make it apply to both the parameter and the
generated property. However, Benjamin pointed out that an attribute on a
promoted property is almost certainly intended to apply to the property
only, especially because parameter attributes have not been supported
historically (in phpdoc). Applying it to both the property and the
parameter may cause issues for attributes that want to strictly validate
where they are used.I think the best answer to this question may be to forbid the use of
attributes on promoted properties entirely, because there is no unambiguous
interpretation for them. I also think that using attributes pushes this
"syntax sugar" to its limit, as you can easily end up with a 50 line
constructor signature that way, at least if I'm going by some of the more
involved uses of annotations...
This last proposal would kinda defeat constructor promotion and all the
reasons why it was introduced in the first place.
I would expect attributes to apply only to the properties on my side.
If people want different or duplicate attributes on parameters, then it's
normal to not be able to use constructor promotion.
My 2cts,
Nicolas
Hi Nicolas
I would expect attributes to apply only to the properties on my side.
Parameter annotations could be interesting for dependency injection.
Symfony currently has some DI magic through parameter names among
other things. It might be nice to control these through annotations
instead. This would be impossible or involve a lot of hackery and
assumptions if the annotations aren't available on the parameters.
Alternatively we might make this information available through
reflection (like ReflectionParameter::getGeneratedProperty()) so you
can look for the annotation there.
I don't think this is a huge problem, just wanted to mention it.
Ilija
I would expect attributes to apply only to the properties on my side.
Parameter annotations could be interesting for dependency injection.
Symfony currently has some DI magic through parameter names among
other things. It might be nice to control these through annotations
instead. This would be impossible or involve a lot of hackery and
assumptions if the annotations aren't available on the parameters.
Alternatively we might make this information available through
reflection (like ReflectionParameter::getGeneratedProperty()) so you
can look for the annotation there.
Sure, this could be considered.
But this doesn't contradict what I wrote:
if one uses constructor promotion, the then attributes should only go to
the properties to me.
If one wants to add attributes to parameters, then one would need to
opt-out from constructor promotion.
Nicolas
I would expect attributes to apply only to the properties on my side.
Parameter annotations could be interesting for dependency injection.
Symfony currently has some DI magic through parameter names among
other things. It might be nice to control these through annotations
instead. This would be impossible or involve a lot of hackery and
assumptions if the annotations aren't available on the parameters.
Alternatively we might make this information available through
reflection (like ReflectionParameter::getGeneratedProperty()) so you
can look for the annotation there.Sure, this could be considered.
But this doesn't contradict what I wrote:
if one uses constructor promotion, the then attributes should only go to
the properties to me.
If one wants to add attributes to parameters, then one would need to
opt-out from constructor promotion.Nicolas
I'm inclined to agree with Nicolas. My gut feeling (and I don't have more data to back it up than that, I admit) is that property annotations will be more prevalent than parameter annotations, so those should take precedence.
That said... how many will conflict? In practice, the most common use case I can see for parameter attributes would be fancy DI configuration for services... and I don't know that I'd care about attributes on the properties in that case. Conversely, for properties that have attributes that will usually be for extended type information, ORM mapping, and stuff like that. For which... I can't think of what I'd want attributes I'd want on the parameter itself.
Perhaps I'm just not creative enough at this hour, but I'm not sure that "both" is such a problem. It only seems like it would be an issue for attributes that user-space wanted to enforce on certain cases only; since attributes are new, a viable answer there is "btw, if you're restricting your annotation be aware of this double-case and either allow it or don't, but it's on you to decide what if anything to do."
--Larry Garfield
On Wed, Apr 29, 2020 at 2:05 AM Larry Garfield larry@garfieldtech.com
wrote:
I would expect attributes to apply only to the properties on my side.
Parameter annotations could be interesting for dependency injection.
Symfony currently has some DI magic through parameter names among
other things. It might be nice to control these through annotations
instead. This would be impossible or involve a lot of hackery and
assumptions if the annotations aren't available on the parameters.
Alternatively we might make this information available through
reflection (like ReflectionParameter::getGeneratedProperty()) so you
can look for the annotation there.Sure, this could be considered.
But this doesn't contradict what I wrote:
if one uses constructor promotion, the then attributes should only go to
the properties to me.
If one wants to add attributes to parameters, then one would need to
opt-out from constructor promotion.Nicolas
I'm inclined to agree with Nicolas. My gut feeling (and I don't have more
data to back it up than that, I admit) is that property annotations will be
more prevalent than parameter annotations, so those should take precedence.That said... how many will conflict? In practice, the most common use
case I can see for parameter attributes would be fancy DI configuration for
services... and I don't know that I'd care about attributes on the
properties in that case. Conversely, for properties that have attributes
that will usually be for extended type information, ORM mapping, and stuff
like that. For which... I can't think of what I'd want attributes I'd want
on the parameter itself.Perhaps I'm just not creative enough at this hour, but I'm not sure that
"both" is such a problem. It only seems like it would be an issue for
attributes that user-space wanted to enforce on certain cases only; since
attributes are new, a viable answer there is "btw, if you're restricting
your annotation be aware of this double-case and either allow it or don't,
but it's on you to decide what if anything to do."
That's basically my line of thinking as well. I do agree that as things are
right now, most likely a property annotation is intended. But that's
potentially only an artifact of phpdoc annotations not supporting parameter
annotations, so there is no existing ecosystem around them.
I think it might be best to apply to "both" and provide an isPromoted()
method on both ReflectionParameter and ReflectionProperty. Any code that
wishes to validate the allowed positions of an attribute can then skip
properties/parameters that report isPromoted() as true, to avoid reporting
false positives.
Nikita
I think it might be best to apply to "both" and provide an isPromoted()
method on both ReflectionParameter and ReflectionProperty. Any code that
wishes to validate the allowed positions of an attribute can then skip
properties/parameters that report isPromoted() as true, to avoid reporting
false positives.
That sounds good. Deal on my side.
Nicolas
On Wed, Apr 29, 2020 at 9:47 AM Nicolas Grekas nicolas.grekas+php@gmail.com
wrote:
I think it might be best to apply to "both" and provide an isPromoted()
method on both ReflectionParameter and ReflectionProperty. Any code that
wishes to validate the allowed positions of an attribute can then skip
properties/parameters that report isPromoted() as true, to avoid
reporting
false positives.That sounds good. Deal on my side.
Just to mention, any approach here potentially conflicts with anything we
consider for potential target validation on attributes, i.e. declaring for
an attribute that it is only allowed on a property OR an argument.
At the point constructor promotion happens, we can also not look into the
attribute to see if its target=property or target=parameter, because this
would require triggering autoloader.
Nicolas
On Wed, Apr 29, 2020 at 10:56 AM Benjamin Eberlei kontakt@beberlei.de
wrote:
On Wed, Apr 29, 2020 at 9:47 AM Nicolas Grekas <
nicolas.grekas+php@gmail.com> wrote:I think it might be best to apply to "both" and provide an isPromoted()
method on both ReflectionParameter and ReflectionProperty. Any code that
wishes to validate the allowed positions of an attribute can then skip
properties/parameters that report isPromoted() as true, to avoid
reporting
false positives.That sounds good. Deal on my side.
Just to mention, any approach here potentially conflicts with anything we
consider for potential target validation on attributes, i.e. declaring for
an attribute that it is only allowed on a property OR an argument.At the point constructor promotion happens, we can also not look into the
attribute to see if its target=property or target=parameter, because this
would require triggering autoloader.
Does it really conflict though? Can't the target validation just ignore
invalid attributes when promotion is involved (or rather, only check that
the attribute is valid for either property or parameter, but not
necessarily both of them)?
Nikita
On Wed, Apr 29, 2020 at 10:56 AM Benjamin Eberlei kontakt@beberlei.de
wrote:On Wed, Apr 29, 2020 at 9:47 AM Nicolas Grekas <
nicolas.grekas+php@gmail.com> wrote:I think it might be best to apply to "both" and provide an isPromoted()
method on both ReflectionParameter and ReflectionProperty. Any code
that
wishes to validate the allowed positions of an attribute can then skip
properties/parameters that report isPromoted() as true, to avoid
reporting
false positives.That sounds good. Deal on my side.
Just to mention, any approach here potentially conflicts with anything we
consider for potential target validation on attributes, i.e. declaring for
an attribute that it is only allowed on a property OR an argument.At the point constructor promotion happens, we can also not look into the
attribute to see if its target=property or target=parameter, because this
would require triggering autoloader.Does it really conflict though? Can't the target validation just ignore
invalid attributes when promotion is involved (or rather, only check that
the attribute is valid for either property or parameter, but not
necessarily both of them)?
Since target validation would happen on ReflectionAttribute::newInstance
alongside other validation already proposed, this means there is already
the ReflectionAttribute instantiated, so we could only avoid throwing an
exception, but the code might still get an attribute returned that doesn't
belong there. The technical problem here is that we defer the validation to
newInstance(), and not already during getAttributes(), to avoid
autoloading. It looks like we can either have target validation and have to
move validation to Reflection*::getAttributes(), or we can't have the
validation.
The same problem would essentially appear with a "repeatable=true/false"
feature that prevents the same attribute from being declared multiple
times. Its validation should also better be done at
Reflection*::getAttributes().
Maybe to allow for access to attributes without validation we should
instead have
Reflection*::getAttributes(ReflectionAttribute::FLAGS_NO_VALIDATION)
and
don't defer validation to newInstance() by default?
Nikita
On Wed, Apr 29, 2020 at 11:27 AM Benjamin Eberlei kontakt@beberlei.de
wrote:
On Wed, Apr 29, 2020 at 11:07 AM Nikita Popov nikita.ppv@gmail.com
wrote:On Wed, Apr 29, 2020 at 10:56 AM Benjamin Eberlei kontakt@beberlei.de
wrote:On Wed, Apr 29, 2020 at 9:47 AM Nicolas Grekas <
nicolas.grekas+php@gmail.com> wrote:I think it might be best to apply to "both" and provide an
isPromoted()
method on both ReflectionParameter and ReflectionProperty. Any code
that
wishes to validate the allowed positions of an attribute can then skip
properties/parameters that report isPromoted() as true, to avoid
reporting
false positives.That sounds good. Deal on my side.
Just to mention, any approach here potentially conflicts with anything
we consider for potential target validation on attributes, i.e. declaring
for an attribute that it is only allowed on a property OR an argument.At the point constructor promotion happens, we can also not look into
the attribute to see if its target=property or target=parameter, because
this would require triggering autoloader.Does it really conflict though? Can't the target validation just ignore
invalid attributes when promotion is involved (or rather, only check that
the attribute is valid for either property or parameter, but not
necessarily both of them)?Since target validation would happen on ReflectionAttribute::newInstance
alongside other validation already proposed, this means there is already
the ReflectionAttribute instantiated, so we could only avoid throwing an
exception, but the code might still get an attribute returned that doesn't
belong there. The technical problem here is that we defer the validation to
newInstance(), and not already during getAttributes(), to avoid
autoloading. It looks like we can either have target validation and have to
move validation to Reflection*::getAttributes(), or we can't have the
validation.The same problem would essentially appear with a "repeatable=true/false"
feature that prevents the same attribute from being declared multiple
times. Its validation should also better be done at
Reflection*::getAttributes().Maybe to allow for access to attributes without validation we should
instead have
Reflection*::getAttributes(ReflectionAttribute::FLAGS_NO_VALIDATION)
and
don't defer validation to newInstance() by default?
Performing validation when the getAttributes() call is performed does sound
reasonable to me. We can also add a class flag to perform this validation
only once (if it is successful), so the cost doesn't have to be paid by
every single getAttributes() consumer.
For the purpose of this RFC, I've now updated it to say that attributes
will be applied to both properties and parameters (
https://wiki.php.net/rfc/constructor_promotion#attributes), but with an
explicit note that we should change the behavior prior to the PHP 8 release
if it turns out to be problematic, e.g. with regards to an attribute
validation feature. I think this is something of a detail, and we'll be
mostly fine whichever way we chose, but it's hard to say right now which
option is best, in particular with regard to future attributes improvements.
Regards,
Nikita
Performing validation when the getAttributes() call is performed does sound
reasonable to me. We can also add a class flag to perform this validation
only once (if it is successful), so the cost doesn't have to be paid by
every single getAttributes() consumer.For the purpose of this RFC, I've now updated it to say that attributes
will be applied to both properties and parameters (
https://wiki.php.net/rfc/constructor_promotion#attributes), but with an
explicit note that we should change the behavior prior to the PHP 8 release
if it turns out to be problematic, e.g. with regards to an attribute
validation feature. I think this is something of a detail, and we'll be
mostly fine whichever way we chose, but it's hard to say right now which
option is best, in particular with regard to future attributes improvements.Regards,
Nikita
Thinking on it further, there's something sitting in the back of my head that's bothering me. This helped me flesh it out.
I like promotion, and think it will be great for simplifying code. However, as attributes show as we load more bells and whistles onto properties (even if they are desirable and useful bells and whistles) the level of conflict is going to get larger.
Consider, today we have:
class Point {
protected int $x;
protected int $y;
public function __construct(int $x, int $y) {
$this->x = $x;
$this->y = $y;
}
}
And this is needlessly verbose. With promotion, that simplifies to:
class Point {
public function __construct(protected int $x, protected int $y) {}
}
And this is good. There seems little dispute about that.
But then we add attributes, and it starts to get a little odd:
class Point {
public function __construct(
<<Positive>>
protected int $x,
<<Positive>>
protected int $y,
) {}
}
And has the possible confusion between parameter attributes and property attributes.
But... there's plenty of other things that may get added to properties. We've discussed the "readonly" property and the more robust assymetric visibility. What would that look like?
class Point {
public function __construct(
<<Positive>>
int $x {
public get,
private set,
},
<<Positive>>
int $y {
public get,
private set,
},
) {}
}
And that's now an awful lot of metadata to shove into, technically, a function signature. And if accessor methods ever happen:
class Point {
public function __construct(
<<Positive>>
int $x {
public get () {
return $this->x;
},
private set($new) {
if ($new < 0) {
throw new Exception();
}
$this->x = $new;
},
},
<<Positive>>
int $y {
public get () {
return $this->y;
},
private set($new) {
if ($new < 0) {
throw new Exception();
}
$this->y = $new;
},
},
) {}
}
And now we have methods defined inside a function signature, and that's just bizzarro. It also means at least 4 indent levels before you even get to the body of the methods.
And there's probably other things that could get attached to properties at some point I'm not thinking of that someone will suggest.
So I have to wonder, are we painting ourselves into a hole? Most of the uses I can think of for extra-metadata-properties are... overlapping with the case where you want to have constructor promotion. That means "well if you need to do fancy stuff just don't use promotion" becomes a non-sustainable answer, because the cases where you want both will increase rather than be an edge case.
Is there a way we can build in an escape hatch for ourselves to avoid these issues, both attributes and for future extension?
First idea off my head, which may or may not be good:
Allow an alternate keyword to fill in the body of the constructor, but NOT fill in the property. You still define the property yourself and put all the extra metadata there. To wit:
class Point {
<<Positive>>
int $x {
public get () {
return $this->x;
},
private set($new) {
if ($new < 0) {
throw new Exception();
}
$this->x = $new;
},
},
<<Positive>>
int $y {
public get () {
return $this->y;
},
private set($new) {
if ($new < 0) {
throw new Exception();
}
$this->y = $new;
},
},
public function __construct(promote int $x, promote int $y) {}
}
That eliminates 2 of the 4 places you'd have to repeat the name instead of 3, but means the constructor isn't burdened with having to hold a potentially non-trivial amount of metadata. It also resolves the attribute question as splitting it up like this would let you do "one or the other" (property vs parameter).
That may not be the best way, but I'm slightly uneasy with a path that could lead to 30 lines of property metadata squeezed into a constructor signature definition. :-)
Thoughts?
--Larry Garfield
On Tue, May 5, 2020 at 10:27 PM Larry Garfield larry@garfieldtech.com
wrote:
Performing validation when the getAttributes() call is performed does
sound
reasonable to me. We can also add a class flag to perform this validation
only once (if it is successful), so the cost doesn't have to be paid by
every single getAttributes() consumer.For the purpose of this RFC, I've now updated it to say that attributes
will be applied to both properties and parameters (
https://wiki.php.net/rfc/constructor_promotion#attributes), but with an
explicit note that we should change the behavior prior to the PHP 8
release
if it turns out to be problematic, e.g. with regards to an attribute
validation feature. I think this is something of a detail, and we'll be
mostly fine whichever way we chose, but it's hard to say right now which
option is best, in particular with regard to future attributes
improvements.Regards,
NikitaThinking on it further, there's something sitting in the back of my head
that's bothering me. This helped me flesh it out.I like promotion, and think it will be great for simplifying code.
However, as attributes show as we load more bells and whistles onto
properties (even if they are desirable and useful bells and whistles) the
level of conflict is going to get larger.Consider, today we have:
class Point {
protected int $x;
protected int $y;public function __construct(int $x, int $y) {
$this->x = $x;
$this->y = $y;
}
}And this is needlessly verbose. With promotion, that simplifies to:
class Point {
public function __construct(protected int $x, protected int $y) {}
}And this is good. There seems little dispute about that.
But then we add attributes, and it starts to get a little odd:
class Point {
public function __construct(
<<Positive>>
protected int $x,<<Positive>> protected int $y,
) {}
}And has the possible confusion between parameter attributes and property
attributes.But... there's plenty of other things that may get added to properties.
We've discussed the "readonly" property and the more robust assymetric
visibility. What would that look like?class Point {
public function __construct(
<<Positive>>
int $x {
public get,
private set,
},<<Positive>> int $y { public get, private set, },
) {}
}And that's now an awful lot of metadata to shove into, technically, a
function signature. And if accessor methods ever happen:class Point {
public function __construct(
<<Positive>>
int $x {
public get () {
return $this->x;
},
private set($new) {
if ($new < 0) {
throw new Exception();
}
$this->x = $new;
},
},<<Positive>> int $y { public get () { return $this->y; }, private set($new) { if ($new < 0) { throw new Exception(); } $this->y = $new; }, },
) {}
}And now we have methods defined inside a function signature, and that's
just bizzarro. It also means at least 4 indent levels before you even get
to the body of the methods.And there's probably other things that could get attached to properties at
some point I'm not thinking of that someone will suggest.So I have to wonder, are we painting ourselves into a hole? Most of the
uses I can think of for extra-metadata-properties are... overlapping with
the case where you want to have constructor promotion. That means "well if
you need to do fancy stuff just don't use promotion" becomes a
non-sustainable answer, because the cases where you want both will increase
rather than be an edge case.Is there a way we can build in an escape hatch for ourselves to avoid
these issues, both attributes and for future extension?First idea off my head, which may or may not be good:
Allow an alternate keyword to fill in the body of the constructor, but NOT
fill in the property. You still define the property yourself and put all
the extra metadata there. To wit:class Point {
<<Positive>>
int $x {
public get () {
return $this->x;
},
private set($new) {
if ($new < 0) {
throw new Exception();
}
$this->x = $new;
},
},<<Positive>>
int $y {
public get () {
return $this->y;
},
private set($new) {
if ($new < 0) {
throw new Exception();
}
$this->y = $new;
},
},public function __construct(promote int $x, promote int $y) {}
}That eliminates 2 of the 4 places you'd have to repeat the name instead of
3, but means the constructor isn't burdened with having to hold a
potentially non-trivial amount of metadata. It also resolves the attribute
question as splitting it up like this would let you do "one or the other"
(property vs parameter).That may not be the best way, but I'm slightly uneasy with a path that
could lead to 30 lines of property metadata squeezed into a constructor
signature definition. :-)
Hey Larry,
At least for me, there's a pretty clear line on what is acceptable to use
with constructor promotion and what isn't: That line has been crossed when
your property declarations started to include their own {} blocks.
Constructor promotion is a short-hand notation for the cases where the
constructor boilerplate is much larger than the actual functionality. If
you have 100 lines of property declarations due to heavy accessor use, then
I think the value proposition of constructor promotion converges to zero,
because the constructor boilerplate is no longer a dominating factor.
Regarding your last example, something similar to this has already been
proposed (and declined) in a slightly different form in
https://wiki.php.net/rfc/automatic_property_initialization:
class Point {
public int $x { ... };
public int $y { ... };
public function __construct(int $this->x, int $this->y) {}
}
Regards,
Nikita
On Tue, May 5, 2020 at 10:27 PM Larry Garfield larry@garfieldtech.com
wrote:Performing validation when the getAttributes() call is performed does
sound
reasonable to me. We can also add a class flag to perform this validation
only once (if it is successful), so the cost doesn't have to be paid by
every single getAttributes() consumer.For the purpose of this RFC, I've now updated it to say that attributes
will be applied to both properties and parameters (
https://wiki.php.net/rfc/constructor_promotion#attributes), but with an
explicit note that we should change the behavior prior to the PHP 8
release
if it turns out to be problematic, e.g. with regards to an attribute
validation feature. I think this is something of a detail, and we'll be
mostly fine whichever way we chose, but it's hard to say right now which
option is best, in particular with regard to future attributes
improvements.Regards,
NikitaThinking on it further, there's something sitting in the back of my head
that's bothering me. This helped me flesh it out.I like promotion, and think it will be great for simplifying code.
However, as attributes show as we load more bells and whistles onto
properties (even if they are desirable and useful bells and whistles) the
level of conflict is going to get larger.Consider, today we have:
class Point {
protected int $x;
protected int $y;public function __construct(int $x, int $y) {
$this->x = $x;
$this->y = $y;
}
}And this is needlessly verbose. With promotion, that simplifies to:
class Point {
public function __construct(protected int $x, protected int $y) {}
}And this is good. There seems little dispute about that.
But then we add attributes, and it starts to get a little odd:
class Point {
public function __construct(
<<Positive>>
protected int $x,<<Positive>> protected int $y,
) {}
}And has the possible confusion between parameter attributes and property
attributes.But... there's plenty of other things that may get added to properties.
We've discussed the "readonly" property and the more robust assymetric
visibility. What would that look like?class Point {
public function __construct(
<<Positive>>
int $x {
public get,
private set,
},<<Positive>> int $y { public get, private set, },
) {}
}And that's now an awful lot of metadata to shove into, technically, a
function signature. And if accessor methods ever happen:class Point {
public function __construct(
<<Positive>>
int $x {
public get () {
return $this->x;
},
private set($new) {
if ($new < 0) {
throw new Exception();
}
$this->x = $new;
},
},<<Positive>> int $y { public get () { return $this->y; }, private set($new) { if ($new < 0) { throw new Exception(); } $this->y = $new; }, },
) {}
}And now we have methods defined inside a function signature, and that's
just bizzarro. It also means at least 4 indent levels before you even get
to the body of the methods.And there's probably other things that could get attached to properties at
some point I'm not thinking of that someone will suggest.So I have to wonder, are we painting ourselves into a hole? Most of the
uses I can think of for extra-metadata-properties are... overlapping with
the case where you want to have constructor promotion. That means "well if
you need to do fancy stuff just don't use promotion" becomes a
non-sustainable answer, because the cases where you want both will increase
rather than be an edge case.Is there a way we can build in an escape hatch for ourselves to avoid
these issues, both attributes and for future extension?First idea off my head, which may or may not be good:
Allow an alternate keyword to fill in the body of the constructor, but NOT
fill in the property. You still define the property yourself and put all
the extra metadata there. To wit:class Point {
<<Positive>>
int $x {
public get () {
return $this->x;
},
private set($new) {
if ($new < 0) {
throw new Exception();
}
$this->x = $new;
},
},<<Positive>>
int $y {
public get () {
return $this->y;
},
private set($new) {
if ($new < 0) {
throw new Exception();
}
$this->y = $new;
},
},public function __construct(promote int $x, promote int $y) {}
}That eliminates 2 of the 4 places you'd have to repeat the name instead of
3, but means the constructor isn't burdened with having to hold a
potentially non-trivial amount of metadata. It also resolves the attribute
question as splitting it up like this would let you do "one or the other"
(property vs parameter).That may not be the best way, but I'm slightly uneasy with a path that
could lead to 30 lines of property metadata squeezed into a constructor
signature definition. :-)Hey Larry,
At least for me, there's a pretty clear line on what is acceptable to use
with constructor promotion and what isn't: That line has been crossed when
your property declarations started to include their own {} blocks.
Constructor promotion is a short-hand notation for the cases where the
constructor boilerplate is much larger than the actual functionality. If
you have 100 lines of property declarations due to heavy accessor use, then
I think the value proposition of constructor promotion converges to zero,
because the constructor boilerplate is no longer a dominating factor.Regarding your last example, something similar to this has already been
proposed (and declined) in a slightly different form in
https://wiki.php.net/rfc/automatic_property_initialization:class Point {
public int $x { ... };
public int $y { ... };public function __construct(int $this->x, int $this->y) {}
}
Regards,
Nikita
I agree that "when it has its own block statements" is a reasonable heuristic. My concern is that I see those situations (where you'd have block statements and where promotion would be helpful because the constructor is otherwise useless) as having very high overlap. Even without full accessors, asymmetric visibility is a natural place for them to overlap. I don't have data on that (obviously, since properties don't have block statements now), just a gut feeling.
I'm trying to figure out how we can plan ahead and avoid an "all or nothing" problem in the future. A "promote" keyword in the future might be an option; we don't have to fully solve it now, just make sure we're not painting ourselves into a corner.
--Larry Garfield
Hi Nikita,
Nikita Popov wrote:
I think the best answer to this question may be to forbid the use of
attributes on promoted properties entirely, because there is no unambiguous
interpretation for them. I also think that using attributes pushes this
"syntax sugar" to its limit, as you can easily end up with a 50 line
constructor signature that way, at least if I'm going by some of the more
involved uses of annotations...
I think that is a great starting point because it is a safe decision: if
we later decide there is a better behaviour, we can change it without
causing any problems for existing code. So, if we don't come up with a
better idea, I would strongly recommend that one. I also agree with your
other thoughts on it. :)
Thanks,
Andrea
Hi internals,
I would like to submit the following RFC for your consideration:
https://wiki.php.net/rfc/constructor_promotionThis is based on one off the suggestions made in
https://externals.io/message/109220, and some existing discussion on the
topic can be found in that thread.The primary motivation for this feature is to close the currently rather
wide gap between the use of ad-hoc array structures, and the use of
well-defined and type-safe value objects. The latter is what we want people
to use, but the former is, unfortunately, what is easy to use.Regards,
Nikita
I am rather concerned about constructor promotion will result in some complex "single" lines of code that all must be syntactically correct for any of it to be syntactically correct. It will make it more challenging to read and write code as we add attributes and PHP doc to properties. And if we still have to deal with concern that parameter names that were previously never indented to become part of the API will be exposed as part of the API, with potential breakage when name changes.
Pondering these issues I think I have a very simple proposal that would address most (all?) of the concerns I am aware for both Constructor Property Promotion and Named Parameters.
Consider simply what we might call "Parameter Blocks." Since PHP always expects a parentheses to follow the function or method name it should be possible to opt-in replace it with a brace-enclosed block of parameters instead since it would be unambiguous and this no conflict, and no need for new keywords.
The following illustrates how it my be used by repurposing an example from the RFC:
abstract class Node {
public function __construct{
/**
* @param Location|null — The starting location for this abstract node
*/
Location $startLoc = null;
protected Location $startLoc = null;
/**
* @param Location|null — The ending location for this abstract node
*/
protected Location $endLoc = null;
} {}
}
class ParamNode extends Node {
public function __construct{
/**
* @param string $name — Names this Node.
*/
public string $name;
/**
* @param ExprNode|null $default — Contains the expression this node represents
*/
<<ORM\Entity("exprNode")>>
public ExprNode $default = null;
/**
* @param TypeNode|null $type — Contains the type expected for this node's expression
*/
<<ORM\Entity("typeNode")>>
public TypeNode $type = null;
/**
* @param bool $byRef — True if this node is a reference, false otherwise
*/
public bool $byRef = false;
/**
* @param bool $byRef — True if this node is variadic, false otherwise
*/
public bool $variadic = false;
/**
* @param Location|null — The starting location for this node
*/
Location $startLoc = null;
/**
* @param Location|null — The ending location for this node
*/
Location $endLoc = null;
} {
parent::__construct($startLoc, $endLoc);
}
}
Envision how that same code would have to be written if all as part of the single "line" contained within the parenthesis of a programming language?
As far as I can tell this is a clean and elegant approach with many fewer downsides than those that have been discussed thus far. I hope that each of you reading this will seriously consider this is viable alternative option.
-Mike
Hi Mike,
Consider simply what we might call "Parameter Blocks." Since PHP always expects a parentheses to follow the function or method name it should be possible to opt-in replace it with a brace-enclosed block of parameters instead since it would be unambiguous and this no conflict, and no need for new keywords.
The following illustrates how it my be used by repurposing an example from the RFC:
...
I'm not really clear what this example is showing. It seems to be the
same as the one in Nikita's RFC, but with the punctuation changed
slightly from "(public $foo, public $bar)" to "{public $foo; public $bar;}"
I'm not sure how this changes anything, or how it relates to named
parameters. Could you expand on the problems you see this solving?
Regards,
--
Rowan Tommins (né Collins)
[IMSoP]
Hi Mike,
Consider simply what we might call "Parameter Blocks." Since PHP always expects a parentheses to follow the function or method name it should be possible to opt-in replace it with a brace-enclosed block of parameters instead since it would be unambiguous and this no conflict, and no need for new keywords.
The following illustrates how it my be used by repurposing an example from the RFC:
...I'm not really clear what this example is showing. It seems to be the same as the one in Nikita's RFC, but with the punctuation changed slightly from "(public $foo, public $bar)" to "{public $foo; public $bar;}"
That is exactly the case.
I'm not sure how this changes anything, or how it relates to named parameters. Could you expand on the problems you see this solving?
Let's look at it like this, with line numbers that would be reported in the case of syntax error:
1: function foo(public $foo, public $bar) {}
vs.
1: function foo {
2: public $foo;
3: public $bar;
4: } {}
Now, consider the following two examples. On which line would PHP report the error?
function foo() (
public $foo,
public $ bar
) {}
vs.
function foo {
public $foo;
public $ bar;
} {}
With two parameters, no attributes and no PHP doc the error is pretty easy to spot.
Now consider a constructor w/20 properties/parameters, 5 attributes across those properties, and a total of 100 lines of PHP Doc interspersed and it becomes a lot more difficult to deal with if it is one virtual line — as PHP sees it — than spread across 125 lines.
Much harder to diagnose a syntax error.
It is for the same reason I avoid code that instantiates a single array literal that spans hundreds of line of code and instead create methods to append small array literals to the array. If you have one really long virtual line it makes it much harder to identify where syntax errors occurred.
Having one long virtual line also limits what features can be added in future versions of PHP.
If you need help visualizing it I mocked up an example from actual function that is in product that was written by someone else before I joined the client's current team:
https://gist.github.com/mikeschinkel/faaee3fc8ccc2371c7200f3d634289c4 https://gist.github.com/mikeschinkel/faaee3fc8ccc2371c7200f3d634289c4
-Mike
Now, consider the following two examples. On which line would PHP report
the error?function foo() (
public $foo,
public $ bar
) {}
vs.
function foo {
public $foo;
public $ bar;
} {}
This feels like an implementation detail of the parser, rather than an
automatic consequence of the syntax, and the current parser seems to handle
it just fine.
<?php
function foo(
$foo,
$ bar
) {}
unexpected '$', expecting variable... on line 4
https://3v4l.org/AC9kh
The same is true of arrays: both syntax and run-time errors are reported
with correct line numbers, e.g. https://3v4l.org/fMp4t and
https://3v4l.org/BXO8q
If you need help visualizing it I mocked up an example from actual
function that is in product that was written by someone else before I
joined the client's current team:https://gist.github.com/mikeschinkel/faaee3fc8ccc2371c7200f3d634289c4
Here's the single-declaration version of that with a syntax error on line
163: https://3v4l.org/mGq2f
I think you're solving a non-existent problem here.
Regards,
Rowan Tommins
[IMSoP]
the current parser seems to handle it just fine.
Interesting. You are correct on this point. I was going by memory that we had lots of problems with nested arrays, I should have tested this one assertion before posting.
I think you're solving a non-existent problem here.
True, but that was only one of the several reasons I proposed the atlernate syntax, if you'll reread my original post you'll see the next point:
- The proposal on the table for Named Parameters effectively converts all parameters names into public aspects of the their function's API. Some suggested allowing aliases, allowing OUT-OUT, but AFAIK there have been no suggestion for how to OPT-OUT of named parameters entirely.
IOW, with the proposed syntax Named Parameters would infect every function/method across all of userland PHP code, whether the developer wants to maintain it as part of their API or not. This would be like PHP 8.0 making all private variable publicly accessible.
This alternate syntax would make Named Parameters OPT-IN allowing developers to actively CHOOSE them — or not — as part of their public API.
The rest were not part of my first email as I was trying to keep it shorter, but I now see my brevity was not effective as I had hoped, so...
-
Adding the ability to fully parse all features of properties and parameters into two different contexts each with their own syntax variants will add ongoing maintenance burden of core (or at least it seems apparent that it would.)
-
The alternate block syntax would simplify userland refactoring to move properties to parameters and back when compared to the RFC for CPP. Mostly (all?) it would require would be copying and pasting of lines. The RFC syntax would require at least a minimum amount of refactoring (semicolons to commas and back.)
-
Who knows what syntax contortions will be required for parameters as we try to apply new language features that affect properties? IOW, the current RFC is a less safe for future compatibility than the alternate block syntax.
-
New features for properties will likely take more discussion time to determine acceptable alternate syntaxes for using inside the parenthesis and then more time to implement, possibly delaying universally desired features or even nipping them in the bud.
-
Userland tooling would need to support both syntaxes, requiring more work for those project's maintainers, probably delay updates, and certainly requiring more ongoing maintenance.
-
Userland developers will be confused by and have to learn two slightly different syntaxes for essentially the same (type of) thing.
-
I believe this alternate syntax addresses Michal Bruzuchalski's and Larry Garfield's concerns, at least partially:
https://externals.io/message/109335#109343
https://externals.io/message/109335#110024
- This alternate syntax would reduce the desugaring required; only the value assignment would be needed:
https://wiki.php.net/rfc/constructor_promotion#desugaring
https://externals.io/message/109335#109515
- Nikita claims there is a "line" to be crossed when property declarations have their own blocks and would disallow their use with CPP. And with the RFC's syntax that seems indeed an appropriate limitation. But with the alternate proposed syntax it would not need to be a limitation and I would argue that fewer limitations on future potential would be better.
https://externals.io/message/109335#110034
And finally:
- The current Constructor Property Promotion adds complexity to the language where there is no analogue elsewhere PHP, and having things "be like existing PHP" is a frequent argument related to new features on this list.
PHP is primarily a line and block oriented language, and CPP purports to add Javascript-like complexity into a single statement. Frankly that is one of the reasons I so dislike programming in Javascript; I find the smaller distinct line-oriented statements of PHP much easier to reason about and would think many userland developers would also.
The alternate syntax fits in with PHP's existing line- and block-oriented flavor much better the RFC's syntax. Simplicity (should) win(s).
-Mike
- The proposal on the table for Named Parameters effectively converts
all parameters names into public aspects of the their function's API.
Yes, an opt-in version of named parameters might be useful. However, I'm
not immediately convinced it should share any syntax with property
promotion; the only real connection is that we're discussing them at the
same time.
- Adding the ability to fully parse all features of properties and parameters into two different contexts each with their own syntax variants will add ongoing maintenance burden of core (or at least it seems apparent that it would.)
- Who knows what syntax contortions will be required ...
- New features for properties will likely take more discussion time to determine acceptable alternate syntaxes ...
The only reason I can think there would need to be variations in the
syntax would be if there was some new syntax for properties which used
commas in a way that would be ambiguous in parameter lists. Otherwise,
parsing a promoted parameter is exactly the same as parsing a
non-promoted one, but you stop at "," rather than ";".
- The alternate block syntax would simplify userland refactoring to move properties to parameters and back...
- The current Constructor Property Promotion adds complexity to the language where there is no analogue elsewhere PHP
By its nature, this proposed feature is merging two existing syntaxes.
The current syntax proposal looks mostly like the constructor's existing
parameter list; yours looks mostly like the existing property
declarations; I'm not sure either is automatically better.
Switching to or from promoted properties will always require changing
one or both parts of the class - the largest edit will be deleting or
re-creating the body of the constructor.
- Userland tooling would need to support both syntaxes...
- Userland developers will be confused by and have to learn two slightly different syntaxes for essentially the same (type of) thing.
These points are both true no matter what the syntax looks like: it will
be a new syntax, for a new feature, and tools and programmers will have
to adapt to that. Glancing at the implementation on the RFC, the changes
to the actual PHP parser actually seem fairly small.
- I believe this alternate syntax addresses Michal Bruzuchalski's and Larry Garfield's concerns...
- Nikita claims there is a "line" to be crossed when property declarations have their own blocks and would disallow their use with CPP...
I hesitate to speak for them, but I think their concerns are more
conceptual: that this requires nesting a large portion of the class
definition inside the constructor definition. Changing the syntax
doesn't really change that - the "parameter block" would still be an
extra level of nesting, attached to the constructor definition, rather
than at the top level of the class, where it lives today.
- This alternate syntax would reduce the desugaring required
The compiler won't need to transform the actual source code; the
"de-sugaring" is just a representation of the equivalent code if
written manually.
PHP is primarily a line and block oriented language, and CPP purports to add Javascript-like complexity into a single statement.
I'm not sure what is "Javascript-like" about the proposed syntax, or why
using semicolons rather than commas makes it less so.
Regards,
--
Rowan Tommins (né Collins)
[IMSoP]
- I believe this alternate syntax addresses Michal Bruzuchalski's and Larry Garfield's concerns...
- Nikita claims there is a "line" to be crossed when property declarations have their own blocks and would disallow their use with CPP...
I hesitate to speak for them, but I think their concerns are more
conceptual: that this requires nesting a large portion of the class
definition inside the constructor definition. Changing the syntax
doesn't really change that - the "parameter block" would still be an
extra level of nesting, attached to the constructor definition, rather
than at the top level of the class, where it lives today.
Correct. My concern isn't with comma vs semicolon. My concern is that the property definition is then living between public function __construct(
and ) {
.
When the property definition is basic (visibility, type, and something like readonly) that's still reasonable enough for the benefit it brings.
When the property definition becomes involved enough that it is preferable to have it span lines (docblock, attributes, or something like property accessors or potentially even just asymmetric visibility), that's when I get fidgety, because then you're technically writing
public function __construct(/** @see blah.html / <<Positive>> protected int $count, /* @see blah.html */ <<Positive>> <<LessThan(99)>> protected int $age,) {
}
Just with convenience newlines. Semi-colons and curly braces wouldn't in themselves address that.
What might would be putting promotable properties in an entirely separate construct from the constructor.
An off the cuff riff from that might be something like:
class Stuff {
promoted {
/**
* @see blah.html
*/
<<Positive>>
protected int $count;
/**
* @see blah.html
*/
<<Positive>>
<<LessThan(99)>>
protected int $age;
}
}
That moves them out of the constructor entirely, and might overlap with the desire for named parameters.
$p = new Stuff(5, 30);
// or
$p = new Stuff{count: 5, age: 30};
However, that has the major drawback of then not being in the constructor, so promoting only some constructor arguments becomes impossible. It's also worse on the BC front.
I don't know if that's a good idea or not; as I said, just an off-the-cuff riff on what would address the "complex properties" question better. For the simple case, the current RFC syntax seems fine. (Maybe someone can riff off of this to something better.)
--Larry Garfield
I am rather concerned about
Mike,
It's nice that you want to contribute to PHP. But trying to brainstorm
ideas over email is not productive.
Spending your time helping get the manual more up-to-date would be far
more beneficial than throwing out "Hey why don't you try this"
comments.
a standard library of PHP-written functionality bundled in a PHAR file that would be included with future implementations of PHP and automatically included by PHP.
Anyone who has worked with Java or Go would know that having core
functionality separate from the executable would bring in a whole new
set of error conditions related to classpath or gopath. And
additionally version mismatch problems between the library found, and
the language executable being invoked.
Additionally, while both strings and arrays in PHP work, how they are
implemented leaves something to be desired. For strings, it's the
behaviour around character sets/unicode. For arrays, it's a 'having a
single data structure for all possible needs' problem.
Spending time on a 'standard library' before there have been
improvements to strings and/or arrays is unlikely to produce anything
of great value.
As I said, if you have energy that can be used to make PHP better, I
think helping with the manual would be useful
https://github.com/php/doc-en Or if you can, getting up to speed with
C to work on bugs would also be good https://bugs.php.net/ But just
throwing out ideas is not a productive use of your time and distracts
people from more useful conversations.
cheers
Dan
Ack
Mike,
It's nice that you want to contribute to PHP. But trying to brainstorm
ideas over email is not productive.
Hi Dan,
I appreciate that you, too, want to make PHP better, as a language and
as a project; but just about everything in this e-mail is wrong.
Firstly, you've managed to reply to the wrong thread.
Secondly, you've once again taken it on yourself to proclaim when people
should and shouldn't use this e-mail list, without actually asking
anyone else's opinion.
Thirdly, Mike is not the only one "brainstorming" this particular idea,
as your comments about "spending your time better elsewhere" imply. Ben
Ramsey, Larry Garfield, and Dik Takken have all expressed support for
the general idea, and it has come up in other discussions as a potential
benefit of preloading and JIT.
I certainly don't agree with all Mike's ideas, nor do I always feel the
mailing list is as productive as it could be, but I think this message
was just way off base.
Regards,
--
Rowan Tommins (né Collins)
[IMSoP]
Hi internals,
I would like to submit the following RFC for your consideration:
https://wiki.php.net/rfc/constructor_promotionThis is based on one off the suggestions made in
https://externals.io/message/109220, and some existing discussion on the
topic can be found in that thread.The primary motivation for this feature is to close the currently rather
wide gap between the use of ad-hoc array structures, and the use of
well-defined and type-safe value objects. The latter is what we want people
to use, but the former is, unfortunately, what is easy to use.Regards,
Nikita
I know I said it a few times already, but ... I plan to start voting on
this soon. There has been some additional discussion recently on
alternative syntax/approaches, such as Mike's parameter blocks, and Larry's
"promoted" keyword. I wasn't really convinced by either of those, so I plan
to go forward with the proposal as is.
Nikita
Please tell me if you have further concerns
Question - as this is just sugar, presumably it doesn't add much
complexity to the PHP engine?
Vague concern - because it's a novel syntax, I find it hard to read,
particularly when there are comments* for the properties. Do you think
I'll just get used to it, or is it likely to be an ongoing issue?
Also, in five years time, if we have structs**, people will be
wondering why we bothered with this. But I think it does make life
easier for developers in the meantime.
cheers
Dan
Ack
- Example of comments.
class UrlToPdfProcessor
{
public function __construct(
// The full url to reach the instance of chrome running in
headless mode.
public string $chromeUrl,
// A path to a directory where the output PDF will be written to.
// It must already exist and be writeable by the process running
// the PDF processor.
public string $tmpPath,
// The internal domain name for the site to be rendered.
// The domain name should not contain either protocol or path
// components
string $internalDomain
) {
}
}
** structs - https://github.com/Danack/RfcCodex/blob/master/structs.md
Please tell me if you have further concerns
Question - as this is just sugar, presumably it doesn't add much
complexity to the PHP engine?
Yes, the implementation complexity for this feature is minimal, as these
things go.
Vague concern - because it's a novel syntax, I find it hard to read,
particularly when there are comments* for the properties. Do you think
I'll just get used to it, or is it likely to be an ongoing issue?
Well, I can't really answer this with confidence, but given the number of
people who told me how much they love this feature in TypeScript, I would
hazard that you would indeed get used to it.
Also, in five years time, if we have structs**, people will be
wondering why we bothered with this. But I think it does make life
easier for developers in the meantime.
When we discussed this topic in chat last time, I got the feeling that the
rough consensus was that introducing "structs" as a feature distinct from
classes is a bad idea, and we should instead try to make relevant features
available for classes. Constructor promotion is one of those :)
class UrlToPdfProcessor
{
public function __construct(
// The full url to reach the instance of chrome running in
headless mode.
public string $chromeUrl,// A path to a directory where the output PDF will be written to. // It must already exist and be writeable by the process running // the PDF processor. public string $tmpPath, // The internal domain name for the site to be rendered. // The domain name should not contain either protocol or path // components string $internalDomain
) {
}
}
As suggested in
https://wiki.php.net/rfc/constructor_promotion#coding_style_considerations,
my recommendation is to use normal @param annotations for constructor
arguments, as we do now. While interleaving parameters and comments is
certainly possible (already), it's certainly not common style, and I don't
think it will become common style with constructor promotion.
Regards,
Nikita