Hello everyone,
I have completed the draft for an RFC, to add Typed Properties. The
patch has been written by the one and only Joe Watkins.
https://wiki.php.net/rfc/typed-properties
I would really appreciate constructive feedback on this RFC, with a
few areas especially:
-
How scared are we that integers can be expanded to floats on runtime?
-
This whole temporary nullability situation, where unset properties
will error on attempted usage if not set. Should they instead error
after the constructor has been called if they are still not holding a
value? -
Weak vs Strict. Right now this is entirely strict, with no
declare() to change mode. Reasons for this vary, from various sources,
but include "Not sure how to implement it" and "Well people should not
be using properties as part of their public API".
Help on 3 would be appreciated.
Also let's please avoid "PHP IS TURNING INTO JAVA" and the other
rather common rhetoric. Strict Type Hinting might have been seen as a
battleground for fans of strict and fans of weak to fight through a
keyboard, but this RFC will not be the repeat.
We'll have a nice, orderly, constructive conversation about this RFC,
and improve the patch as you all provide feedback.
Let me know what you think folks!
Hello everyone,
I have completed the draft for an RFC, to add Typed Properties. The
patch has been written by the one and only Joe Watkins.https://wiki.php.net/rfc/typed-properties
I would really appreciate constructive feedback on this RFC, with a
few areas especially:
- How scared are we that integers can be expanded to floats on runtime?
First, this looks awesome, Phil!
After reading through it, I think we can consult with function return
types:
function blah() : int {
return 55.5;
}
var_dump(blah());
Will return 55. It's a little different with properties, but what are
your thoughts on this runtime conversion?
This whole temporary nullability situation, where unset properties
will error on attempted usage if not set. Should they instead error
after the constructor has been called if they are still not holding a
value?Weak vs Strict. Right now this is entirely strict, with no
declare() to change mode. Reasons for this vary, from various sources,
but include "Not sure how to implement it" and "Well people should not
be using properties as part of their public API".Help on 3 would be appreciated.
Also let's please avoid "PHP IS TURNING INTO JAVA" and the other
rather common rhetoric. Strict Type Hinting might have been seen as a
battleground for fans of strict and fans of weak to fight through a
keyboard, but this RFC will not be the repeat.We'll have a nice, orderly, constructive conversation about this RFC,
and improve the patch as you all provide feedback.Let me know what you think folks!
Hello everyone,
I have completed the draft for an RFC, to add Typed Properties. The
patch has been written by the one and only Joe Watkins.https://wiki.php.net/rfc/typed-properties
I would really appreciate constructive feedback on this RFC, with a
few areas especially:
- How scared are we that integers can be expanded to floats on runtime?
First, this looks awesome, Phil!
After reading through it, I think we can consult with function return
types:function blah() : int {
return 55.5;
}var_dump(blah());
Will return 55. It's a little different with properties, but what are
your thoughts on this runtime conversion?
Hey Will,
Sure you'll see 55 in weak mode, but in strict mode you'll see 55.5.
I don't really want to delve too much into how return types work in
this RFC, because it doesn't feel entirely relevant.
- Weak vs Strict. Right now this is entirely strict, with no
declare() to change mode. Reasons for this vary, from various sources,
but include "Not sure how to implement it" and "Well people should not
be using properties as part of their public API".
As much as I didn't (and don't) particularly like the declare()
switch, it doesn't seem like a good idea to me to introduce a typing
feature a year after it that doesn't use it, but has its own mode of
operation. To me, it seems like this:
class Foo {
public int $num;
}
(new Foo)->num = $bar;
Should behave the same as the setter equivalent does today:
class Foo {
public $num;
public function setNum(int $num) { $this->num = $num; }
}
(new Foo)->setNum($num);
That is: if $num either can't be coerced to an integer (in weak mode)
or isn't itself an integer (in strict mode), a TypeError should be
thrown.
We could argue about whether properties should be part of a public
API, but the reality is that a class declaring a public property
effectively is making it part of its API, whether you or I think it's
a good idea or not. :)
We'll have a nice, orderly, constructive conversation about this RFC,
and improve the patch as you all provide feedback.Let me know what you think folks!
The above leads into another question I'm interested in your (and
Joe's) general thoughts on: how do you think this would potentially
intersect with a property getter/setter RFC in the future? Might be
good fodder for the future scope section!
Finally, while the RFC shows invalid assignments generating fatal
errors, the patch seems like it throws TypeError exceptions instead.
Which one is the desired behaviour? (I'd prefer TypeError, personally,
for consistency with function type declarations today.)
Adam
- Weak vs Strict. Right now this is entirely strict, with no
declare() to change mode. Reasons for this vary, from various sources,
but include "Not sure how to implement it" and "Well people should not
be using properties as part of their public API".As much as I didn't (and don't) particularly like the declare()
switch, it doesn't seem like a good idea to me to introduce a typing
feature a year after it that doesn't use it, but has its own mode of
operation. To me, it seems like this:class Foo {
public int $num;
}(new Foo)->num = $bar;
Should behave the same as the setter equivalent does today:
class Foo {
public $num;
public function setNum(int $num) { $this->num = $num; }
}(new Foo)->setNum($num);
That is: if $num either can't be coerced to an integer (in weak mode)
or isn't itself an integer (in strict mode), a TypeError should be
thrown.We could argue about whether properties should be part of a public
API, but the reality is that a class declaring a public property
effectively is making it part of its API, whether you or I think it's
a good idea or not. :)We'll have a nice, orderly, constructive conversation about this RFC,
and improve the patch as you all provide feedback.
Let me know what you think folks!
The above leads into another question I'm interested in your (and
Joe's) general thoughts on: how do you think this would potentially
intersect with a property getter/setter RFC in the future? Might be
good fodder for the future scope section!
Yeah, I'll add a note about that. :)
Finally, while the RFC shows invalid assignments generating fatal
errors, the patch seems like it throws TypeError exceptions instead.
Which one is the desired behaviour? (I'd prefer TypeError, personally,
for consistency with function type declarations today.)Adam
If you put them in the class definition you'll get a fatal error from
the compiler (might change) and at runtime you get an Error. Different
things for different things. :)
Hey, thank you both for investing your time :-)
Property types are definitely interesting.
Am 16.03.2016 um 17:36 schrieb Phil Sturgeon pjsturgeon@gmail.com:
Hello everyone,
I have completed the draft for an RFC, to add Typed Properties. The
patch has been written by the one and only Joe Watkins.https://wiki.php.net/rfc/typed-properties
I would really appreciate constructive feedback on this RFC, with a
few areas especially:
- How scared are we that integers can be expanded to floats on runtime?
This IMHO is quite necessary, for the same reasons why we even allow int to float widening with strict return types.
class foo {
float $bar;
function __construct(int $x) {
assert($x > 0);
$this->bar = 10 / $x;
}
}
This is perfectly innocuous, but will fail for values 1, 2, 5 and 10, if we don't allow widening.
Depending on the values it might also be not caught in testing.
- This whole temporary nullability situation, where unset properties
will error on attempted usage if not set. Should they instead error
after the constructor has been called if they are still not holding a
value?
Erroring when using the unset property is the way to go.
The example from the RFC is showing exactly why...
class Foo {
public int $foo;
}
$foo = new Foo();
echo $foo->foo; // Patch currently errors here
With one property, not so much an issue, we could force a constructor.
But with 20 properties, definitely an issue.
If we'd error after constructor call, there are also other complications…
class Foo {
public int $bar;
function __construct() {
global $foo;
$foo = $this;
}
}
try {
new Foo;
} catch (Error $e) {}
var_dump($foo->bar); // not set, but instance is referenced
Sure, we could find ways to circumvent that and change the class to totally non-functional and making every usage of it throw an Error, but it isn't quite elegant.
- Weak vs Strict. Right now this is entirely strict, with no
declare() to change mode. Reasons for this vary, from various sources,
but include "Not sure how to implement it" and "Well people should not
be using properties as part of their public API".Help on 3 would be appreciated.
Now, if we already have int to float widening (which I recommend), we anyway need to do certain checks.
At that point we can just as well add weak typing, because, why not?
We do not have an exceptional handling for return types (which are even more subject to be strict-only as it is function-local — properties may be public). And so we shouldn't have either for property types.
Thus, my recommendation is to be consistent regarding strict and weak typing regarding argument and return types.
Also let's please avoid "PHP IS TURNING INTO JAVA" and the other
rather common rhetoric. Strict Type Hinting might have been seen as a
battleground for fans of strict and fans of weak to fight through a
keyboard, but this RFC will not be the repeat.We'll have a nice, orderly, constructive conversation about this RFC,
and improve the patch as you all provide feedback.Let me know what you think folks!
Thanks,
Bob
Hey, thank you both for investing your time :-)
Property types are definitely interesting.Am 16.03.2016 um 17:36 schrieb Phil Sturgeon pjsturgeon@gmail.com:
Hello everyone,
I have completed the draft for an RFC, to add Typed Properties. The
patch has been written by the one and only Joe Watkins.https://wiki.php.net/rfc/typed-properties
I would really appreciate constructive feedback on this RFC, with a
few areas especially:
- How scared are we that integers can be expanded to floats on runtime?
This IMHO is quite necessary, for the same reasons why we even allow int
to float widening with strict return types.class foo {
float $bar;
function __construct(int $x) {
assert($x > 0);
$this->bar = 10 / $x;
}
}This is perfectly innocuous, but will fail for values 1, 2, 5 and 10, if
we don't allow widening.
Depending on the values it might also be not caught in testing.
- This whole temporary nullability situation, where unset properties
will error on attempted usage if not set. Should they instead error
after the constructor has been called if they are still not holding a
value?Erroring when using the unset property is the way to go.
The example from the RFC is showing exactly why...
class Foo {
public int $foo;
}
$foo = new Foo();
echo $foo->foo; // Patch currently errors hereWith one property, not so much an issue, we could force a constructor.
But with 20 properties, definitely an issue.If we'd error after constructor call, there are also other complications…
class Foo {
public int $bar;
function __construct() {
global $foo;
$foo = $this;
}
}
try {
new Foo;
} catch (Error $e) {}
var_dump($foo->bar); // not set, but instance is referencedSure, we could find ways to circumvent that and change the class to
totally non-functional and making every usage of it throw an Error, but it
isn't quite elegant.
- Weak vs Strict. Right now this is entirely strict, with no
declare() to change mode. Reasons for this vary, from various sources,
but include "Not sure how to implement it" and "Well people should not
be using properties as part of their public API".Help on 3 would be appreciated.
Now, if we already have int to float widening (which I recommend), we
anyway need to do certain checks.
At that point we can just as well add weak typing, because, why not?We do not have an exceptional handling for return types (which are even
more subject to be strict-only as it is function-local — properties may be
public). And so we shouldn't have either for property types.Thus, my recommendation is to be consistent regarding strict and weak
typing regarding argument and return types.Also let's please avoid "PHP IS TURNING INTO JAVA" and the other
rather common rhetoric. Strict Type Hinting might have been seen as a
battleground for fans of strict and fans of weak to fight through a
keyboard, but this RFC will not be the repeat.We'll have a nice, orderly, constructive conversation about this RFC,
and improve the patch as you all provide feedback.Let me know what you think folks!
Thanks,
Bob
I'm not 100% convinced that we need this now we have full type hint support
for parameters and return types; but it does have potential to remove
boiler plate for public properties. For point 2 nullable properties, how
about some syntax which allows for a property to be null, or for it to be a
specific type. I'd suggest a null assignment to fall in line with parameter
type hints eg:
class Foo {
protected int $bar = null; //can be null
private stdClass $baz; //can't be null
}
Am 16.03.2016 um 18:27 schrieb Chris Riley t.carnage@gmail.com:
Am 16.03.2016 um 17:36 schrieb Phil Sturgeon <pjsturgeon@gmail.com mailto:pjsturgeon@gmail.com>:
- This whole temporary nullability situation, where unset properties
will error on attempted usage if not set. Should they instead error
after the constructor has been called if they are still not holding a
value?Erroring when using the unset property is the way to go.
The example from the RFC is showing exactly why...
class Foo {
public int $foo;
}
$foo = new Foo();
echo $foo->foo; // Patch currently errors hereWith one property, not so much an issue, we could force a constructor.
But with 20 properties, definitely an issue.If we'd error after constructor call, there are also other complications…
class Foo {
public int $bar;
function __construct() {
global $foo;
$foo = $this;
}
}
try {
new Foo;
} catch (Error $e) {}
var_dump($foo->bar); // not set, but instance is referencedSure, we could find ways to circumvent that and change the class to totally non-functional and making every usage of it throw an Error, but it isn't quite elegant.
I'm not 100% convinced that we need this now we have full type hint support for parameters and return types; but it does have potential to remove boiler plate for public properties. For point 2 nullable properties, how about some syntax which allows for a property to be null, or for it to be a specific type. I'd suggest a null assignment to fall in line with parameter type hints eg:
class Foo {
protected int $bar = null; //can be null
private stdClass $baz; //can't be null
}
The point is basically, the value should never be accessed and actually return null.
If it is accessed, it always should return the type I provided.
Having it potentially return null would completely miss the point (and also confuse static analysis which would assume the value could be null, when in reality it never should be).
Concrete example: https://github.com/amphp/aerys/blob/master/lib/InternalRequest.php https://github.com/amphp/aerys/blob/master/lib/Client.php
That's a bunch of public properties (value object). The values are all initialized when the object is created — if a property is accessed and was forgotten to be initialized, it should error. That's helpful for detecting bugs.
You definitely don't want to define these values, which are supposed to be set upon initialization, to be possibly null. Because they actually are never null from their first read access on.
E.g. InternalRequest->responseWriter is always a Generator object when accessed. The value is never potentially null (and thus would theoretically have to be checked for being null or not).
Having to force null here, would be a big mistake. Semantically and for static analysis.
Bob
Hello everyone,
I have completed the draft for an RFC, to add Typed Properties. The
patch has been written by the one and only Joe Watkins.
I'm no fan of all this typing business, but that's a lost case. Anyways:
What about references? (yeah, references should die, but currently they
still exist)
What's expected here:
class A {
public int $i;
}
$a = new A();
$r = &$a->i;
$r = "foobar";
johannes
Am 16.03.2016 um 18:39 schrieb Johannes Schlüter johannes@schlueters.de:
Hello everyone,
I have completed the draft for an RFC, to add Typed Properties. The
patch has been written by the one and only Joe Watkins.I'm no fan of all this typing business, but that's a lost case. Anyways:
What about references? (yeah, references should die, but currently they
still exist)What's expected here:
class A {
public int $i;
}$a = new A();
$r = &$a->i;
$r = "foobar";johannes
The patch currently prevents that.
It prevents all creation of references to properties.
The reason is technical:
We'd basically need typed zvals.
Or add some sort of write callback to typed references.
If you have a clean solution to these issues, you're more than welcome to contribute here.
Bob
The patch currently prevents that.
It prevents all creation of references to properties.
To all properties? Thus
class C {
private $data = []; // No type, old code
public function sort()
{
sort($this->data);
}
}
won't work anymore? - I think that's a major break to the language.
Workaround then would be:
class C {
private $data = []; // No type, old code
public function sort()
{
$data = $this->data;
sort($data);
$this->data = $data;
}
}
Or is there a simpler way? - Quite ugly and quite some migration pain.
The reason is technical:
We'd basically need typed zvals.
Or add some sort of write callback to typed references.
That matches my assumption.
Slightly un-serious:
If you have a clean solution to these issues, you're more than welcome to contribute here.
- No typing
- No references
:-D
johannes
Am 16.03.2016 um 19:05 schrieb Johannes Schlüter johannes@schlueters.de:
The patch currently prevents that.
It prevents all creation of references to properties.To all properties? Thus
class C {
private $data = []; // No type, old codepublic function
sort()
{
sort($this->data);
}
}won't work anymore? - I think that's a major break to the language.
Workaround then would be:
class C {
private $data = []; // No type, old codepublic function
sort()
{
$data = $this->data;
sort($data);
$this->data = $data;
}
}Or is there a simpler way? - Quite ugly and quite some migration pain.
Eih, only to typed properties. Everything else would be insane ;-)
Sorry for being imprecise.
The reason is technical:
We'd basically need typed zvals.
Or add some sort of write callback to typed references.That matches my assumption.
Slightly un-serious:
If you have a clean solution to these issues, you're more than welcome to contribute here.
- No typing
- No references
:-D
E_BC_BREAK :-P
johannes
Bob
Eih, only to typed properties. Everything else would be insane ;-)
Sorry for being imprecise.
Ok, quite a lot better, but still weird difference to the language.
There's a notable amount of places where references are being used and
often users aren't aware. (Binding parameters in database queries, in
order to support OUT parameters, comes to mind, many array functions
take an array, some functions use it to report error codes, ...)
I guess this will be a famous support issue.
And yeah, I'd love to "fix" all those places and get rid of the
references, but BC ... it's quite massive :-)
johannes
Am 16.03.2016 um 19:41 schrieb Johannes Schlüter johannes@schlueters.de:
Eih, only to typed properties. Everything else would be insane ;-)
Sorry for being imprecise.Ok, quite a lot better, but still weird difference to the language.
There's a notable amount of places where references are being used and
often users aren't aware. (Binding parameters in database queries, in
order to support OUT parameters, comes to mind, many array functions
take an array, some functions use it to report error codes, ...)I guess this will be a famous support issue.
And yeah, I'd love to "fix" all those places and get rid of the
references, but BC ... it's quite massive :-)johannes
Totally one of my biggest issues with it...
While I'm liking the general idea, this definitely may be the reason why I'd vote "no" later.
But we're in a very early state, maybe some clever mind finds a solution to this problem and makes everyone happy :-)
Bob
class SomeStruct{ public int $a; }
class Joe
{
public SomeStruct $struct;
function test1() : SomeStruct{
return new SomeStruct(); // TypeError: can't return
a type whose fields' type declarations aren't fulfilled
}
function test2(SomeStruct $struct){ // TypeError: can't pass in
a type whose fields' type declarations aren't fulfilled [*]
}
function test3(){
$this->struct = new SomeStruct(); // TypeError: can't assign
to a typed field a type whose fields' type declarations aren't fulfilled
}
}
(new Joe)->test2(new SomeStruct()); // [*]
2016-03-16 19:41 GMT+01:00 Johannes Schlüter johannes@schlueters.de:
Eih, only to typed properties. Everything else would be insane ;-)
Sorry for being imprecise.Ok, quite a lot better, but still weird difference to the language.
There's a notable amount of places where references are being used and
often users aren't aware. (Binding parameters in database queries, in
order to support OUT parameters, comes to mind, many array functions
take an array, some functions use it to report error codes, ...)I guess this will be a famous support issue.
And yeah, I'd love to "fix" all those places and get rid of the
references, but BC ... it's quite massive :-)johannes
Hi Johannes,
Johannes Schlüter wrote:
Eih, only to typed properties. Everything else would be insane ;-)
Sorry for being imprecise.Ok, quite a lot better, but still weird difference to the language.
There's a notable amount of places where references are being used and
often users aren't aware. (Binding parameters in database queries, in
order to support OUT parameters, comes to mind, many array functions
take an array, some functions use it to report error codes, ...)I guess this will be a famous support issue.
And yeah, I'd love to "fix" all those places and get rid of the
references, but BC ... it's quite massive :-)
Disallowing references here might make users more aware of which
functions take parameters by reference, and avoid accidentally modifying
values. So this part of the RFC might be a good thing.
Thanks.
--
Andrea Faulds
https://ajf.me/
Andrea Faulds wrote on 17/03/2016 22:32:
Hi Johannes,
Johannes Schlüter wrote:
Eih, only to typed properties. Everything else would be insane ;-)
Sorry for being imprecise.Ok, quite a lot better, but still weird difference to the language.
There's a notable amount of places where references are being used and
often users aren't aware. (Binding parameters in database queries, in
order to support OUT parameters, comes to mind, many array functions
take an array, some functions use it to report error codes, ...)I guess this will be a famous support issue.
And yeah, I'd love to "fix" all those places and get rid of the
references, but BC ... it's quite massive :-)Disallowing references here might make users more aware of which
functions take parameters by reference, and avoid accidentally
modifying values. So this part of the RFC might be a good thing.
I'm not a fan of this kind of side-effect coupling - "because this
feature is new, it also triggers other new features". If you want no-ref
properties, that should be something a user chooses directly, e.g.
"class Foo { public noref $bar; }".
Adding an "array" typehint to something absolutely should not prevent it
being passed to sort()
, IMHO.
Perhaps we could have an IS_TYPED_REF zval? You don't actually need to
store the desired type, only assert that the old and new reference
targets are of the same type when performing assignment.
Regards,
Rowan Collins
[IMSoP]
On Mon, Mar 21, 2016 at 4:37 PM, Rowan Collins rowan.collins@gmail.com
wrote:
Andrea Faulds wrote on 17/03/2016 22:32:
Hi Johannes,
Johannes Schlüter wrote:
Eih, only to typed properties. Everything else would be insane ;-)
Sorry for being imprecise.Ok, quite a lot better, but still weird difference to the language.
There's a notable amount of places where references are being used and
often users aren't aware. (Binding parameters in database queries, in
order to support OUT parameters, comes to mind, many array functions
take an array, some functions use it to report error codes, ...)I guess this will be a famous support issue.
And yeah, I'd love to "fix" all those places and get rid of the
references, but BC ... it's quite massive :-)Disallowing references here might make users more aware of which
functions take parameters by reference, and avoid accidentally modifying
values. So this part of the RFC might be a good thing.I'm not a fan of this kind of side-effect coupling - "because this feature
is new, it also triggers other new features". If you want no-ref
properties, that should be something a user chooses directly, e.g. "class
Foo { public noref $bar; }".Adding an "array" typehint to something absolutely should not prevent it
being passed tosort()
, IMHO.Perhaps we could have an IS_TYPED_REF zval? You don't actually need to
store the desired type, only assert that the old and new reference targets
are of the same type when performing assignment.
While it is no secret that we don't particularly like references, this is
not the reason why they are forbidden. There is no conspiracy to punish
users of references by denying them use of new typing features. No, the
reasons here are technical. Let me illustrate some of the issues involved
in more detail.
Lets start with some more "conceptual" issue. Consider this code:
class Foo {
public int $bar = 42;
}
$foo = new Foo;
$bar =& $foo->bar;
unset($foo);
$bar = "xyz";
How does this code behave? The assignment $bar = "xyz" violates the "int"
typehint. However, the object those typed property we're referencing has
already been destroyed. Is the reference still typed or not?
If you answered "yes" to this question, consider an additional fact: It is
an implementation invariant that a reference with refcount 1 can be
converted to a value (a reference-set containing a single variable has
value semantics). The places where this happens or does not happens are not
evident to userland developers. The above code would throw, but if you
inserted a switch on $bar before hand it wouldn't. Wat?
If you answered "no" to this question, also consider another issue: In the
above example, the point where $foo is destroyed is clear. But what if the
destruction is actually triggered by a garbage collection run? As its not
possible to predict when garbage collection runs, you may have two directly
consecutive and identical statements, one of which throws, while the other
doesn't. (Not even to mention how impractical it is to track the
destruction of the object those property you're referencing.)
That's one issue. Another problem is related to "spooky action at a
distance". If you deal with parameter types or typed properties, you know
what you're in for. If you know the type of the object, you know what type
is expected. With references, this is generally not the case. You do not
know where the reference is coming from originally and what type constraint
it might have. Unless we actually keep track of which typed property a
reference is associated with (and maybe also where in the code it was
taken), you couldn't even know why you're getting a type error.
Now, for the most part, references in PHP are fairly explicit. You need to
include a & sigil during assignment and argument passing. You could argue
that, if you see that you're accepting an argument by reference, you just
have to take the possibility of it being typed into account. However, this
is simply not true. While references are mostly explicit, they aren't
always. If you create an array $array = [&$foo->bar] and then pass around
that array by value, $array[0] will still be a reference. This means that
an $array[0] = "xyz" assignment, in a piece of code that does not itself
use references in any way, can still end up with a type error.
Typed references are a ticking time bomb. They contagiously pervade through
the code and may go off at any point. Any point at all, even if it doesn't
visibly involve references.
Now, these are some of the "conceptual" issues. Even discounting them, we'd
still be up against a large purely implementational problem if we wanted to
support typed references. Your suggestion of using IS_TYPED_REF that just
checks if the type of the new value matches with that of the existing value
will fail in the presence of object types, as they are instanceof based. We
would have to store the desired type of the value. For debugging purposes
at least, we'd likely also store the class and property enforcing the
constraint. This constraint would need to be enforced in all places that
might possibly assign to a reference. For the places in the virtual
machine, we could manage. There's a limited (albeit large) number of places
where reference assignments can occur and we could add checks to them (even
though ensuring that existing code does not performance regress might be
non-trivial). But reference assignments happen not only in the engine. We
have a large body of extension code that assumes you can change a reference
just by writing into a zval. PHP 7 contained a large change to the way
references are handled, and we are still finding new places (even in
bundled extension code) that haven't been updated. Changing these kinds of
fundamental assumptions always has fallout.
As things stand now, you essentially have a choice between typed properties
without reference for PHP 7.1, or typed properties with references for PHP
9.0. I'd also like to note that it is always possible to add support for
references at a later point in time, should we find a good way of dealing
with them.
Thanks,
Nikita
Nikita Popov wrote on 21/03/2016 17:05:
While it is no secret that we don't particularly like references, this
is not the reason why they are forbidden. There is no conspiracy to
punish users of references by denying them use of new typing features.
No, the reasons here are technical. Let me illustrate some of the
issues involved in more detail.
Thanks for the detailed explanation of the problems, which are indeed
deeper than I'd appreciated. To be clear, I never thought it was the
intention to dissuade users from using references, but a couple of
people have implied that they consider it no bad thing.
For me, the restriction on setting references makes this whole feature a
no-go for the language as it stands. I don't think it is at all
reasonable for the following code to produce a TypeError:
class Sorter {
private array $data;
public function __construct(array $data) {
$this->data = $data;
sort($this->data);
}
}
new Sorter([1,3,6,5,2,4]);
It may be that we've reached the limit of how much typing we can add to
the language incrementally, and need actual typed variables, so that
references could be made only between variables with the same type hint,
e.g.
class A { public int $foo=42; }
$a = new A;
int $ref =& $a->foo;
The sort()
example would work in this scenario because the expected
parameter type is array, so the reference being created is of the
matching type.
Obviously, this would be a huge change to the language, and no doubt
have all sorts of impacts and problems of its own, but I don't think it
makes sense to say "you can typehint your property names, but you lose
some existing functionality if you do so, for technical reasons".
Regards,
Rowan Collins
[IMSoP]
Den 2016-03-22 kl. 14:15, skrev Rowan Collins:
Nikita Popov wrote on 21/03/2016 17:05:
While it is no secret that we don't particularly like references,
this is not the reason why they are forbidden. There is no conspiracy
to punish users of references by denying them use of new typing
features. No, the reasons here are technical. Let me illustrate some
of the issues involved in more detail.Thanks for the detailed explanation of the problems, which are indeed
deeper than I'd appreciated. To be clear, I never thought it was the
intention to dissuade users from using references, but a couple of
people have implied that they consider it no bad thing.For me, the restriction on setting references makes this whole feature
a no-go for the language as it stands. I don't think it is at all
reasonable for the following code to produce a TypeError:class Sorter {
private array $data;
public function __construct(array $data) {
$this->data = $data;
sort($this->data);
}
}
new Sorter([1,3,6,5,2,4]);It may be that we've reached the limit of how much typing we can add
to the language incrementally, and need actual typed variables, so
that references could be made only between variables with the same
type hint, e.g.class A { public int $foo=42; }
$a = new A;
int $ref =& $a->foo;The
sort()
example would work in this scenario because the expected
parameter type is array, so the reference being created is of the
matching type.Obviously, this would be a huge change to the language, and no doubt
have all sorts of impacts and problems of its own, but I don't think
it makes sense to say "you can typehint your property names, but you
lose some existing functionality if you do so, for technical reasons".Regards,
Hm... In Hack the above code works, see:
https://3v4l.org/nsA5W
Well they have a different implementation as mentioned in
the RFC, so no wonder. But they do have typed porperties in
the language.
Regards //Björn Larsson
Björn Larsson wrote on 22/03/2016 15:04:
Hm... In Hack the above code works, see:
https://3v4l.org/nsA5WWell they have a different implementation as mentioned in
the RFC, so no wonder. But they do have typed porperties in
the language.
It's not just a different implementation, it's a completely different
feature - there's no actual type checking at runtime, the type hint is
more like an annotation for use with static analysis tools. See
https://3v4l.org/ntCr7 which happily assigns int(6) to the "typed
property" in HHVM, but is detected by the RFC branch.
(Incidentally, I note the TypeError's message is missing the variable
name; I presume that's either a known issue or an out-of-date build on
3v4l?)
Regards,
Rowan Collins
[IMSoP]
Hi,
This mail turned out to look quite a lot like a rant - sorry about that. I
can assure you I am genuinely looking for answers on the questions that
follow ...
https://3v4l.org/Lq5dA/rfc#tabs (an example is from the RFC itself)
It doesn't make sense to me, for numerous reasons:
- Why are any magic methods called for public (and already declared)
properties, especially from inside the object's constructor? - Even if there's a sane reason for that first point, a __get() call may
not even attempt to access the property in question, so why is its return
value taken into account? - Why does unset($this->bar) call $this->__get('bar') and not
$this->__unset('bar'), like it happens for all properties currently? - Furthermore, why is unset($this->bar) valid on initialized properties,
but not on uninitialized ones?
I also still feel that completely preventing a typed property to "return
NULL" in any shape or form is a way too opinionated decision ...
I can understand disabling explicit NULL
assignments. And I don't
particularly like it, I also understand throwing TypeError for direct
access on an unitialized property:
class Foo {
public int $bar;
}
$foo = new Foo();
$this->bar = null;
if ($this->bar === null) {}
// ^ I agree, both of these SHOULD throw TypeError
However, I see no reason why this shouldn't work:
class Foo {
public int $bar = null;
}
$foo = new Foo();
is_null($foo->bar) && doSomething();
That's no longer a question of should uninitialized properties be
accessible; it's explicit - the developer writing this wants the property
to have NULL
at least as the default value.
With all the "consistency" arguments thrown against virtually all proposals
so far, why is this one inconsistent with typed parameters?
Sure, the RFC half-addresses this question by saying that it intentionally
doesn't touch on the subject of nullability because of the eventual union
types feature, but that feature is not even officially proposed yet.
Theoretically, it may not ever change its status from "Draft", let alone be
accepted, but even if so - I don't see it disallowing function foo(int $bar
= null) {}, so does it apply here?
And finally, and most importantly - throwing a TypeError even on an isset()
... that's not even a function call.
Some may prefer to think of isset() as if it was an inverted is_null()
, but
we all know it's not quite the same and it doesn't emit notices for
undefined variables. So why should it throw TypeError for properties? Why
can't we at least have a way to check if a property was ever initialized?
Cheers,
Andrey.
Hi again,
Apparently I misunderstood the parts of the RFC that I was most concerned
about (isset() and what causes magic method calls).
Joe clarified most of it for me elsewhere and so now I'll be answering my
own questions here, in case that helps anybody else:
Hi,
This mail turned out to look quite a lot like a rant - sorry about that. I
can assure you I am genuinely looking for answers on the questions that
follow ...https://3v4l.org/Lq5dA/rfc#tabs (an example is from the RFC itself)
It doesn't make sense to me, for numerous reasons:
- Why are any magic methods called for public (and already declared)
properties, especially from inside the object's constructor?- Even if there's a sane reason for that first point, a __get() call may
not even attempt to access the property in question, so why is its return
value taken into account?
Apparently, after you call unset() on any kind of property, magic methods
will be called the next time you try to access it - seems quite strange to
me, but not this RFC's fault, so ...
- Why does unset($this->bar) call $this->__get('bar') and not
$this->__unset('bar'), like it happens for all properties currently?
It doesn't.
The comment on line 7 makes it look like that's the case but actually tries
to say what I just did above.
- Furthermore, why is unset($this->bar) valid on initialized properties,
but not on uninitialized ones?
It's valid in both cases, I've misread the error message line.
I also still feel that completely preventing a typed property to "return
NULL" in any shape or form is a way too opinionated decision ...I can understand disabling explicit
NULL
assignments. And I don't
particularly like it, I also understand throwing TypeError for direct
access on an unitialized property:class Foo { public int $bar; } $foo = new Foo(); $this->bar = null; if ($this->bar === null) {} // ^ I agree, both of these SHOULD throw TypeError
However, I see no reason why this shouldn't work:
class Foo { public int $bar = null; } $foo = new Foo(); is_null($foo->bar) && doSomething();
That's no longer a question of should uninitialized properties be
accessible; it's explicit - the developer writing this wants the property
to haveNULL
at least as the default value.
With all the "consistency" arguments thrown against virtually all
proposals so far, why is this one inconsistent with typed parameters?
Sure, the RFC half-addresses this question by saying that it intentionally
doesn't touch on the subject of nullability because of the eventual union
types feature, but that feature is not even officially proposed yet.
Theoretically, it may not ever change its status from "Draft", let alone be
accepted, but even if so - I don't see it disallowing function foo(int $bar
= null) {}, so does it apply here?
I still disagree with this, but it seems like I'm by far in the minority.
And finally, and most importantly - throwing a TypeError even on an
isset() ... that's not even a function call.
Some may prefer to think of isset() as if it was an invertedis_null()
,
but we all know it's not quite the same and it doesn't emit notices for
undefined variables. So why should it throw TypeError for properties? Why
can't we at least have a way to check if a property was ever initialized?
I've misread the error message line for this one too, which is good,
because I was most concerned about being able to do an isset() :)
Sorry for the noise.
Cheers,
Andrey.
I'd like to thank everyone for their feedback on this RFC!
It looks like the majority of concerns were solved during the course
of this discussion, which is great news.
The RFC has been expanded in a few areas to take care of a few other
concerns, so please go and review it and let me know if you have any
feedback.
https://wiki.php.net/rfc/typed-properties
Voting will start in a few days.
Cheers,
Phil
Den 2016-03-29 kl. 17:32, skrev Phil Sturgeon:
I'd like to thank everyone for their feedback on this RFC!
It looks like the majority of concerns were solved during the course
of this discussion, which is great news.The RFC has been expanded in a few areas to take care of a few other
concerns, so please go and review it and let me know if you have any
feedback.https://wiki.php.net/rfc/typed-properties
Voting will start in a few days.
Cheers,
Phil
A question. Should the visibility keyword public be optional
like it is for methods and static properties?
Regards //Björn
On Tue, Mar 29, 2016 at 2:58 PM, Björn Larsson
bjorn.x.larsson@telia.com wrote:
Den 2016-03-29 kl. 17:32, skrev Phil Sturgeon:
I'd like to thank everyone for their feedback on this RFC!
It looks like the majority of concerns were solved during the course
of this discussion, which is great news.The RFC has been expanded in a few areas to take care of a few other
concerns, so please go and review it and let me know if you have any
feedback.https://wiki.php.net/rfc/typed-properties
Voting will start in a few days.
Cheers,
PhilA question. Should the visibility keyword public be optional
like it is for methods and static properties?Regards //Björn
Hey Björn, this RFC has literally zero impact on visibility keywords.
You can use public, private, protected, you can omit it entirely or
you can use var if you like. It makes no difference.
Den 2016-03-29 kl. 22:38, skrev Phil Sturgeon:
On Tue, Mar 29, 2016 at 2:58 PM, Björn Larsson
bjorn.x.larsson@telia.com wrote:Den 2016-03-29 kl. 17:32, skrev Phil Sturgeon:
I'd like to thank everyone for their feedback on this RFC!
It looks like the majority of concerns were solved during the course
of this discussion, which is great news.The RFC has been expanded in a few areas to take care of a few other
concerns, so please go and review it and let me know if you have any
feedback.https://wiki.php.net/rfc/typed-properties
Voting will start in a few days.
Cheers,
PhilA question. Should the visibility keyword public be optional
like it is for methods and static properties?Regards //Björn
Hey Björn, this RFC has literally zero impact on visibility keywords.
You can use public, private, protected, you can omit it entirely or
you can use var if you like. It makes no difference.
I tried omitting public keyword from class declaration:
class A {
public int $i = 3;
}
on https://3v4l.org/OFr2a/rfc#tabs and got error message.
Anyway, appreciating the good work, both of you are doing
in moving this proposal forward.
Regards //Björn
On Wed, Mar 16, 2016 at 1:39 PM, Johannes Schlüter
johannes@schlueters.de wrote:
Hello everyone,
I have completed the draft for an RFC, to add Typed Properties. The
patch has been written by the one and only Joe Watkins.I'm no fan of all this typing business, but that's a lost case. Anyways:
What about references? (yeah, references should die, but currently they
still exist)What's expected here:
class A {
public int $i;
}$a = new A();
$r = &$a->i;
$r = "foobar";johannes
Hey there!
I put your code into 3v4l so you could see what would happen:
Fatal error: Uncaught TypeError: Typed property A::$i must not be referenced
Is that what you expect? If not, what would you expect?
What's expected here:
class A {
public int $i;
}$a = new A();
$r = &$a->i;
$r = "foobar";
I put your code into 3v4l so you could see what would happen:
Fatal error: Uncaught TypeError: Typed property A::$i must not be referenced
Is that what you expect? If not, what would you expect?
Well, I think a naïve user would expect to get an error about "foobar"
not being an integer and would expect $r = 42; to work.
Maybe the other example from the discussion with Bob might be more clear
about the issue:
class A {
private array $data = [42, 23];
public function sort()
{
sort($this->data);
print_r($this->data);
}
}
$a = new A();
$a->sort();
An "Uncaught TypeError" is completely unexpected there.
btw. while testing this it seems the patch is broken:
https://3v4l.org/sOBca/rfc#tabs - this gives a strange error ("A::$data
must be array, unknown used") in the print_r call!?
https://3v4l.org/kl5e2/rfc#tabs - this changes the type
johannes
Hi all,
On Thu, Mar 17, 2016 at 8:30 AM, Johannes Schlüter
johannes@schlueters.de wrote:
What's expected here:
class A {
public int $i;
}$a = new A();
$r = &$a->i;
$r = "foobar";I put your code into 3v4l so you could see what would happen:
Fatal error: Uncaught TypeError: Typed property A::$i must not be referenced
Is that what you expect? If not, what would you expect?
I expect work like parameter type hint.
[yohgaki@dev github-php-src]$ ./php-bin -a
Interactive shell
php > declare(strict_types=1);
php > function foo(int $i) { $i='abc'; var_dump($i); }
php > foo(123);
string(3) "abc"
PHP is not strongly typed language (at least now).
Regards,
--
Yasuo Ohgaki
yohgaki@ohgaki.net
Hello everyone,
I have completed the draft for an RFC, to add Typed Properties. The
patch has been written by the one and only Joe Watkins.
Overall thought: <3
I would really appreciate constructive feedback on this RFC, with a
few areas especially:
- How scared are we that integers can be expanded to floats on runtime?
As others have said, follow the existing widening rules. int->float is
safe for strict params and returns, so should be safe here for the same
reason. Inconsistency here would suck.
- This whole temporary nullability situation, where unset properties
will error on attempted usage if not set. Should they instead error
after the constructor has been called if they are still not holding a
value?
I fall back to a statement I made in a blog post a while back:
http://www.garfieldtech.com/blog/empty-return-values
" But consider what you could do instead that would not force me to
throw is_null()
around my code, because throwing is_null()
around my
code makes me sad. NULL
should be the return value of last resort,
because it means nothing. (Literally.)"
Allowing default-null on properties means that as someone using that
property, I have two options:
- Throw a lot of
is_null()
calls around my code. - Assume that whoever initialized the code provided a value by the time
initialization is done and skip those extra checks.
Insert that old adage about what happens when you assume.
End-of-constructor checks seem like a good approach; they have to be
uninitialized at some point when new is called initially, but they
should be guaranteed set as soon as possible. End of the constructor is
"as soon as possible", and I think reasonably
static-analysis-catchable. (Meaning my IDE can yell at me appropriately.)
That removes is_null()
calls from the rest of my codebase, which is a
good thing.
- Weak vs Strict. Right now this is entirely strict, with no
declare() to change mode. Reasons for this vary, from various sources,
but include "Not sure how to implement it" and "Well people should not
be using properties as part of their public API".
I cannot help on the implementation front, but logically it doesn't make
sense to me as an end-user that properties are always-strict while
params and returns are not. As with integer->float widening,
consistency should be the default position. If that means a slight
performance hit in weak mode (does it?), well, that's one more reason
people should be using strict mode. :-)
Regarding the void type (as noted in the Open Issues section): A void
type on a property renders that property useless. You couldn't ever
assign to it, and it would only ever evaluate to NULL
at best. So... do
whatever is easiest, implementation-wise. If it's technically possible
to define a property as void, meh, I don't much care since it's useless
anyway. If it's easy enough to forbid, forbid. If it's easier to make
it legal syntax but optimize out of the class structure on compile,
that's good too.
Syntactically, what happens if you omit the visibility keyword, or use
var? (Assuming var survives.) Ie, is this legal:
class Foo {
int $a;
}
And/or, since most other pre-keywords are of flexible position would
this be legal:
class Foo {
int public $a;
}
Like Adam, I'd also be interested in the implications for the property
getter/setter RFC as I would love for that to come back at some point.
(Out of scope for this RFC, certainly, but making sure it won't break
anything in that direction later would be good.)
--
--Larry Garfield
On Wed, Mar 16, 2016 at 12:50 PM, Larry Garfield larry@garfieldtech.com
wrote:
- This whole temporary nullability situation, where unset properties
will error on attempted usage if not set. Should they instead error
after the constructor has been called if they are still not holding a
value?I fall back to a statement I made in a blog post a while back:
http://www.garfieldtech.com/blog/empty-return-values
" But consider what you could do instead that would not force me to throw
is_null()
around my code, because throwingis_null()
around my code makes
me sad.NULL
should be the return value of last resort, because it means
nothing. (Literally.)"Allowing default-null on properties means that as someone using that
property, I have two options:
- Throw a lot of
is_null()
calls around my code.- Assume that whoever initialized the code provided a value by the time
initialization is done and skip those extra checks.Insert that old adage about what happens when you assume.
End-of-constructor checks seem like a good approach; they have to be
uninitialized at some point when new is called initially, but they should
be guaranteed set as soon as possible. End of the constructor is "as soon
as possible", and I think reasonably static-analysis-catchable. (Meaning
my IDE can yell at me appropriately.)That removes
is_null()
calls from the rest of my codebase, which is a good
thing.
I'm not sure I like the "after contructor" because that's not necessarily
when the object is finished being initialized. Consider this:
abstract class Contract
{
private function __construct() {}
public static function fromArray(array $aProps) : Contract
{
$s = new static();
foreach ($aProps as $k => $v) {
if (property_exists(static::class, $k)) {
$s->$k = $v;
}
}
return $s;
}
}
class User extends Contract {
public int $id;
public string $name;
}
$user = User::fromArray(['id' => 123, 'name' => 'derokorian']);
In this example, the constructor finishes but the properties still are not
initialized at time of finishing. This is a pretty common pattern as well.
Hi,
On Wed, Mar 16, 2016 at 12:50 PM, Larry Garfield larry@garfieldtech.com
wrote:
- This whole temporary nullability situation, where unset properties
will error on attempted usage if not set. Should they instead error
after the constructor has been called if they are still not holding a
value?I fall back to a statement I made in a blog post a while back:
http://www.garfieldtech.com/blog/empty-return-values
" But consider what you could do instead that would not force me to throw
is_null()
around my code, because throwingis_null()
around my code makes
me sad.NULL
should be the return value of last resort, because it means
nothing. (Literally.)"Allowing default-null on properties means that as someone using that
property, I have two options:
- Throw a lot of
is_null()
calls around my code.- Assume that whoever initialized the code provided a value by the time
initialization is done and skip those extra checks.Insert that old adage about what happens when you assume.
End-of-constructor checks seem like a good approach; they have to be
uninitialized at some point when new is called initially, but they should
be guaranteed set as soon as possible. End of the constructor is "as
soon
as possible", and I think reasonably static-analysis-catchable. (Meaning
my IDE can yell at me appropriately.)That removes
is_null()
calls from the rest of my codebase, which is a
good
thing.I'm not sure I like the "after contructor" because that's not necessarily
when the object is finished being initialized. Consider this:abstract class Contract
{
private function __construct() {}public static function fromArray(array $aProps) : Contract { $s = new static(); foreach ($aProps as $k => $v) { if (property_exists(static::class, $k)) { $s->$k = $v; } } return $s; }
}
class User extends Contract {
public int $id;
public string $name;
}$user = User::fromArray(['id' => 123, 'name' => 'derokorian']);
In this example, the constructor finishes but the properties still are not
initialized at time of finishing. This is a pretty common pattern as well.
Also, a LOT of code would end up with e.g. a default value of 0 for an
integer property, still having to check if it was properly populated with a
positive value - not much of an improvement over NULLs.
Cheers,
Andrey.
Also, a LOT of code would end up with e.g. a default value of 0 for an
integer property, still having to check if it was properly populated
with a positive value - not much of an improvement over NULLs. Cheers,
Andrey.
That's fine. Type checking doesn't say that a value is correct or
meaningful; it just enforces the shape of the data.
Vis, type enforcement tells you that the property is an integer; it
doesn't tell you that the integer is within the range of legal values
for the temperature of a room. It tells you that a property is a
string; it doesn't tell you that the string doesn't contain NSFW words.
Those are all application-level concerns, not type-level concerns, no
matter what we do here. (Just like they are for parameters and returns
already.)
IMO, int/string/float is only a big deal on properties if you're using
reflection for DB schema generation or similar, as it lets you remove an
annotation. (That's a useful benefit, though.) The bigger impact for
me is for class names, so I can enforce a given object type on a certain
property. Knowing "at least it's not NULL" has a much larger benefit in
that case, as you avoid many "method called on non-object 100 execution
lines later than the actual bug, good luck finding it" errors. :-)
--
--Larry Garfield
On Thu, Mar 17, 2016 at 12:00 AM, Larry Garfield larry@garfieldtech.com
wrote:
Also, a LOT of code would end up with e.g. a default value of 0 for an
integer property, still having to check if it was properly populated with a
positive value - not much of an improvement over NULLs. Cheers, Andrey.That's fine. Type checking doesn't say that a value is correct or
meaningful; it just enforces the shape of the data.Vis, type enforcement tells you that the property is an integer; it
doesn't tell you that the integer is within the range of legal values for
the temperature of a room. It tells you that a property is a string; it
doesn't tell you that the string doesn't contain NSFW words. Those are all
application-level concerns, not type-level concerns, no matter what we do
here. (Just like they are for parameters and returns already.)IMO, int/string/float is only a big deal on properties if you're using
reflection for DB schema generation or similar, as it lets you remove an
annotation. (That's a useful benefit, though.) The bigger impact for me
is for class names, so I can enforce a given object type on a certain
property. Knowing "at least it's not NULL" has a much larger benefit in
that case, as you avoid many "method called on non-object 100 execution
lines later than the actual bug, good luck finding it" errors. :-)
Yet, your argument was about avoiding is_null()
calls within application
logic and you'd gain nothing if you end up having to write ! empty()
instead. :)
I'd rather have a NULL
to tell me that I have nothing instead of a
supposedly initialized but otherwise invalid value.
Cheers,
Andrey.
On Thu, Mar 17, 2016 at 12:00 AM, Larry Garfield larry@garfieldtech.com
wrote:Also, a LOT of code would end up with e.g. a default value of 0 for an
integer property, still having to check if it was properly populated with a
positive value - not much of an improvement over NULLs. Cheers, Andrey.That's fine. Type checking doesn't say that a value is correct or
meaningful; it just enforces the shape of the data.Vis, type enforcement tells you that the property is an integer; it
doesn't tell you that the integer is within the range of legal values for
the temperature of a room. It tells you that a property is a string; it
doesn't tell you that the string doesn't contain NSFW words. Those are all
application-level concerns, not type-level concerns, no matter what we do
here. (Just like they are for parameters and returns already.)IMO, int/string/float is only a big deal on properties if you're using
reflection for DB schema generation or similar, as it lets you remove an
annotation. (That's a useful benefit, though.) The bigger impact for me
is for class names, so I can enforce a given object type on a certain
property. Knowing "at least it's not NULL" has a much larger benefit in
that case, as you avoid many "method called on non-object 100 execution
lines later than the actual bug, good luck finding it" errors. :-)Yet, your argument was about avoiding
is_null()
calls within application
logic and you'd gain nothing if you end up having to write ! empty()
instead. :)
I'd rather have aNULL
to tell me that I have nothing instead of a
supposedly initialized but otherwise invalid value.Cheers,
Andrey.
Not sure what you're saying there Andrey.
That's fine. Type checking doesn't say that a value is correct or meaningful; it just enforces the shape of the data.
Larry
Solid points! Exactly that. A type relevant default of 0 is not
definitively invalid, it's up to application logic to work that out. 0
is totally valid as an integer.
I do quite like the idea of type relevant defaults being provided, so
0, [], "", etc, just like Go.
The only trouble I see there is: what sane default is given for
public Foo $foo;
? Instantiate the class? Erf.
On Thu, Mar 17, 2016 at 12:23 AM, Phil Sturgeon pjsturgeon@gmail.com
wrote:
Yet, your argument was about avoiding
is_null()
calls within application
logic and you'd gain nothing if you end up having to write ! empty()
instead. :)
I'd rather have aNULL
to tell me that I have nothing instead of a
supposedly initialized but otherwise invalid value.Cheers,
Andrey.Not sure what you're saying there Andrey.
That's fine. Type checking doesn't say that a value is correct or
meaningful; it just enforces the shape of the data.
LarrySolid points! Exactly that. A type relevant default of 0 is not
definitively invalid, it's up to application logic to work that out. 0
is totally valid as an integer.I do quite like the idea of type relevant defaults being provided, so
0, [], "", etc, just like Go.The only trouble I see there is: what sane default is given for
public Foo $foo;
? Instantiate the class? Erf.
I guess I'm not being verbose enough then ... I'm not really good at that.
Given this class as an example:
abstract class Foo {
public int $bar;
}
We're discussing whether the $bar property should be allowed to have a NULL
value after instantiation, right?
Larry's point being that it can't be NULL, so that we don't have to do
is_null()
checks throughout the code. And I understand that.
However:
- You'd only need to do the
is_null()
/isset() calls if you're not sure
whether the property was populated with a meaningful value (i.e. if you
constructor doesn't populate it) - A meaningful value is one that is valid for the application logic,
because that same logic defines its ... meaning - If you absolutely forbid typed properties to hold
NULL
values, you'd
force users to resort to using int(0), float(0.0), string('') as the
defaults for properties that cannot be populated at instantiation time for
whatever reason
(and you know people WILL do anything as long as it hides an error message)
I'm not saying that these defaults are definitely invalid (and as far as
typing goes - sure, the type is correct), but they will be meaningless in
a lot of cases.
So now you still have to check if the property was really populated or not,
only using a function different than is_null()
.
I'd say that's inventing a new problem in order to solve the old one. We
don't want that, do we? :)
Cheers,
Andrey.
Awesome feature thanks for the RFC. :)
- How scared are we that integers can be expanded to floats on runtime?
This is already what happens in PHP all the time and I personally only
see harm if we change that. People are already not understand what the
difference between the two is and someone who is serious about this
topic is taking care of it on her/his own (many thanks to intdiv) or
is using strings anyways (bcmath/gmp).
- This whole temporary nullability situation, where unset properties
will error on attempted usage if not set. Should they instead error
after the constructor has been called if they are still not holding a
value?
I see a big problem with the erroring no matter when as others already
pointed out, e.g. together with named constructors (or factory methods
if you prefer that name) but also with lazy loading. I think that the
null value of properties during and after construction should simply be
ignored and left to the implementer. I know that it would be nice to
have 100% assurance that the property is always set but it would simply
result in rules that are too strict for various use cases. I mean,
aren't we trying to solve a problem that never was a problem here?
Another more complicated user case would be mysqli_fetch_object that
populates the properties with values from a storage but values that
should become something specific and strict at some point but are
initially populated as strings. Type coercion would be a nice thing here
but with strict checks afterwards. Note that this cannot be handled by
the extension due to user types:
final class Bar {
private string $data;
public function __construct(string $data) {
$this->data = $data;
}
}
final class Foo {
private Bar $bar;
private function __construct() {
if (isset($this->bar)) {
$this->bar = new Bar($bar);
}
}
}
$mysqli_result->fetch_object(Foo::class);
It is correctly populated with a string and the constructor changes it
to the desired instance. All fine, but the strict type check would kill
everything here.
I think that the strict type checks should start with the first
userland assignment and at no other point in time. The correct
initialization of an object is up to the implementer as it always was.
[...] how about some syntax which allows for a property to be null,
or for it to be a specific type. I'd suggest a null assignment to
fall in line with parameter type hints eg:class Foo {
protected int $bar = null; //can be null
private stdClass $baz; //can't be null
}
This aligns nicely with the syntax that is present in PHP for arguments
since ages and it directly could solve the Nullable problem mentioned in
future scope. An example to illustrate how nicely it aligns with
existing PHP 5+ syntax:
final class Foo {
private int $bar = null;
public function setBar(int $bar = null): void {
$this->bar = $bar;
}
}
(new Foo)->setBar(42);
(new Foo)->setBar(null);
Additionally the extreme verbosity is super nice.
- Weak vs Strict. Right now this is entirely strict, with no
declare() to change mode. Reasons for this vary, from various sources,
but include "Not sure how to implement it" and "Well people should not
be using properties as part of their public API".
An ini setting to control type checks and hints would be a nice thing to
have. Like the checked mode of Google's Dart language. Such a setting
could and should also allow to disable type checks altogether, like we
have it with assertions:
strict_types = -1 ; NOOP
strict_types = 0 ; PHP 5 Style
strict_types = 1 ; PHP 7 Coercion Style
strict_types = 2 ; PHP 7 Strict Style
Where 1 would be the default since the default should be for development
and people who hate strict types just throw that zero in their ini and
are fine to reuse any code out there while ignoring all property/scalar
type checks/hints.
This would make the declare
thingy pretty useless and I think it is
easier to understand. I thought that this approach was kind of weird
since its inception.
What about references? (yeah, references should die, but currently
they still exist)What's expected here:
class A {
public int $i;
}$a = new A();
$r = &$a->i;
$r = "foobar";
I think this missing functionality is a blocker right now. Either we
have a plan to get rid of references altogether as Johannes mentions it
or the typed properties need support for it.
Note that the following currently works with the patch, which might lead
to more ranting in regards to missing/overlooked functionality if the
properties on their own do not support it.
declare(strict_types=1);
class A {
private int $x = 0;
public function &x(): int {
return $this->x;
}
}
$a = new A;
$x =& $a->x();
$x = 42;
echo $a->x(); // 42
--
Richard "Fleshgrinder" Fussenegger
Hi all,
Am 16.03.2016 um 21:29 schrieb Fleshgrinder:
Another more complicated user case would be mysqli_fetch_object that
populates the properties with values from a storage but values that
should become something specific and strict at some point but are
initially populated as strings. Type coercion would be a nice thing here
but with strict checks afterwards.
I really don't like the special way mysqli and PDO use here.
Both should use the __set_state() magic method as would any userland code.
But that is a discussion for another thread ;-)
Greets
Dennis
Hi all,
Am 16.03.2016 um 21:29 schrieb Fleshgrinder:
Another more complicated user case would be mysqli_fetch_object that
populates the properties with values from a storage but values that
should become something specific and strict at some point but are
initially populated as strings. Type coercion would be a nice thing here
but with strict checks afterwards.I really don't like the special way mysqli and PDO use here.
Both should use the __set_state() magic method as would any userland code.
But that is a discussion for another thread ;-)Greets
Dennis
I think the discussion is important here because the introduction of
typed properties as proposed would break exactly this functionality of a
few of the most used extensions of all.
That being said, I agree and disagree at the same time with you on
__set_state. I also would prefer it if these methods would call
another method because they always require a check:
final class A {
private $x;
private $y;
private function __construct(O $x = null, O $y = null) {
// mysqli_result::fetch_object
// PDOStatement::fetchObject
if (isset($this->x)) {
$this->x = new O($this->x);
$this->y = new O($this->y);
}
else {
assert(isset($x));
assert(isset($y));
$this->x = $x;
$this->y = $y;
}
// Some assertions on $x and $y ...
}
public static function new($x, $y, ErrorNotifier $n) {
// Expensive validation ...
return new static($x, $y);
}
public static function __set_state(array $data) {
return new static($data['x'], $data['y']);
}
}
Of course there are other ways to implement this. I just want to
illustrate that it is not an ideal situation if we want to keep our code
as DRY as possible while using automated type checks without assertions.
But back to the __set_state problem. __set_state expects that the
members of the passed array are fully initialized because it is the
inverse of var_export. The data that is provided by
mysqli_result::fetch_object and PDOStatement::fetchObject are not
fully initialized so they are not good candidates to use __set_state.
Using another magic method would solve the riddle:
final class A {
private $x;
private $y;
private function __construct(O $x, O $y) {
// Some assertions on $x and $y ...
$this->x = $x;
$this->y = $y;
}
public static function new($x, $y, ErrorNotifier $n) {
// Expensive validation ...
return new static($x, $y);
}
// mysqli_result::fetch_object
// PDOStatement::fetchObject
public static function __init_state(array $data) {
return new static(new O($data['x']), new O($data['y']));
}
public static function __set_state(array $data) {
return new static($data['x'], $data['y']);
}
}
Both examples might be bad but they are everything I just could come up
with.
TL;DR __set_state is not the correct method in this case because it is
the inverse of var_export and expects all members to be fully
initialized what is definitely not the case for both
mysqli_result::fetch_object and PDOStatement::fetchObject.
--
Richard "Fleshgrinder" Fussenegger
Hi,
Fleshgrinder wrote:
- This whole temporary nullability situation, where unset properties
will error on attempted usage if not set. Should they instead error
after the constructor has been called if they are still not holding a
value?I see a big problem with the erroring no matter when as others already
pointed out, e.g. together with named constructors (or factory methods
if you prefer that name) but also with lazy loading. I think that the
null value of properties during and after construction should simply be
ignored and left to the implementer. I know that it would be nice to
have 100% assurance that the property is always set but it would simply
result in rules that are too strict for various use cases. I mean,
aren't we trying to solve a problem that never was a problem here?
This is actually a problem for optimising PHP. If the compiler cannot be
sure a property's value actually matches its declared type, it can't
optimise operations on that property to remove a type check.
I also think it would be unintuitive if typed properties had some
exception where they don't obey the given type. If I ask for an integer,
then surely I should get an integer?
Another more complicated user case would be mysqli_fetch_object that
populates the properties with values from a storage but values that
should become something specific and strict at some point but are
initially populated as strings. Type coercion would be a nice thing here
but with strict checks afterwards. Note that this cannot be handled by
the extension due to user types:final class Bar { private string $data; public function __construct(string $data) { $this->data = $data; } } final class Foo { private Bar $bar; private function __construct() { if (isset($this->bar)) { $this->bar = new Bar($bar); } } } $mysqli_result->fetch_object(Foo::class);
It is correctly populated with a string and the constructor changes it
to the desired instance. All fine, but the strict type check would kill
everything here.I think that the strict type checks should start with the first
userland assignment and at no other point in time. The correct
initialization of an object is up to the implementer as it always was.
This adds a loophole to property type checks which, as previously
mentioned, prevents us from performing optimisations. I also don't see
why internal functions should be treated specially here, given that
surely one could write userland code with the same needs as MySQLi? It
doesn't seem intuitive.
[...] how about some syntax which allows for a property to be null,
or for it to be a specific type. I'd suggest a null assignment to
fall in line with parameter type hints eg:class Foo {
protected int $bar = null; //can be null
private stdClass $baz; //can't be null
}This aligns nicely with the syntax that is present in PHP for arguments
since ages and it directly could solve the Nullable problem mentioned in
future scope.
We could certainly do this, but I'm not sure we should. Like for
arguments, this would mean that your default value must be null, and you
don't have to assign to it. But what if you want a different default
value? What if you want to require it be assigned to?
Adding a syntax form for nullable types (e.g. ?int
or int|null
)
would avoid these issues, and also solve the nullable return type issue.
- Weak vs Strict. Right now this is entirely strict, with no
declare() to change mode. Reasons for this vary, from various sources,
but include "Not sure how to implement it" and "Well people should not
be using properties as part of their public API".An ini setting to control type checks and hints would be a nice thing to
have.
An INI setting to control type checks is impractical, because it would
affect all PHP code running in a given PHP interpreter. This means you
control not only the type checking mode of your own code, but also of
all the libraries you're using. Unless you never use other people's
code, using this INI setting would just break things.
Like the checked mode of Google's Dart language. Such a setting
could and should also allow to disable type checks altogether, like we
have it with assertions:strict_types = -1 ; NOOP strict_types = 0 ; PHP 5 Style strict_types = 1 ; PHP 7 Coercion Style strict_types = 2 ; PHP 7 Strict Style
Why would you want to disable type checks altogether? There might be a
small performance improvement, but you've removed important error
checking. What if something goes wrong at runtime?
Where 1 would be the default since the default should be for development
and people who hate strict types just throw that zero in their ini and
are fine to reuse any code out there while ignoring all property/scalar
type checks/hints.
This solves a problem which I don't believe exists. The system PHP 7 has
means that if you don't want to use strict types, they never affect you.
This would make the
declare
thingy pretty useless and I think it is
easier to understand. I thought that this approach was kind of weird
since its inception.
Yes, the declare() approach is unusual compared to other settings.
That's because it was designed so the choices your code makes doesn't
affect other code which uses it. In the Composer era, this matters,
because a lot of code your interpreter is running will not be written or
maintained by you.
Thanks.
--
Andrea Faulds
https://ajf.me/
Hi,
Fleshgrinder wrote:
I see a big problem with the erroring no matter when as others already
pointed out, e.g. together with named constructors (or factory methods
if you prefer that name) but also with lazy loading. I think that the
null value of properties during and after construction should simply be
ignored and left to the implementer. I know that it would be nice to
have 100% assurance that the property is always set but it would simply
result in rules that are too strict for various use cases. I mean,
aren't we trying to solve a problem that never was a problem here?This is actually a problem for optimising PHP. If the compiler cannot be
sure a property's value actually matches its declared type, it can't
optimise operations on that property to remove a type check.
Can we optimize operations or is this theoretical?
I also think it would be unintuitive if typed properties had some
exception where they don't obey the given type. If I ask for an integer,
then surely I should get an integer?
Of course but there is some void time until an object is fully
initialized. We've seen many examples now in this thread.
Another more complicated user case would be mysqli_fetch_object that
populates the properties with values from a storage but values that
should become something specific and strict at some point but are
initially populated as strings. Type coercion would be a nice thing here
but with strict checks afterwards. Note that this cannot be handled by
the extension due to user types:final class Bar { private string $data; public function __construct(string $data) { $this->data = $data; } } final class Foo { private Bar $bar; private function __construct() { if (isset($this->bar)) { $this->bar = new Bar($bar); } } } $mysqli_result->fetch_object(Foo::class);
It is correctly populated with a string and the constructor changes it
to the desired instance. All fine, but the strict type check would kill
everything here.I think that the strict type checks should start with the first
userland assignment and at no other point in time. The correct
initialization of an object is up to the implementer as it always was.This adds a loophole to property type checks which, as previously
mentioned, prevents us from performing optimisations. I also don't see
why internal functions should be treated specially here, given that
surely one could write userland code with the same needs as MySQLi? It
doesn't seem intuitive.
It is just code that exists and is used and I am trying to find a
solution for it together with this new feature. I already wrote that I
would highly prefer that mysqli_result::fetch_object and
PDOStatement::fetchObject work differently but I am not the maintainer
and I cannot tell if they are willing to change their behavior.
The auto-mapping these methods provide is in general an extremely nice
thing to have. Plus it is much faster than a comparable userland
solution; which is of course implementable.
[...] how about some syntax which allows for a property to be null,
or for it to be a specific type. I'd suggest a null assignment to
fall in line with parameter type hints eg:class Foo {
protected int $bar = null; //can be null
private stdClass $baz; //can't be null
}This aligns nicely with the syntax that is present in PHP for arguments
since ages and it directly could solve the Nullable problem mentioned in
future scope.We could certainly do this, but I'm not sure we should. Like for
arguments, this would mean that your default value must be null, and you
don't have to assign to it. But what if you want a different default
value? What if you want to require it be assigned to?Adding a syntax form for nullable types (e.g.
?int
orint|null
)
would avoid these issues, and also solve the nullable return type issue.
You are absolutely right I did not think about that. But currently it
seems as if nobody (including me) wants nullable types anyways.
- Weak vs Strict. Right now this is entirely strict, with no
declare() to change mode. Reasons for this vary, from various sources,
but include "Not sure how to implement it" and "Well people should not
be using properties as part of their public API".An ini setting to control type checks and hints would be a nice thing to
have.An INI setting to control type checks is impractical, because it would
affect all PHP code running in a given PHP interpreter. This means you
control not only the type checking mode of your own code, but also of
all the libraries you're using. Unless you never use other people's
code, using this INI setting would just break things.Like the checked mode of Google's Dart language. Such a setting
could and should also allow to disable type checks altogether, like we
have it with assertions:strict_types = -1 ; NOOP strict_types = 0 ; PHP 5 Style strict_types = 1 ; PHP 7 Coercion Style strict_types = 2 ; PHP 7 Strict Style
Why would you want to disable type checks altogether? There might be a
small performance improvement, but you've removed important error
checking. What if something goes wrong at runtime?Where 1 would be the default since the default should be for development
This should have been 2.
and people who hate strict types just throw that zero in their ini and
are fine to reuse any code out there while ignoring all property/scalar
type checks/hints.This solves a problem which I don't believe exists. The system PHP 7 has
means that if you don't want to use strict types, they never affect you.This would make the
declare
thingy pretty useless and I think it is
easier to understand. I thought that this approach was kind of weird
since its inception.Yes, the declare() approach is unusual compared to other settings.
That's because it was designed so the choices your code makes doesn't
affect other code which uses it. In the Composer era, this matters,
because a lot of code your interpreter is running will not be written or
maintained by you.Thanks.
I just though its nice to have the setting without having a strong
opinion about it.
--
Richard "Fleshgrinder" Fussenegger
This adds a loophole to property type checks which, as previously
mentioned, prevents us from performing optimisations. I also don't see
why internal functions should be treated specially here, given that
surely one could write userland code with the same needs as MySQLi? It
doesn't seem intuitive.It is just code that exists and is used and I am trying to find a
solution for it together with this new feature. I already wrote that I
would highly prefer that mysqli_result::fetch_object and
PDOStatement::fetchObject work differently but I am not the maintainer
and I cannot tell if they are willing to change their behavior.The auto-mapping these methods provide is in general an extremely nice
thing to have. Plus it is much faster than a comparable userland
solution; which is of course implementable.
Just wanted to add that this is only true for a userland implementation
that does the same as these two methods do because such an
implementation would require reflection.
Creating a O::fromArray as it is best practice and feeding it with a
dictionary is faster and much less magic.
--
Richard "Fleshgrinder" Fussenegger
Hi again,
Fleshgrinder wrote:
Fleshgrinder wrote:
I see a big problem with the erroring no matter when as others already
pointed out, e.g. together with named constructors (or factory methods
if you prefer that name) but also with lazy loading. I think that the
null value of properties during and after construction should simply be
ignored and left to the implementer. I know that it would be nice to
have 100% assurance that the property is always set but it would simply
result in rules that are too strict for various use cases. I mean,
aren't we trying to solve a problem that never was a problem here?This is actually a problem for optimising PHP. If the compiler cannot be
sure a property's value actually matches its declared type, it can't
optimise operations on that property to remove a type check.Can we optimize operations or is this theoretical?
We can do it, in fact as of a week ago PHP 7.1 (or rather the master
branch of php-src) has type-specialised opcodes:
https://github.com/php/php-src/pull/1825
I haven't looked at Optimizer's type-inference code, but I don't think
it currently applies to properties. It would be great if we could extend
it to them using the information typed properties provide us, though.
I also think it would be unintuitive if typed properties had some
exception where they don't obey the given type. If I ask for an integer,
then surely I should get an integer?Of course but there is some void time until an object is fully
initialized. We've seen many examples now in this thread.
That still complicates things. If you need to convert types, I would
suggest using something akin to __wakeup or some method to import data
from an array. Then you could handle type conversion yourself before
assigining to properties.
Thanks.
Andrea Faulds
https://ajf.me/
Den 2016-03-16 kl. 17:36, skrev Phil Sturgeon:
Hello everyone,
I have completed the draft for an RFC, to add Typed Properties. The
patch has been written by the one and only Joe Watkins.https://wiki.php.net/rfc/typed-properties
I would really appreciate constructive feedback on this RFC, with a
few areas especially:
How scared are we that integers can be expanded to floats on runtime?
This whole temporary nullability situation, where unset properties
will error on attempted usage if not set. Should they instead error
after the constructor has been called if they are still not holding a
value?Weak vs Strict. Right now this is entirely strict, with no
declare() to change mode. Reasons for this vary, from various sources,
but include "Not sure how to implement it" and "Well people should not
be using properties as part of their public API".Help on 3 would be appreciated.
Also let's please avoid "PHP IS TURNING INTO JAVA" and the other
rather common rhetoric. Strict Type Hinting might have been seen as a
battleground for fans of strict and fans of weak to fight through a
keyboard, but this RFC will not be the repeat.We'll have a nice, orderly, constructive conversation about this RFC,
and improve the patch as you all provide feedback.Let me know what you think folks!
Like the RFC :)
Could this be an opportunity to have default visibility for property's when
they are typed in a similar fashion like for functions? Meaning no
visibility
implies public and code below would work.
class A {
int $i=7;
function f() {
echo $this->i;
}
}
$a = new A();
$a->f();
echo $a->i;
Was inspired by discussion in "var" Deprecation thread.
Regards //Björn
Like the RFC :)
Could this be an opportunity to have default visibility for property's when
they are typed in a similar fashion like for functions? Meaning no
visibility
implies public and code below would work.
class A {
int $i=7;
function f() {
echo $this->i;
}
}
$a = new A();
$a->f();
echo $a->i;Was inspired by discussion in "var" Deprecation thread.
Regards //Björn
I'd rather like to see this being package visible rather than public. Of
course, that would be yet another Java thingy and I am not trying to
jump on the bandwagon here.
First, I like the Class Friendship RFC because it is very explicit in
terms of giving access to your members. However, it fails to solve some
issues in API design because friendship is not inherited and it is
especially useless in a composer package world where the classes you
might define as a friend are not even available (example follows).
Second, I like the Private Class PR because it is very simple in hiding
a class. The fact that is is purely namespace based is however a problem
because one might want to have a class in a sub-namespace but still have
the ability to instantiate it. Also protected classes would be only
possible by restricting them from the current down to sub-namespaces
which is totally meh.
The introduction of a new package
keyword to provide access modifiers
for more fine grained control could solve all of the above issues.
<?php
// Composer Package `fleshgrinder/best-cms-entity`
package fleshgrinder/entity;
namespace Fleshgrinder\Entity;
use DateTimeImmutable;
final public class Entity {
int $id;
string $name;
DateTimeImmutable $created;
DateTimeImmutable $changed;
private function __construct() {}
public function equals($other) {
if ($other instanceof self) {
return $this->id === $other->id
|| $this->name === $other->name;
}
return false;
}
}
<?php
// Composer Package `fleshgrinder/best-cms-entity`
package fleshgrinder/entity;
namespace Fleshgrinder\Repo;
use Fleshgrinder\Entity\Entity;
public interface Repo {
public function create(string $name): Entity:
}
<?php
// Composer Package `fleshgrinder/best-cms-in-memory-entity-repo`
package fleshgrinder/entity;
namespace Fleshgrinder\Repo;
use DateTimeImmutable;
use Fleshgrinder\Entity\Entity;
final public class InMemoryRepo implements Repo {
private array $entities = [];
public function create(string $name): Entity {
$new = new Entity();
$new->id = count($this->entities) + 1;
$new->name = $name;
$new->created = $new->changed = new DateTimeImmutable();
foreach ($this->entities as $entity) {
if ($entity->equals($new)) {
throw new \Exception;
}
}
$this->entities[] = $new;
return clone $new;
}
}
--
Richard "Fleshgrinder" Fussenegger
Like the RFC :)
Could this be an opportunity to have default visibility for property's
when
they are typed in a similar fashion like for functions? Meaning no
visibility
implies public and code below would work.
class A {
int $i=7;
function f() {
echo $this->i;
}
}
$a = new A();
$a->f();
echo $a->i;Was inspired by discussion in "var" Deprecation thread.
Regards //Björn
I'd rather like to see this being package visible rather than public. Of
course, that would be yet another Java thingy and I am not trying to
jump on the bandwagon here.
Sorry to dampen your enthusiasm, but you are going slightly off topic here.
Package visibility was tried by Guilherme Blanco before, the way namespaces
are implemented this is not possible in PHP in an easy way. As far as i
understood him, its way too much work for the little benefit. Typed
properties is a huge concept on its own, one small step at a time :-)
Sorry to dampen your enthusiasm, but you are going slightly off topic here.
Package visibility was tried by Guilherme Blanco before, the way namespaces
are implemented this is not possible in PHP in an easy way. As far as i
understood him, its way too much work for the little benefit. Typed
properties is a huge concept on its own, one small step at a time :-)
No worries you are not, not at all. I just wanted to thwart you and
others in directly assigning ...
final class A {
int $x;
}
... to be public and obstruct the opportunity of assigning it a
meaningful new functionality.
--
Richard "Fleshgrinder" Fussenegger
Den 2016-03-18 kl. 21:12, skrev Fleshgrinder:
Sorry to dampen your enthusiasm, but you are going slightly off topic here.
Package visibility was tried by Guilherme Blanco before, the way namespaces
are implemented this is not possible in PHP in an easy way. As far as i
understood him, its way too much work for the little benefit. Typed
properties is a huge concept on its own, one small step at a time :-)No worries you are not, not at all. I just wanted to thwart you and
others in directly assigning ...final class A { int $x; }
... to be public and obstruct the opportunity of assigning it a
meaningful new functionality.
Well, but if one should assign int $x without visibility keyword a
meaning, shouldn't it be the same as for function y() {} without
keyword, i.e. public?
Of course one can change both, but that sounds like a 8.0 topic.
Regards //Björn
Den 2016-03-18 kl. 21:12, skrev Fleshgrinder:
No worries you are not, not at all. I just wanted to thwart you and
others in directly assigning ...final class A { int $x; }
... to be public and obstruct the opportunity of assigning it a
meaningful new functionality.Well, but if one should assign int $x without visibility keyword a
meaning, shouldn't it be the same as for function y() {} without
keyword, i.e. public?Of course one can change both, but that sounds like a 8.0 topic.
Regards //Björn
It is a sad state the implicit public properties use var and implicit
public methods nothing, this makes the introduction of new visibility
modifiers terribly complicated. You are completely right, they should be
the same and var should be banned. It is consistent and that is very
important. I cannot and do not want to argue against this. Yet at the
same time the lack of access (and additional visibility) modifiers is
what I am missing the most. Seems as if this has to wait for 8.0 (or
9.0, depending on the resistance). :(
--
Richard "Fleshgrinder" Fussenegger
Moving this to a new thread in order to not spam the other.
Den 2016-03-18 kl. 21:12, skrev Fleshgrinder:
No worries you are not, not at all. I just wanted to thwart you and
others in directly assigning ...final class A { int $x; }
... to be public and obstruct the opportunity of assigning it a
meaningful new functionality.Well, but if one should assign int $x without visibility keyword a
meaning, shouldn't it be the same as for function y() {} without
keyword, i.e. public?Of course one can change both, but that sounds like a 8.0 topic.
Regards //Björn
It is a sad state the implicit public properties use var and implicit
public methods nothing, this makes the introduction of new visibility
modifiers terribly complicated. You are completely right, they should be
the same and var should be banned. It is consistent and that is very
important. I cannot and do not want to argue against this. Yet at the
same time the lack of access (and additional visibility) modifiers is
what I am missing the most. Seems as if this has to wait for 8.0 (or
9.0, depending on the resistance). :(
I just thought about this some more and this is actually not true.
namespace Fleshgrinder\Examples\PhpInternals;
final class A {
int $x;
var $y;
$z;
function getX() {
return $this->x;
}
}
Everything is public here, as we already agreed upon and changing that
would be a topic for 8.0 or 9.0. The var keyword should be banned imho
but I do not know about the $z in the example. Is this even possible
with the parser? Otherwise we could keep the var keyword because with
my proposal here it would become useful again.
assembly fleshgrinder/experiments;
namespace Fleshgrinder\Examples\PhpInternals;
final class A {
int $x;
function getX() {
return $this->x;
}
}
Where assembly could also be module, package, ... and it allows
one to group multiple classes and namespaces together across composer
packages. Furthermore it alters the access and visibility of members. In
the above example where no visibility modifiers are specified both the
class /A/ and the method /getX/ are only accessible within the
fleshgrinder/experiments assembly. This approach would allow the
introduction of such a system in a feature release (7.x.0) without
breaking changes. Either the new keyword is present and missing
visibility modifiers are globally scoped (hence implicit public) due to
everything being in the global assembly by default or the new keyword is
present and missing visibility modifiers are restricting access to the
assembly.
Usage of the word /package/ could be a blessing and a curse at the same
time because the word is already heavily in use in the PHP world, e.g.
/@package/ in DocBlocks or in /composer package/.
The same holds true for /module/ because it is sometimes used to refer
to PHP extensions and PEAR uses it.
Hence, assembly might be the best choice here and is my proposal.
The following proposal in regards to access and visibility is not well
thought through but hopefully a starting point:
Methods
private := limited to the current class
protected := limited to the current and child classes
:= limited to the current assembly
public := global
Properties
private := limited to the current class
protected := limited to the current and child classes
:= limited to the current assembly
public := global
var := E_STRICT
or limited to the current assembly
Classes, Interfaces, and Traits
private := limited to the current namespace
protected := limited to the current and child namespaces
:= limited to the current assembly
public := global
Default assembly is always global and old code would not change at all. :)
--
Richard "Fleshgrinder" Fussenegger
Fleshgrinder wrote on 19/03/2016 10:48:
It is a sad state the implicit public properties usevar and implicit
public methods nothing, this makes the introduction of new visibility
modifiers terribly complicated.
I see it a bit differently - you could think of "var $foo" as shorthand
for "public var $foo", in the same way that "function foo()" is
shorthand for "public function foo()". Obviously, we don't actually
allow "public var", but to me "var" has nothing to do with visibility,
it's just a noise token like "function".
If we did want namespace/package/whatever visibility, it wouldn't make
much sense for it to suddenly become the default for all legacy code, so
it would need a new visibility keyword anyway, and that keyword would be
available to all members as required - properties, methods, and even
constants. I don't see that the existence of "var" makes any difference
to that.
Regards,
Rowan Collins
[IMSoP]
Hello everyone,
I have completed the draft for an RFC, to add Typed Properties. The
patch has been written by the one and only Joe Watkins.https://wiki.php.net/rfc/typed-properties
I would really appreciate constructive feedback on this RFC, with a
few areas especially:
- How scared are we that integers can be expanded to floats on runtime?
This whole temporary nullability situation, where unset properties
will error on attempted usage if not set. Should they instead error
after the constructor has been called if they are still not holding a
value?Weak vs Strict. Right now this is entirely strict, with no
declare() to change mode. Reasons for this vary, from various sources,
but include "Not sure how to implement it" and "Well people should not
be using properties as part of their public API".Help on 3 would be appreciated.
Also let's please avoid "PHP IS TURNING INTO JAVA" and the other
rather common rhetoric. Strict Type Hinting might have been seen as a
battleground for fans of strict and fans of weak to fight through a
keyboard, but this RFC will not be the repeat.We'll have a nice, orderly, constructive conversation about this RFC,
and improve the patch as you all provide feedback.Let me know what you think folks!
Thanks for the RFC. I love it.
Some points:
- You should really try not to open any can of worms with regard to other
already defined parts of the language:
- Assume type juggling is a given, use that exact same rules
- Assume strict/weak is a given, don't try to change this.
- Document how public int $var; works with regard to default type,
explicitly define it outside the scope of nullable types and delegate
decision forward to a potential RFC in that area.
Regarding your bullets then:
- This point is outside the scope of this RFC, it might be relevant when
we discuss changing type juggling to be more consistent. - This is propably the critical point of this RFC, I think given how
public function foo(int $i) forces type juggling, typed properties should
force juggling to. This must be after the constructor, because public
stdClass $foo; would otherwise never work. - Weak vs strict: Outside of this RFC, assume that it is strict when
declared, otherwise weak for consistency with the status quo.
What about PDO's fetch-mode PDO::FETCH_CLASS? Apparently, it first fills in
properties and then calls the constructor. Seems unlikely that the new
behavior for uninitialized properties will cause a problem here, but
sometimes weird things happen. And this is another thing to check.
I also fully support Johannes Schlüter with the reference thing. Binding by
reference to a query, passing by reference to any sort of function might
happen here and there, and the developer will have to find workarounds in a
place which otherwise would be straighforward.
Having said that, I +1 the idea of typed properties (well, my internals
karma does not give much weight to this anyway ;-) ).
On Thu, Mar 17, 2016 at 9:11 AM Benjamin Eberlei kontakt@beberlei.de
wrote:
On Wed, Mar 16, 2016 at 5:36 PM, Phil Sturgeon pjsturgeon@gmail.com
wrote:Hello everyone,
I have completed the draft for an RFC, to add Typed Properties. The
patch has been written by the one and only Joe Watkins.https://wiki.php.net/rfc/typed-properties
I would really appreciate constructive feedback on this RFC, with a
few areas especially:
- How scared are we that integers can be expanded to floats on runtime?
This whole temporary nullability situation, where unset properties
will error on attempted usage if not set. Should they instead error
after the constructor has been called if they are still not holding a
value?Weak vs Strict. Right now this is entirely strict, with no
declare() to change mode. Reasons for this vary, from various sources,
but include "Not sure how to implement it" and "Well people should not
be using properties as part of their public API".Help on 3 would be appreciated.
Also let's please avoid "PHP IS TURNING INTO JAVA" and the other
rather common rhetoric. Strict Type Hinting might have been seen as a
battleground for fans of strict and fans of weak to fight through a
keyboard, but this RFC will not be the repeat.We'll have a nice, orderly, constructive conversation about this RFC,
and improve the patch as you all provide feedback.Let me know what you think folks!
Thanks for the RFC. I love it.
Some points:
- You should really try not to open any can of worms with regard to other
already defined parts of the language:
- Assume type juggling is a given, use that exact same rules
- Assume strict/weak is a given, don't try to change this.
- Document how public int $var; works with regard to default type,
explicitly define it outside the scope of nullable types and delegate
decision forward to a potential RFC in that area.Regarding your bullets then:
- This point is outside the scope of this RFC, it might be relevant when
we discuss changing type juggling to be more consistent.- This is propably the critical point of this RFC, I think given how
public function foo(int $i) forces type juggling, typed properties should
force juggling to. This must be after the constructor, because public
stdClass $foo; would otherwise never work.- Weak vs strict: Outside of this RFC, assume that it is strict when
declared, otherwise weak for consistency with the status quo.--
--
Best regards,
Victor Bolshov
Hi,
- This whole temporary nullability situation, where unset properties
will error on attempted usage if not set. Should they instead error
after the constructor has been called if they are still not holding a
value?
I'd have the error at the time of attempted use, if only because it's
easier for people trying to introduce typing to existing code.
I'd support borrowing the "?" nullable annotation from HackLang for
people who want a less strict behavior:
public ?string $name;
This means that $name can either be a string or the NULL
value. It could
be argued that this encourages bad practices (using NULL
to represent
errors, for example) but I think the benefits in providing easier adoption
outweigh the negatives.
Massive thumbs up from me on typed properties. Might be worth looking at
Hack's implementation as they're already doing this.
- Matt.
Hi,
I'd support borrowing the "?" nullable annotation from HackLang for
people who want a less strict behavior:
public ?string $name;
This means that $name can either be a string or the
NULL
value.
Or, do it like with parameter type hinting:
public string $name = null;
Although I'm all for limiting NULL
to only being the default value:
$this->name = null; // this should throw a TypeError
Cheers,
Andrey.
Hi,
I'd support borrowing the "?" nullable annotation from HackLang for
people who want a less strict behavior:
public ?string $name;
This means that $name can either be a string or the
NULL
value.Or, do it like with parameter type hinting:
public string $name = null;
Although I'm all for limiting
NULL
to only being the default value:$this->name = null; // this should throw a TypeError
NULL
is special type. '' and NULL
is different entity.
class User {
public string $username = NULL;
}
After authentication $username could be "yohgaki". What is should be
after logout? $username = '' does not make sense, but $username =
NULL.
There are ways to work around this, but property cannot be NULL
is
problematic. IMO.
class User {
public string|null $username = NULL;
}
may work. I'm not sure if this is good idea in general.
Regards,
--
Yasuo Ohgaki
yohgaki@ohgaki.net
Although I'm all for limiting
NULL
to only being the default value:$this->name = null; // this should throw a TypeError
NULL
is special type. '' andNULL
is different entity.class User {
public string $username = NULL;
}After authentication $username could be "yohgaki". What is should be
after logout? $username = '' does not make sense, but $username =
NULL.There are ways to work around this, but property cannot be
NULL
is
problematic. IMO.class User {
public string|null $username = NULL;
}may work. I'm not sure if this is good idea in general.
While I understand why some people think 'typed variables' are the bees
knees, we have also had the debate on the relevance of 'NULL' as a key
element of data management. Data validation goes far beyond 'it's an
integer' and a default value which is NOT '0' is just as likely as one
that is as is a return from a database that gives 'NULL' for fields that
are not returned in the sub-joins of a query. Making the 'its and
integer' a special case does not remove the need to then validate that
the integer returned is actually valid, or perhaps taking a different
program path if a NULL
value has been returned.
All I see most of the additional magic code doing to my creating a
usable copy of an array of database entries is adding more delays to
actually viewing the data. As a example, if I look up an individual
record on a family tree, returning a 'null' identifier for the father
link is correct and we don't then load that object. Making the database
return an empty string or a 0 depending on how your identifier is
structured is a complication that is not necessary, as is returning
typed values for data that has not yet been established such as birth
date or age.
Now if we are adding a proper validation layer then including a variable
type makes sense, but just doing part of the job for some bits that may
or may not actually be usable?
--
Lester Caine - G8HFL
Contact - http://lsces.co.uk/wiki/?page=contact
L.S.Caine Electronic Services - http://lsces.co.uk
EnquirySolve - http://enquirysolve.com/
Model Engineers Digital Workshop - http://medw.co.uk
Rainbow Digital Media - http://rainbowdigitalmedia.co.uk
Hi,
Le 16/03/2016 17:36, Phil Sturgeon a écrit :
Hello everyone,
I have completed the draft for an RFC, to add Typed Properties. The
patch has been written by the one and only Joe Watkins.
Maybe you can add a reference to a discussion we had some months ago :
http://www.serverphorums.com/read.php?7,1296955. This was not exactly
about typed properties but it also concludes that enforcing type checks
on every write access requires typed zvals, which would be extremely
powerful, but also a very complex task (mostly for performance reasons).
Regards
François
Quick update folks, the RFC patch now respects strict mode, so will be
weak by default and strict if enabled.
I'll update the RFC to reflect this soon, but I'm busy today and it
won't get done until tomorrow.
Also there is definitely no void keyword for properties.
We'll move onto working out default values and references shortly.
There are some great points in here, so let me get 0.3.0 of the RFC
out and we'll get onto that.
Hi,
Le 16/03/2016 17:36, Phil Sturgeon a écrit :
Hello everyone,
I have completed the draft for an RFC, to add Typed Properties. The
patch has been written by the one and only Joe Watkins.Maybe you can add a reference to a discussion we had some months ago :
http://www.serverphorums.com/read.php?7,1296955. This was not exactly about
typed properties but it also concludes that enforcing type checks on every
write access requires typed zvals, which would be extremely powerful, but
also a very complex task (mostly for performance reasons).Regards
François
Hi Phil,
Phil Sturgeon wrote:
- How scared are we that integers can be expanded to floats on runtime?
This is a vaguely-worded question, and I'm concerned not everyone
reading it might have the same interpretation.
If by "expanded" we mean that you can assign to an int value to a float
typed property, that's fine. We already allow that, even in strict mode,
for parameter and return type declarations. It just gets converted to a
float.
But if you mean that you can assign a float to an int typed property,
and have it not be coerced to an int, and have this work even in strict
mode, I am against that. This would mean allowing integer properties to
overflow to float, which entails a loss of information. If the
declaration is public int $a;
then $a should only be allowed to
contain an integer. Allowing it to also contain a float so that overflow
can happen confuses me - surely avoiding overflow is one of strict
typing's benefits?
- This whole temporary nullability situation, where unset properties
will error on attempted usage if not set. Should they instead error
after the constructor has been called if they are still not holding a
value?
While I'd prefer it if objects were properly initialised by their
constructors, I understand that this would be inconvenient for some
people, so my position here is a weak No.
- Weak vs Strict. Right now this is entirely strict, with no
declare() to change mode. Reasons for this vary, from various sources,
but include "Not sure how to implement it" and "Well people should not
be using properties as part of their public API".Help on 3 would be appreciated.
When I first wrote my Scalar Type Hints RFC and included the optional
strict typing mechanism, I didn't want to allow weak typing for return
types, because I felt it would violate the robustness principle.
However, I ended up allowing it because I feared the inconsistency would
make the RFC less likely to pass. I'm still not sure if weakly-typed
return types were a good idea.
Arguments are an input, return values are an output, but a property is
sort of both. So parameter types should follow whatever works for both
parameter and return types, and that's one reason I lean towards always
strict mode here.
That the programmer would be unsure of the types used for an object's
internal state, and that PHP might silently mangle the data, is also
something I find concerning.
But if we need to allow weak mode for the sake of the PHP way, so be it.
Thanks!
Andrea Faulds
https://ajf.me/
Hi Phil,
Hello everyone,
I have completed the draft for an RFC, to add Typed Properties. The
patch has been written by the one and only Joe Watkins.https://wiki.php.net/rfc/typed-properties
I would really appreciate constructive feedback on this RFC, with a
few areas especially:
How scared are we that integers can be expanded to floats on runtime?
This whole temporary nullability situation, where unset properties
will error on attempted usage if not set. Should they instead error
after the constructor has been called if they are still not holding a
value?Weak vs Strict. Right now this is entirely strict, with no
declare() to change mode. Reasons for this vary, from various sources,
but include "Not sure how to implement it" and "Well people should not
be using properties as part of their public API".Help on 3 would be appreciated.
Also let's please avoid "PHP IS TURNING INTO JAVA" and the other
rather common rhetoric. Strict Type Hinting might have been seen as a
battleground for fans of strict and fans of weak to fight through a
keyboard, but this RFC will not be the repeat.We'll have a nice, orderly, constructive conversation about this RFC,
and improve the patch as you all provide feedback.Let me know what you think folks!
I really love the RFC, thanks for working with Joe on this.
That said, I have a few quite common use-cases that arise and that are a
bit problematic, both because they are hacks and because they are actually
used in large projects.
Specifically, I'm worried about two particular use-cases:
- unsetting properties
- by-ref property assignment
To clarify, un-setting properties allows us to hide properties completely,
loading them on a per-usage basis:
class Foo
public int $bar;
public function __construct() {
unset($this->bar); // is this operation legal? For BC compliance,
I'd expect that to be the case
}
public function __get(string $name)
{
initialize_properties_here($this); // external thing connecting to
db, loading files, yadda yadda
}
}
var_dump((new Foo())->bar); // what happens here? can
initialize_properties_here
actually assign a non-int to $bar
? Will the
type safety still work?
The by-ref property assignment is a bit trickier, but can probably be
worked around with some overhead (re-assigning).
Here's what is going on:
class Foo
public int $bar;
public function __construct() {
unset($this->bar);
}
public function __get(string $name)
{
$this->bar = 123; // default value
$props = ['bar' => & $this->bar]; // is this operation now
considered illegal?
initialize_properties_here($props); // external thing connecting to
db, loading files, yadda yadda
}
}
In addition to these questions, I think that the reflection API also needs
a ReflectionProperty#getType() : ReflectionType
addition in order for the
RFC to be complete.
Cheers,
Marco Pivetta
To answer my own question: the entire RFC is a no-go in its current state.
That said, I have a few quite common use-cases that arise and that are a
bit problematic, both because they are hacks and because they are actually
used in large projects.Specifically, I'm worried about two particular use-cases:
- unsetting properties
- by-ref property assignment
To clarify, un-setting properties allows us to hide properties completely,
loading them on a per-usage basis:class Foo {
public int $bar;
public function __construct() {
unset($this->bar); // is this operation legal? For BC compliance,
I'd expect that to be the case
}
public function __get(string $name)
{
initialize_properties_here($this); // external thing connecting to
db, loading files, yadda yadda
}
}var_dump((new Foo())->bar); // what happens here? can
initialize_properties_here
actually assign a non-int to$bar
? Will the
type safety still work?
See https://3v4l.org/bsHXH/rfc#tabs - unset() doesn't work, and that breaks
some basic PHP semantics that are covered by phpt tests, but just for the
case of typed properties.
The by-ref property assignment is a bit trickier, but can probably be
worked around with some overhead (re-assigning).
Here's what is going on:class Foo {
public int $bar;
public function __construct() {
unset($this->bar);
}
public function __get(string $name)
{
$this->bar = 123; // default value
$props = ['bar' => & $this->bar]; // is this operation now
considered illegal?
initialize_properties_here($props); // external thing connecting
to db, loading files, yadda yadda
}
}
See https://3v4l.org/sKqYb/rfc#tabs - by-ref assignment is forcefully
disabled, and that breaks some more PHP semantics just for the case of
typed properties.
To sum it up, the RFC has one major problem: it focuses on the WHEN
assignments happen, rather than just checking WHAT is assigned.
I think the scope of the proposal needs to be reduced strictly to type
checking.
As it stands, this is too magic (and I do qu
Cheers,
Marco Pivetta
Just an idea of a syntax for nullable properties. What about:
/**
- Can be null
/
public int null $foo;
/* - Can NOT be null
*/
public float $bar;
This type of syntax has a long-living predecessor - SQL (INT NULL). I think
it is readable and it does not introduce ambiguity as in string|null case:
one might suggest that "string|int|float|null" is also possible. In my
solution, it is clear: the property either has the NULL
predicate or it
doesn't.
Cheers!
To answer my own question: the entire RFC is a no-go in its current state.
That said, I have a few quite common use-cases that arise and that are a
bit problematic, both because they are hacks and because they are
actually
used in large projects.Specifically, I'm worried about two particular use-cases:
- unsetting properties
- by-ref property assignment
To clarify, un-setting properties allows us to hide properties
completely,
loading them on a per-usage basis:class Foo {
public int $bar;
public function __construct() {
unset($this->bar); // is this operation legal? For BC compliance,
I'd expect that to be the case
}
public function __get(string $name)
{
initialize_properties_here($this); // external thing connecting
to
db, loading files, yadda yadda
}
}var_dump((new Foo())->bar); // what happens here? can
initialize_properties_here
actually assign a non-int to$bar
? Will
the
type safety still work?See https://3v4l.org/bsHXH/rfc#tabs - unset() doesn't work, and that
breaks
some basic PHP semantics that are covered by phpt tests, but just for the
case of typed properties.The by-ref property assignment is a bit trickier, but can probably be
worked around with some overhead (re-assigning).
Here's what is going on:class Foo {
public int $bar;
public function __construct() {
unset($this->bar);
}
public function __get(string $name)
{
$this->bar = 123; // default value
$props = ['bar' => & $this->bar]; // is this operation now
considered illegal?
initialize_properties_here($props); // external thing connecting
to db, loading files, yadda yadda
}
}See https://3v4l.org/sKqYb/rfc#tabs - by-ref assignment is forcefully
disabled, and that breaks some more PHP semantics just for the case of
typed properties.To sum it up, the RFC has one major problem: it focuses on the WHEN
assignments happen, rather than just checking WHAT is assigned.I think the scope of the proposal needs to be reduced strictly to type
checking.
As it stands, this is too magic (and I do quCheers,
Marco Pivetta
--
Best regards,
Victor Bolshov
First a general note on the complete nullability first: It is not up to
us as language designers to decide whether it is good practice to use
nullable properties or not. It is supported by many systems since ages
(e.g. SQL where nullable fields are also not recommended but heck they
are useful) and it is common practice in PHP. Hence, there must be
support for it. That does not mean that it is endorsed in any way by us,
it is just there for people who need it for the same reasons they needed
it in the past 20 or so years.
I also would like to repeat that ...
final class A {
private int $x = null;
}
... aligns perfectly with PHP 5+ syntax for optional arguments, is
unambiguous (in terms of interpreting, reading, understanding, and
writing), does not introduce a new syntax/keyword (e.g. /?int/) just
because the cool kids from Facebook have it and -- last but not least --
developers can use /isset/ and /=== null/ instead of weird checks like
/empty/ (super dangerous), /=== -1/, /=== 42/, /=== 'invalid'/, or ...
you get the picture.
Null is perfect for nothing and that is what null is: nothing.
To sum it up, the RFC has one major problem: it focuses on the WHEN
assignments happen, rather than just checking WHAT is assigned.I think the scope of the proposal needs to be reduced strictly to type
checking.
I know I am repeating myself but Marco summed it up perfectly and I
agree to 100% with him. The checks should start with the first userland
assignment and continue from there on to check the type upon userland
assignment.
I am stressing the /userland assignment/ because of previously mentioned
functionalities of some extension like /mysqli_result::fetch_object/ or
/PDOStatement::fetchObject/ who should be allowed to assign completely
different types. Casting and such should be the problem of implementers.
(The maintainers of the extension could of course implement auto-casting
for scalar types like /int/ but more than that seems like overkill too me.)
Disallowing references here might make users more aware of which
functions take parameters by reference, and avoid accidentally
modifying values. So this part of the RFC might be a good thing.
This is a noble intend, however, it makes the code harder to read and it
requires silly assignment operations just to get something done. Doing
stuff like ...
$sort_me = $this->array;
sort($sort_me);
$this->array = $sort_me;
... is an absolute no go, just look at it, however ...
$this->array = array_sort($this->array);
... would be awesome but the forces against clean-up and deprecations
are strong in the PHP world.
https://wiki.php.net/rfc/consistent_function_names
We could alter the lists use (e.g.) a /ref/ keyword for stuff that
requires stuff to be passed by reference and introduce new ones without it:
- /array_sort/ ~> new
- /array_ref_sort/ ~> /sort/
This would achieve the same goal but people could go for what they want
while making them more aware. Plus, there is an upgrade path if
references are to be removed from PHP at some point in the future.
Well ... I think this idea is also more noble than it is really
applicable, simply because there are still too many functions left that
do not need a rename but a reference. :P
--
Richard "Fleshgrinder" Fussenegger
First a general note on the complete nullability first: It is not up to
us as language designers to decide whether it is good practice to use
nullable properties or not. It is supported by many systems since ages
(e.g. SQL where nullable fields are also not recommended but heck they
are useful) and it is common practice in PHP. Hence, there must be
support for it. That does not mean that it is endorsed in any way by us,
it is just there for people who need it for the same reasons they needed
it in the past 20 or so years.
At the risk of a small tangent, I cannot agree at all here. The whole
point of language design is to make decisions regarding what "good
practices" to encourage, enforce, or disallow. Some languages disallow
mutable variables (despite them being useful). Some languages disallow
global variables (despite them being occasionally useful). Some
languages disallow NULLs (despite them having use cases in many
languages). Those are all viable and reasonable decisions for a
language designer to make in various circumstances; to say that it's
"not up to us as language designers" to make such decisions is to
abdicate our responsibility as language designers.
There may well be good arguments to allow property nullability. There's
also good arguments against it. We'll decide one way or another. But
"we shouldn't take a position at all" is worse than taking a position,
because that implicitly does take a position (allowing it), while
pretending that we didn't. Basically it means lying to ourselves about
it, which is never the right answer.
Design is about making deliberate, informed decisions. Let's make a
deliberate, informed decision.
To help with the informed part, can anyone speak to how other languages
handle null-typed properties? I believe Java allows everything to be
NULL
(which sucks), but I'm not sure about other modern languages. Can
anyone speak to that? (I'd be particularly interested in Python, Go,
and Rust, but any language you can speak to definitively would be
valuable to know about. Javascript and Ruby are, AFAIK, insufficiently
typed for the question to apply.)
--Larry Garfield
First a general note on the complete nullability first: It is not up to
us as language designers to decide whether it is good practice to use
nullable properties or not. It is supported by many systems since ages
(e.g. SQL where nullable fields are also not recommended but heck they
are useful) and it is common practice in PHP. Hence, there must be
support for it. That does not mean that it is endorsed in any way by us,
it is just there for people who need it for the same reasons they needed
it in the past 20 or so years.At the risk of a small tangent, I cannot agree at all here. The whole
point of language design is to make decisions regarding what "good
practices" to encourage, enforce, or disallow. Some languages disallow
mutable variables (despite them being useful). Some languages disallow
global variables (despite them being occasionally useful). Some languages
disallow NULLs (despite them having use cases in many languages). Those
are all viable and reasonable decisions for a language designer to make in
various circumstances; to say that it's "not up to us as language
designers" to make such decisions is to abdicate our responsibility as
language designers.There may well be good arguments to allow property nullability. There's
also good arguments against it. We'll decide one way or another. But "we
shouldn't take a position at all" is worse than taking a position, because
that implicitly does take a position (allowing it), while pretending that
we didn't. Basically it means lying to ourselves about it, which is never
the right answer.Design is about making deliberate, informed decisions. Let's make a
deliberate, informed decision.To help with the informed part, can anyone speak to how other languages
handle null-typed properties? I believe Java allows everything to beNULL
(which sucks), but I'm not sure about other modern languages. Can anyone
speak to that? (I'd be particularly interested in Python, Go, and Rust,
but any language you can speak to definitively would be valuable to know
about. Javascript and Ruby are, AFAIK, insufficiently typed for the
question to apply.)--Larry Garfield
--
C A Horte, the famous computer scientist, calls the introduction of nulls
into his type system (like how Java did it decades later) to be his Billion
dollar mistake. He did it for the mathematical elegance and didn't foresee
all of the null errors that would come. Nulls sound great in theory, in
practice they can be trouble.
Walter
--
The greatest dangers to liberty lurk in insidious encroachment by men of
zeal, well-meaning but without understanding. -- Justice Louis D. Brandeis
C A Horte, the famous computer scientist, calls the introduction of nulls
into his type system (like how Java did it decades later) to be his Billion
dollar mistake. He did it for the mathematical elegance and didn't foresee
all of the null errors that would come. Nulls sound great in theory, in
practice they can be trouble.
If one is dealing with a single variable this statement may apply, and
since everybody is trying to define the function of a single variable
they are missing the bigger picture.
When running a RELATIONAL database query, one is often combining the
results of several tables, Joins can be defined to only include a result
if the sub-record also exists, or ignore those fields if for that record
the result does not apply. NULL
is the CORRECT return for those fields,
and isnull is the correct check to select a function that is used when
that element of the result does not apply. One does not expect the
database engine to replace those NULL
values with acceptable typed
default results. This is why many of the previous discussions on how to
handle NULL
have resulted in even more problems! NULL
is a state of the
variable and the 'error message' contained within each line of a result set.
The argument goes on that NOT typing your variables is bad design, and
for some places that is a correct statement, but as I have said many
times, simply expecting the database result to be an integer is bad
design since the value returned will also have a defined length. We know
the problems that returning a BIGINT creates in PHP5, but simply trying
to ensure 'integer' is 64bit internally produces different results where
the database value is actually 8, 16 or 32 bits. And different results
again on 32bit builds of PHP don't help!
It's not unreasonable for a complex SQL query to produce different
'type' results for a column if the unions combine SMALLINT and INTEGER
values, and in these cases a 'string' result ensures consistency, which
PHP currently maps nicely, but which a strict type would baulk at.
Personally I would be looking to ensure the database design was
consistent, but we have 20+ years of data and originally the smallest
data type was used to keep the record sizes down. Just how many
different 'integer' fields do exist ... each with different rules.
--
Lester Caine - G8HFL
Contact - http://lsces.co.uk/wiki/?page=contact
L.S.Caine Electronic Services - http://lsces.co.uk
EnquirySolve - http://enquirysolve.com/
Model Engineers Digital Workshop - http://medw.co.uk
Rainbow Digital Media - http://rainbowdigitalmedia.co.uk
First a general note on the complete nullability first: It is not up to
us as language designers to decide whether it is good practice to use
nullable properties or not. It is supported by many systems since ages
(e.g. SQL where nullable fields are also not recommended but heck they
are useful) and it is common practice in PHP. Hence, there must be
support for it. That does not mean that it is endorsed in any way by us,
it is just there for people who need it for the same reasons they needed
it in the past 20 or so years.At the risk of a small tangent, I cannot agree at all here. The whole
point of language design is to make decisions regarding what "good
practices" to encourage, enforce, or disallow. Some languages disallow
mutable variables (despite them being useful). Some languages disallow
global variables (despite them being occasionally useful). Some
languages disallow NULLs (despite them having use cases in many
languages). Those are all viable and reasonable decisions for a
language designer to make in various circumstances; to say that it's
"not up to us as language designers" to make such decisions is to
abdicate our responsibility as language designers.There may well be good arguments to allow property nullability. There's
also good arguments against it. We'll decide one way or another. But
"we shouldn't take a position at all" is worse than taking a position,
because that implicitly does take a position (allowing it), while
pretending that we didn't. Basically it means lying to ourselves about
it, which is never the right answer.Design is about making deliberate, informed decisions. Let's make a
deliberate, informed decision.To help with the informed part, can anyone speak to how other languages
handle null-typed properties? I believe Java allows everything to be
NULL
(which sucks), but I'm not sure about other modern languages. Can
anyone speak to that? (I'd be particularly interested in Python, Go,
and Rust, but any language you can speak to definitively would be
valuable to know about. Javascript and Ruby are, AFAIK, insufficiently
typed for the question to apply.)--Larry Garfield
Nope Larry, you are actually totally right and I am completely wrong.
I played around with the current implementation at https://3v4l.org/ and
some PHP voodoo that is required to work around missing access modifiers
for proper information hiding and I could not find a situation where
nullability was actually required nor beneficial at all.
I simply was still stuck in the beginning of the discussion where it was
asked in regards to invalid state after the construction and if
properties are allowed to be void (I call it now void to avoid my
own confusion with null) or not. Hence, I mixed that up and came to
completely wrong conclusions and a desperate argumentation towards
language design. I actually feel ashamed right now because that is truly
what language design is about and I was preaching that in other threads. :P
Properties need the ability to be void even after construction and it is
up to the object to keep track and ensure state. Nullability is imho not
necessary at all. So we are all in line here.
Only topics left (for me) are special features
(mysqli_result::fetch_object and PDOStatement::fetchObject) and
references.
I see solutions (summarized in other messages of mine) to all of them
but I do not see them being implemented or even thought about.
--
Richard "Fleshgrinder" Fussenegger
Properties need the ability to be void even after construction and it is
up to the object to keep track and ensure state. Nullability is imho not
necessary at all. So we are all in line here.
If the record I am working with identifies elements are incorrectly
matched, then making a value 'void' is the correct action. We would just
NULL
that identifier and fire off a delete to the linked record in the
database. Otherwise we have to create additional logic to handle that.
--
Lester Caine - G8HFL
Contact - http://lsces.co.uk/wiki/?page=contact
L.S.Caine Electronic Services - http://lsces.co.uk
EnquirySolve - http://enquirysolve.com/
Model Engineers Digital Workshop - http://medw.co.uk
Rainbow Digital Media - http://rainbowdigitalmedia.co.uk
Properties need the ability to be void even after construction and it is
up to the object to keep track and ensure state. Nullability is imho not
necessary at all. So we are all in line here.If the record I am working with identifies elements are incorrectly
matched, then making a value 'void' is the correct action. We would just
NULL
that identifier and fire off a delete to the linked record in the
database. Otherwise we have to create additional logic to handle that.
And void would translate to unset($record->property) and not to
$record->property = null. This works with the current implementation
and any isset($record->property) works too. As you can see,
nullability was not required. :)
--
Richard "Fleshgrinder" Fussenegger
Properties need the ability to be void even after construction and it is
up to the object to keep track and ensure state. Nullability is imho not
necessary at all. So we are all in line here.If the record I am working with identifies elements are incorrectly
matched, then making a value 'void' is the correct action. We would just
NULL
that identifier and fire off a delete to the linked record in the
database. Otherwise we have to create additional logic to handle that.And void would translate to unset($record->property) and not to
$record->property = null. This works with the current implementation
and any isset($record->property) works too. As you can see,
nullability was not required. :)
I did not say what we have now is actually right ... NULL
state is
simply a mess :)
I need '$record->propertyX = null' because the property is part of the
data model. Unsetting propertyX so it does not exist means I have to
recreate it if I want to add it again, BUT if I am reading a new record
from the database I need a variable to put the propertyX into even if
that element of the result array is 'NULL'.
Going back to 'basics', I have an array of fields ( and using ADOdb
rather than PDO I have accurate data on what each field is! ).
Initializing the object view of that record I scan each field value and
validate the data, or skip it if the field is NULL. Do I need a
propertyX if it has no value - in my book yes - so that when building
the template to display the data I can include that this does not exist.
Could I use 'isset' instead - no - because THAT state says leave out the
field from the data display altogether. Could I create some arbitrary
default value that is 'not set' ... yes, but that is not how the years
of code base has been built! NULL
is 'value not set' not 'this variable
does not exist'
--
Lester Caine - G8HFL
Contact - http://lsces.co.uk/wiki/?page=contact
L.S.Caine Electronic Services - http://lsces.co.uk
EnquirySolve - http://enquirysolve.com/
Model Engineers Digital Workshop - http://medw.co.uk
Rainbow Digital Media - http://rainbowdigitalmedia.co.uk
Properties need the ability to be void even after construction and it is
up to the object to keep track and ensure state. Nullability is imho not
necessary at all. So we are all in line here.If the record I am working with identifies elements are incorrectly
matched, then making a value 'void' is the correct action. We would just
NULL
that identifier and fire off a delete to the linked record in the
database. Otherwise we have to create additional logic to handle that.And void would translate to unset($record->property) and not to
$record->property = null. This works with the current implementation
and any isset($record->property) works too. As you can see,
nullability was not required. :)I did not say what we have now is actually right ...
NULL
state is
simply a mess :)I need '$record->propertyX = null' because the property is part of the
data model. Unsetting propertyX so it does not exist means I have to
recreate it if I want to add it again, BUT if I am reading a new record
from the database I need a variable to put the propertyX into even if
that element of the result array is 'NULL'.Going back to 'basics', I have an array of fields ( and using ADOdb
rather than PDO I have accurate data on what each field is! ).
Initializing the object view of that record I scan each field value and
validate the data, or skip it if the field is NULL. Do I need a
propertyX if it has no value - in my book yes - so that when building
the template to display the data I can include that this does not exist.
Could I use 'isset' instead - no - because THAT state says leave out the
field from the data display altogether. Could I create some arbitrary
default value that is 'not set' ... yes, but that is not how the years
of code base has been built!NULL
is 'value not set' not 'this variable
does not exist'
You are mixing to things like I did. If you never unset and never assign
then the value of the property is null or as I defined to avoid
confusion void. All fine. The question was, should this result in an
error and the general consensus here is /no/.
The thingy with unset would be during runtime for whatever dubious
reasons.
--
Richard "Fleshgrinder" Fussenegger
You are mixing to things like I did. If you never unset and never assign
then the value of the property is null or as I defined to avoid
confusion void. All fine. The question was, should this result in an
error and the general consensus here is /no/.
My point is that $object->property = null; after the object has been
'constructed' is still a valid assignment ... the object's content may
well be changed by changing the loaded object id.
--
Lester Caine - G8HFL
Contact - http://lsces.co.uk/wiki/?page=contact
L.S.Caine Electronic Services - http://lsces.co.uk
EnquirySolve - http://enquirysolve.com/
Model Engineers Digital Workshop - http://medw.co.uk
Rainbow Digital Media - http://rainbowdigitalmedia.co.uk
You are mixing to things like I did. If you never unset and never assign
then the value of the property is null or as I defined to avoid
confusion void. All fine. The question was, should this result in an
error and the general consensus here is /no/.My point is that $object->property = null; after the object has been
'constructed' is still a valid assignment ... the object's content may
well be changed by changing the loaded object id.
I am not so sure about that and one could always reside to not using
type hints in such situations. Additionally I do not think that one
should change the /loaded object id/ better create a completely new
instance. But I might be missing more context here.
--
Richard "Fleshgrinder" Fussenegger
You are mixing to things like I did. If you never unset and never assign
then the value of the property is null or as I defined to avoid
confusion void. All fine. The question was, should this result in an
error and the general consensus here is /no/.My point is that $object->property = null; after the object has been
'constructed' is still a valid assignment ... the object's content may
well be changed by changing the loaded object id.I am not so sure about that and one could always reside to not using
type hints in such situations. Additionally I do not think that one
should change the /loaded object id/ better create a completely new
instance. But I might be missing more context here.
This may well be where my own problem comes into play. TRADITIONALLY you
simply cached an array of data and used the object to view and
manipulate each record in the array. Even today that stage has not
changed as we STILL do a single database access to grab the material.
But the idea of then creating 'new instances' of each item seems
overkill if all one is doing is manipulating the data to present a
particular view of it. All of the overhead even of 'typed properties'
does little to improve the results but rather adds excessive work to
data which in a read only mode has already been fully validated. It's
only when one goes to edit a record that the need for more refined
validation comes in, and validating then occurs in the context of the
data, not simply if it's a number or string. In the good old days one
would simply load a script that contained the functions necessary for
the job rather than loading all of the potential code relating to the
object and constructing an 'instance' to then apply a new date of birth
or event.
The way everything seems to be moving is that the 'application' will be
fully compiled and cached before you ever use it, in order to compensate
for all the extra code which will actually never be used by a large
section of the clients base. Auto-loading everything just on the off
chance it may be used is not progress in my book.
--
Lester Caine - G8HFL
Contact - http://lsces.co.uk/wiki/?page=contact
L.S.Caine Electronic Services - http://lsces.co.uk
EnquirySolve - http://enquirysolve.com/
Model Engineers Digital Workshop - http://medw.co.uk
Rainbow Digital Media - http://rainbowdigitalmedia.co.uk
Hi all,
first up, major thanks to both Joe and Phil for putting this together.
I'm fully behind the concept of typed properties and think it would be a
great feature to have in the language.
I have a few points I'd like to make on the current implementation being
proposed.
- There's an issue with the way the __get magic method works. I understand
this has been introduced from allowing unset on typed properties (because
of the reasons Marco mentioned previously), however it's still something I
feel needs addressing:
class Foo
{
public int $bar;
public function __get($name)
{
return 123;
}
}
$foo = new Foo();
unset($foo->bar);
var_dump($foo->bar); // int(123)
$foo = new Foo();
var_dump($foo->bar); // Fatal error: Uncaught TypeError: Typed property
Foo::$bar must not be accessed before initialisation
I'd consider the fact that var_dump($foo->bar); produces different things
here a bug. They're both unset/uninitialised and should produce the same
result. Which in my opinion should be "int(123)", but i'll get onto that in
a bit.
- Having a guarantee that a variable is of a certain type has a ton of
benefits, mostly that you don't have to add defensive code to check its
type. As Larry pointed out, littering your code based with !is_null() is
not a nice thing to have to do.
Unfortunately typed variables will at some point be uninitialised. you
can't really force the developer to set them (either at construct or later)
as this is something that should in my opinion always belong at
implementation and not baked in/forced at the language level. With this in
mind, handling uninitialised state will always be a thing that needs to be
done. We can't really guarantee that the property was set up properly, or
start to return a default value of said type.
Further to this, and the fact that the ability to unset typed properties
has been reintroduced it now becomes possible to initialise a property,
have it unset and get TypeError's on subsequent access (where no __get() is
provided). So even after initialisation we can never be 100% sure a
property hasn't been destroyed and contains the type you'd expect.
As it doesn't appear there's any way we can actually provide this
guarantee. I'd suggest removing TypeError's on access as it's superfluous.
This would then solve my first point as the magic method would be called as
expected. I know this isn't ideal, but the type check on set is still very
powerful and still provides a guarantee that you have either an
uninitialised property, or a one of a type you'd expect.
- One final small point; it would be great if we could also have typed
properties outside the scope of a class. Not a show stopper by any means,
but certainly a nice to have.
That's all. Again thanks so much for putting this together.
Lee
/@leedavis81
Hello everyone,
I have completed the draft for an RFC, to add Typed Properties. The
patch has been written by the one and only Joe Watkins.https://wiki.php.net/rfc/typed-properties
I would really appreciate constructive feedback on this RFC, with a
few areas especially:
How scared are we that integers can be expanded to floats on runtime?
This whole temporary nullability situation, where unset properties
will error on attempted usage if not set. Should they instead error
after the constructor has been called if they are still not holding a
value?Weak vs Strict. Right now this is entirely strict, with no
declare() to change mode. Reasons for this vary, from various sources,
but include "Not sure how to implement it" and "Well people should not
be using properties as part of their public API".Help on 3 would be appreciated.
Also let's please avoid "PHP IS TURNING INTO JAVA" and the other
rather common rhetoric. Strict Type Hinting might have been seen as a
battleground for fans of strict and fans of weak to fight through a
keyboard, but this RFC will not be the repeat.We'll have a nice, orderly, constructive conversation about this RFC,
and improve the patch as you all provide feedback.Let me know what you think folks!
Morning Lee,
Phil is in a field somewhere, but I don't want to leave you hanging on for
an answer ... so ...
class Foo
{
public $bar;
public function __get($name)
{
return 123;
}
}
$foo = new Foo();
var_dump($foo->bar);
You should not expect __get to be called in this example code; __get is
only invoked when the property is undefined.
That's what unset does; It doesn't just destroy the value, but tells the
engine that the property is not declared (leaves it undef), which is what
results in the subsequent invocation of __get upon access to the unset
property.
Accessing the property before it was unset though, as it always did,
attempts to read the default value (null).
We haven't changed any of that ...
If $bar were typed, it would obviously be unacceptable to return anything
other than a value of the declared type upon access, so we must not allow
return to happen if there is no value on a typed property, because it's an
obvious violation.
Accessing a typed member before there is any possible chance the variable
was set is a programmer error - colloquially known as "use before
initialization" - a very common programming error, which we helpfully raise
an exception for.
Accessing a typed member after they were unset is another programming error
- "use after free" - again, we helpfully raise an exception.
In both of these circumstances, raising an exception is the only possible
action; Anything else really would constitute a change to the way objects
work, and or a violation of the type declared.
The most you can say about this, is that it's strange that the same
exception is raised for both cases, which we're aware of; Right now, it's
impossible to distinguish between the two states.
The things you can be sure of:
- If you get a value from an access of a typed property, it is the
correct type. - If you get exceptions, you have logical errors in your code.
Cheers
Joe
Hi all,
first up, major thanks to both Joe and Phil for putting this together.
I'm fully behind the concept of typed properties and think it would be a
great feature to have in the language.I have a few points I'd like to make on the current implementation being
proposed.
- There's an issue with the way the __get magic method works. I understand
this has been introduced from allowing unset on typed properties (because
of the reasons Marco mentioned previously), however it's still something I
feel needs addressing:class Foo
{
public int $bar;
public function __get($name)
{
return 123;
}
}$foo = new Foo();
unset($foo->bar);
var_dump($foo->bar); // int(123)$foo = new Foo();
var_dump($foo->bar); // Fatal error: Uncaught TypeError: Typed property
Foo::$bar must not be accessed before initialisationI'd consider the fact that var_dump($foo->bar); produces different things
here a bug. They're both unset/uninitialised and should produce the same
result. Which in my opinion should be "int(123)", but i'll get onto that in
a bit.
- Having a guarantee that a variable is of a certain type has a ton of
benefits, mostly that you don't have to add defensive code to check its
type. As Larry pointed out, littering your code based with !is_null() is
not a nice thing to have to do.Unfortunately typed variables will at some point be uninitialised. you
can't really force the developer to set them (either at construct or later)
as this is something that should in my opinion always belong at
implementation and not baked in/forced at the language level. With this in
mind, handling uninitialised state will always be a thing that needs to be
done. We can't really guarantee that the property was set up properly, or
start to return a default value of said type.Further to this, and the fact that the ability to unset typed properties
has been reintroduced it now becomes possible to initialise a property,
have it unset and get TypeError's on subsequent access (where no __get() is
provided). So even after initialisation we can never be 100% sure a
property hasn't been destroyed and contains the type you'd expect.As it doesn't appear there's any way we can actually provide this
guarantee. I'd suggest removing TypeError's on access as it's superfluous.
This would then solve my first point as the magic method would be called as
expected. I know this isn't ideal, but the type check on set is still very
powerful and still provides a guarantee that you have either an
uninitialised property, or a one of a type you'd expect.
- One final small point; it would be great if we could also have typed
properties outside the scope of a class. Not a show stopper by any means,
but certainly a nice to have.That's all. Again thanks so much for putting this together.
Lee
/@leedavis81
On Wed, Mar 16, 2016 at 4:36 PM, Phil Sturgeon pjsturgeon@gmail.com
wrote:Hello everyone,
I have completed the draft for an RFC, to add Typed Properties. The
patch has been written by the one and only Joe Watkins.https://wiki.php.net/rfc/typed-properties
I would really appreciate constructive feedback on this RFC, with a
few areas especially:
How scared are we that integers can be expanded to floats on runtime?
This whole temporary nullability situation, where unset properties
will error on attempted usage if not set. Should they instead error
after the constructor has been called if they are still not holding a
value?Weak vs Strict. Right now this is entirely strict, with no
declare() to change mode. Reasons for this vary, from various sources,
but include "Not sure how to implement it" and "Well people should not
be using properties as part of their public API".Help on 3 would be appreciated.
Also let's please avoid "PHP IS TURNING INTO JAVA" and the other
rather common rhetoric. Strict Type Hinting might have been seen as a
battleground for fans of strict and fans of weak to fight through a
keyboard, but this RFC will not be the repeat.We'll have a nice, orderly, constructive conversation about this RFC,
and improve the patch as you all provide feedback.Let me know what you think folks!
Hi Joe,
Accessing the property before it was unset though, as it always did,
attempts to read the default value (null).We haven't changed any of that ...
Yes, you're right, this is exactly how it currently behaves :
https://3v4l.org/YpfYe (apologies for not checking this first).
Still seems inconsistent, but that's now outside the scope of the RFC.
The most you can say about this, is that it's strange that the same
exception is raised for both cases, which we're aware of; Right now, it's
impossible to distinguish between the two states.
Makes sense. AccessTypeError, WriteTypeError to both extend TypeError?
The things you can be sure of:
- If you get a value from an access of a typed property, it is the
correct type.- If you get exceptions, you have logical errors in your code.
Perfect. I'd still prefer the TypeError to only occur on setting rather
than on access as well (along with a fair amount of others:
https://twitter.com/leedavis81/status/710872866556874753), this would then
have the RFC only concerned itself with the WHAT rather than the WHEN, but
as long as that promise can be upheld then it's a great feature to have.
I think adding in rules at the language level to dictate when it should be
set (i.e after construct) is a bad move, so I'd be in favour of leaving it
as it stands at the moment. Property accessors could solve the problem of
uninitialised properties later on.
Cheers for all your hard work guys,
Lee
/@leedavis81
Morning Lee,
Makes sense. AccessTypeError, WriteTypeError to both extend TypeError?
Well maybe ... I'm not sure there is much purpose in distinguishing between
the two, while the engine doesn't make the distinction for normal untyped
properties.
If we make the distinction for typed properties, why do untyped ones behave
differently ?
We have to make every effort for typed and untyped properties to be
consistent with each other, even if strange ...
I'd still prefer the TypeError to only occur on setting rather than on
access as well
This would provide the opportunity for you to violate the declared type:
$foo = new class {
public int $bar = 10;
public function __construct() {
unset($this->bar);
}
public function __get($name) {
return "oh dear!";
}
};
var_dump($foo->bar);
In order to provide those "perfect" guarantees we must not allow that to
happen.
Zend objects aren't just full of the magic you know about in user land,
they can work in all kinds of strange and wonderful ways: Bottom line, the
value you wrote doesn't have to be the one returned when you read, for all
kinds of reasons.
The fact is the check is extremely cheap and without them, declaring the
type of the property in the first place would be pointless.
Cheers
Joe
Hi Joe,
Accessing the property before it was unset though, as it always did,
attempts to read the default value (null).We haven't changed any of that ...
Yes, you're right, this is exactly how it currently behaves :
https://3v4l.org/YpfYe (apologies for not checking this first).
Still seems inconsistent, but that's now outside the scope of the RFC.The most you can say about this, is that it's strange that the same
exception is raised for both cases, which we're aware of; Right now, it's
impossible to distinguish between the two states.Makes sense. AccessTypeError, WriteTypeError to both extend TypeError?
The things you can be sure of:
- If you get a value from an access of a typed property, it is the
correct type.- If you get exceptions, you have logical errors in your code.
Perfect. I'd still prefer the TypeError to only occur on setting rather
than on access as well (along with a fair amount of others:
https://twitter.com/leedavis81/status/710872866556874753), this would
then have the RFC only concerned itself with the WHAT rather than the WHEN,
but as long as that promise can be upheld then it's a great feature to have.I think adding in rules at the language level to dictate when it should be
set (i.e after construct) is a bad move, so I'd be in favour of leaving it
as it stands at the moment. Property accessors could solve the problem of
uninitialised properties later on.Cheers for all your hard work guys,
Lee
/@leedavis81