[RFC Discussion] Typed Properties

php.internals

Philip Sturgeon

10 years ago
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: 1. How scared are we that integers can be expanded to floats on runtime? 2. 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? 3. 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!

Will Fitch

10 years ago
On Wed, Mar 16, 2016, at 11:36 AM, Phil Sturgeon 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: > > 1. 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?

Philip Sturgeon

10 years ago
On Wed, Mar 16, 2016 at 12:49 PM, Will Fitch <willfitch@php.net> wrote:
> > > On Wed, Mar 16, 2016, at 11:36 AM, Phil Sturgeon 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: >> >> 1. 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.

Adam Harvey

10 years ago
On 16 March 2016 at 09:36, Phil Sturgeon <pjsturgeon@gmail.com> wrote:
> 3. 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

Philip Sturgeon

10 years ago
On Wed, Mar 16, 2016 at 1:08 PM, Adam Harvey <aharvey@php.net> wrote:
> On 16 March 2016 at 09:36, Phil Sturgeon <pjsturgeon@gmail.com> wrote: >> 3. 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. :)

Bob Weinand

10 years ago
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: > > 1. 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.
> 2. 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.
> 3. 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

Chris Riley

10 years ago
On 16 March 2016 at 17:17, Bob Weinand <bobwei9@hotmail.com> wrote:
> 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: > > > > 1. 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. > > > 2. 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. > > > 3. 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 > -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: http://www.php.net/unsub.php > >
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 }

Bob Weinand

10 years ago
> Am 16.03.2016 um 18:27 schrieb Chris Riley <t.carnage@gmail.com>: > On 16 March 2016 at 17:17, Bob Weinand <bobwei9@hotmail.com <mailto:bobwei9@hotmail.com>> wrote: > > Am 16.03.2016 um 17:36 schrieb Phil Sturgeon <pjsturgeon@gmail.com <mailto:pjsturgeon@gmail.com>>: > > 2. 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. > > 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

Johannes Schlueter

10 years ago
On Wed, 2016-03-16 at 12:36 -0400, Phil Sturgeon 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'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

Bob Weinand

10 years ago
> Am 16.03.2016 um 18:39 schrieb Johannes Schlüter <johannes@schlueters.de>: > > On Wed, 2016-03-16 at 12:36 -0400, Phil Sturgeon 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'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

Johannes Schlueter

10 years ago
On Wed, 2016-03-16 at 18:50 +0100, Bob Weinand wrote:
> 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

Bob Weinand

10 years ago
> Am 16.03.2016 um 19:05 schrieb Johannes Schlüter <johannes@schlueters.de>: > > On Wed, 2016-03-16 at 18:50 +0100, Bob Weinand wrote: >> 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.
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

Johannes Schlueter

10 years ago
On Wed, 2016-03-16 at 19:15 +0100, Bob Weinand 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 :-) johannes

Bob Weinand

10 years ago
> Am 16.03.2016 um 19:41 schrieb Johannes Schlüter <johannes@schlueters.de>: > > On Wed, 2016-03-16 at 19:15 +0100, Bob Weinand 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 :-) > > > 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

Net Mo

10 years ago
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>:

Andrew Faulds

10 years ago
Hi Johannes, Johannes Schlüter wrote:
> On Wed, 2016-03-16 at 19:15 +0100, Bob Weinand 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/

Rowan Collins

10 years ago
Andrea Faulds wrote on 17/03/2016 22:32:
> Hi Johannes, > > Johannes Schlüter wrote: >> On Wed, 2016-03-16 at 19:15 +0100, Bob Weinand 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]

Nikita Popov

10 years ago
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: >> >>> On Wed, 2016-03-16 at 19:15 +0100, Bob Weinand 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. >
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

Rowan Collins

10 years ago
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]

Philip Sturgeon

10 years ago
On Wed, Mar 16, 2016 at 1:39 PM, Johannes Schlüter <johannes@schlueters.de> wrote:
> On Wed, 2016-03-16 at 12:36 -0400, Phil Sturgeon 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'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: https://3v4l.org/taC8D Fatal error: Uncaught TypeError: Typed property A::$i must not be referenced Is that what you expect? If not, what would you expect?

Johannes Schlueter

10 years ago
On Wed, 2016-03-16 at 15:29 -0400, Phil Sturgeon 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: > > https://3v4l.org/taC8D > > 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

Yasuo Ohgaki

10 years ago
Hi all, On Thu, Mar 17, 2016 at 8:30 AM, Johannes Schlüter <johannes@schlueters.de> wrote:
> On Wed, 2016-03-16 at 15:29 -0400, Phil Sturgeon 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: >> >> https://3v4l.org/taC8D >> >> 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

Larry Garfield

10 years ago
On 3/16/16 11:36 AM, Phil Sturgeon 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
Overall thought: <3
> I would really appreciate constructive feedback on this RFC, with a > few areas especially: > > 1. 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.
> 2. 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: 1) Throw a lot of is_null() calls around my code. 2) 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.
> 3. 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

Ryan Pallas

10 years ago
On Wed, Mar 16, 2016 at 12:50 PM, Larry Garfield <larry@garfieldtech.com> wrote:
> On 3/16/16 11:36 AM, Phil Sturgeon wrote: > >> >> 2. 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: > > 1) Throw a lot of is_null() calls around my code. > 2) 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.

Andrey Andreev

10 years ago
Hi, On Wed, Mar 16, 2016 at 9:21 PM, Ryan Pallas <derokorian@gmail.com> wrote:
> On Wed, Mar 16, 2016 at 12:50 PM, Larry Garfield <larry@garfieldtech.com> > wrote: > > > On 3/16/16 11:36 AM, Phil Sturgeon wrote: > > > >> > >> 2. 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: > > > > 1) Throw a lot of is_null() calls around my code. > > 2) 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.

Larry Garfield

10 years ago
On 3/16/16 2:30 PM, Andrey Andreev 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. :-)
-- --Larry Garfield

Andrey Andreev

10 years ago
On Thu, Mar 17, 2016 at 12:00 AM, Larry Garfield <larry@garfieldtech.com> wrote:
> On 3/16/16 2:30 PM, Andrey Andreev 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.

Philip Sturgeon

10 years ago
On Wed, Mar 16, 2016 at 6:17 PM, Andrey Andreev <narf@devilix.net> wrote:
> On Thu, Mar 17, 2016 at 12:00 AM, Larry Garfield <larry@garfieldtech.com> > wrote: > >> On 3/16/16 2:30 PM, Andrey Andreev 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.
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.

Andrey Andreev

10 years ago
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 a NULL 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. >
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: 1. 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) 2. A meaningful value is one that is valid for the application logic, because that same logic defines its ... meaning 3. 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.

Fleshgrinder

10 years ago
Awesome feature thanks for the RFC. :) On 3/16/2016 5:36 PM, Phil Sturgeon wrote:
> 1. 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*). On 3/16/2016 5:36 PM, Phil Sturgeon wrote:
> 2. 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. On 3/16/2016 6:27 PM, Chris Riley wrote:
> [...] 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. On 3/16/2016 5:36 PM, Phil Sturgeon wrote:
> 3. 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. On 3/16/2016 6:39 PM, Johannes Schlüter wrote:
> 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

Dennis Birkholz

10 years ago
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

Fleshgrinder

10 years ago
On 3/18/2016 11:10 PM, Dennis Birkholz wrote:
> 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

Andrew Faulds

10 years ago
Hi, Fleshgrinder wrote:
> On 3/16/2016 5:36 PM, Phil Sturgeon wrote: >> 2. 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.
> On 3/16/2016 6:27 PM, Chris Riley wrote: >> [...] 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.
> > On 3/16/2016 5:36 PM, Phil Sturgeon wrote: >> 3. 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/

Fleshgrinder

10 years ago
On 3/19/2016 9:32 PM, Andrea Faulds wrote:
> 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? On 3/19/2016 9:32 PM, Andrea Faulds wrote:
> 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. On 3/19/2016 9:32 PM, Andrea Faulds wrote:
>> >> 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. On 3/19/2016 9:32 PM, Andrea Faulds wrote:
>> On 3/16/2016 6:27 PM, Chris Riley wrote: >>> [...] 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. >
You are absolutely right I did not think about that. But currently it seems as if nobody (including me) wants nullable types anyways. On 3/19/2016 9:32 PM, Andrea Faulds wrote:
>> On 3/16/2016 5:36 PM, Phil Sturgeon wrote: >>> 3. 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

Fleshgrinder

10 years ago
On 3/20/2016 2:19 PM, Fleshgrinder wrote:
> On 3/19/2016 9:32 PM, Andrea Faulds wrote: >> 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

Andrew Faulds

10 years ago
Hi again, Fleshgrinder wrote:
> On 3/19/2016 9:32 PM, Andrea Faulds 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.
> On 3/19/2016 9:32 PM, Andrea Faulds wrote: >> 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/

Björn Larsson

10 years ago
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: > > 1. How scared are we that integers can be expanded to floats on runtime? > > 2. 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? > > 3. 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

Fleshgrinder

10 years ago
On 3/16/2016 10:12 PM, Björn Larsson wrote:
> 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

Benjamin Eberlei

10 years ago
On Wed, Mar 16, 2016 at 10:47 PM, Fleshgrinder <php@fleshgrinder.com> wrote:
> On 3/16/2016 10:12 PM, Björn Larsson wrote: > > 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 :-)

Fleshgrinder

10 years ago
On 3/17/2016 9:03 AM, Benjamin Eberlei wrote:
> 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

Björn Larsson

10 years ago
Den 2016-03-18 kl. 21:12, skrev Fleshgrinder:
> On 3/17/2016 9:03 AM, Benjamin Eberlei wrote: >> 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

Fleshgrinder

10 years ago
On 3/19/2016 11:38 AM, Björn Larsson wrote:
> 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

Fleshgrinder

10 years ago
Moving this to a new thread in order to not spam the other. On 3/19/2016 11:48 AM, Fleshgrinder wrote:
> On 3/19/2016 11:38 AM, Björn Larsson wrote: >> 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

Rowan Collins

10 years ago
Fleshgrinder wrote on 19/03/2016 10:48:
> 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.
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]

Benjamin Eberlei

10 years ago
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: > > 1. How scared are we that integers can be expanded to floats on runtime?
> 2. 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? > > 3. 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: 1. 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: 1. This point is outside the scope of this RFC, it might be relevant when we discuss changing type juggling to be more consistent. 2. 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. 3. Weak vs strict: Outside of this RFC, assume that it is strict when declared, otherwise weak for consistency with the status quo.

Victor Bolshov

10 years ago
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: > > > > 1. How scared are we that integers can be expanded to floats on runtime? > > > > 2. 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? > > > > 3. 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: > > 1. 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: > > 1. This point is outside the scope of this RFC, it might be relevant when > we discuss changing type juggling to be more consistent. > 2. 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. > 3. Weak vs strict: Outside of this RFC, assume that it is strict when > declared, otherwise weak for consistency with the status quo. > > > > > > -- > > PHP Internals - PHP Runtime Development Mailing List > > To unsubscribe, visit: http://www.php.net/unsub.php > > > > >
-- Best regards, Victor Bolshov

Matt Prelude

10 years ago
Hi, On 16/03/16 16:36, Phil Sturgeon wrote:
> 2. 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.

Andrey Andreev

10 years ago
Hi, On Thu, Mar 17, 2016 at 1:02 PM, Matt Prelude <me@mprelu.de> wrote: 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.

Yasuo Ohgaki

10 years ago
Hi, On Thu, Mar 17, 2016 at 8:46 PM, Andrey Andreev <narf@devilix.net> wrote:
> 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

Lester Caine

10 years ago
On 18/03/16 10:55, Yasuo Ohgaki wrote:
>> 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.
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

François Laupretre

10 years ago
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. > > https://wiki.php.net/rfc/typed-properties >
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

Philip Sturgeon

10 years ago
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. On Thu, Mar 17, 2016 at 9:20 AM, François Laupretre <francois@php.net> wrote:

Andrew Faulds

10 years ago
Hi Phil, Phil Sturgeon wrote:
> 1. 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?
> 2. 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.
> 3. 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/

Marco Pivetta

10 years ago
Hi Phil, On 16 March 2016 at 17:36, 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: > > 1. How scared are we that integers can be expanded to floats on runtime? > > 2. 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? > > 3. 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 http://twitter.com/Ocramius http://ocramius.github.com/

Marco Pivetta

10 years ago
To answer my own question: the entire RFC is a no-go in its current state. On 18 March 2016 at 10:09, Marco Pivetta <ocramius@gmail.com> wrote:
> 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 http://twitter.com/Ocramius http://ocramius.github.com/

Victor Bolshov

10 years ago
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! On Fri, Mar 18, 2016 at 12:49 PM Marco Pivetta <ocramius@gmail.com> wrote:
> To answer my own question: the entire RFC is a no-go in its current state. > On 18 March 2016 at 10:09, Marco Pivetta <ocramius@gmail.com> wrote: > > > 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 > > http://twitter.com/Ocramius > > http://ocramius.github.com/ >
-- Best regards, Victor Bolshov

Fleshgrinder

10 years ago
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. On 3/18/2016 12:48 PM, Marco Pivetta wrote:
> 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.) On 3/17/2016 11:32 PM, Andrea Faulds wrote:
> 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

Larry Garfield

10 years ago
On 03/18/2016 03:49 PM, Fleshgrinder wrote:
> 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

Walter Parker

10 years ago
On Friday, March 18, 2016, Larry Garfield <larry@garfieldtech.com> wrote:
> On 03/18/2016 03:49 PM, Fleshgrinder wrote: > >> 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 > > -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: http://www.php.net/unsub.php > > 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

Lester Caine

10 years ago
On 19/03/16 05:20, Walter Parker wrote:
>> 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

Fleshgrinder

10 years ago
On 3/19/2016 5:04 AM, Larry Garfield wrote:
> On 03/18/2016 03:49 PM, Fleshgrinder wrote: >> 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

Lester Caine

10 years ago
On 19/03/16 10:23, Fleshgrinder wrote:
> 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

Fleshgrinder

10 years ago
On 3/19/2016 11:54 AM, Lester Caine wrote:
> On 19/03/16 10:23, Fleshgrinder wrote: >> 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

Lester Caine

10 years ago
On 19/03/16 11:02, Fleshgrinder wrote:
>>> 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

Fleshgrinder

10 years ago
On 3/19/2016 12:23 PM, Lester Caine wrote:
> On 19/03/16 11:02, Fleshgrinder wrote: >>>> 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

Lee davis

10 years ago
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. 1) 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. 2) 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. 3) 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:

Joe Watkins

10 years ago
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 On Fri, Mar 18, 2016 at 8:39 PM, Lee Davis <leedavis81@gmail.com> wrote:

Lee davis

10 years ago
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

Joe Watkins

10 years ago
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 On Sat, Mar 19, 2016 at 9:17 AM, Lee Davis <leedavis81@gmail.com> wrote: