Final variables

php.internals

David Rodrigues

9 years ago
Hello internals, Java supports the "final" keyword before a variable to determines that this variables never change it reference. If I declare a variable as "final", I can only initialize that once, then I could not change it after (but I can manipulate the instance, without issues). There some special reason to PHP doesn't supports things like that? final $number = 123; $number = 456; // Error: you change change final variables. final $object = new stdClass; $object->allowed = true; // No error. This feature make sense because it tells to dev that the variable value could not be updated directly (new reference). Which make easy to identify when a variable is modifiable or not. It is valid for a RFC? - It can uses the "final" keyword; - It doesn't a BC; I too think that a final variable can be useful to make some internal improvements on language (but it could be a BC for PHP internals). I mean, as the variable could not be modified, then it don't need be a "flexible" type internally (I guess that it is a zval, right?). Reference: https://github.com/kalessil/phpinspectionsea/issues/363
-- David Rodrigues

Kalle Sommer Nielsen

9 years ago
Hi David 2017-06-28 18:10 GMT+02:00 David Rodrigues <david.proweb@gmail.com>:
> Hello internals, > > Java supports the "final" keyword before a variable to determines that this > variables never change it reference. If I declare a variable as "final", I > can only initialize that once, then I could not change it after (but I can > manipulate the instance, without issues). > > There some special reason to PHP doesn't supports things like that?
It seems like what you are looking for here is actually a constant[1]. However constants do not support non scalar types, such as array or objects, what would really solve it on the objects side of things would be the introduction of a "readonly" keyword or similar, like that of C#[2]
> final $number = 123; > $number = 456; // Error: you change change final variables. > > final $object = new stdClass; > $object->allowed = true; // No error. > > This feature make sense because it tells to dev that the variable value > could not be updated directly (new reference). Which make easy to identify > when a variable is modifiable or not. > > It is valid for a RFC? > > - It can uses the "final" keyword; > - It doesn't a BC;
Anything is usually valid for an RFC, however I think (personally) that this should rather be an RFC for a readonly keyword if anything
> I too think that a final variable can be useful to make some internal > improvements on language (but it could be a BC for PHP internals). I mean, > as the variable could not be modified, then it don't need be a "flexible" > type internally (I guess that it is a zval, right?). > > Reference: https://github.com/kalessil/phpinspectionsea/issues/363 > > -- > David Rodrigues
[1] http://php.net/constants [2] https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/readonly
-- regards, Kalle Sommer Nielsen kalle@php.net

Niklas Keller

9 years ago
2017-06-28 18:59 GMT+02:00 Kalle Sommer Nielsen <kalle@php.net>:
> Hi David > > 2017-06-28 18:10 GMT+02:00 David Rodrigues <david.proweb@gmail.com>: > > Hello internals, > > > > Java supports the "final" keyword before a variable to determines that > this > > variables never change it reference. If I declare a variable as "final", > I > > can only initialize that once, then I could not change it after (but I > can > > manipulate the instance, without issues). > > > > There some special reason to PHP doesn't supports things like that? > > It seems like what you are looking for here is actually a constant[1]. > However constants do not support non scalar types, such as array or > objects,
Arrays are supported, but not objects. Regards, Niklas

Kalle Sommer Nielsen

9 years ago
2017-06-28 19:37 GMT+02:00 Niklas Keller <me@kelunik.com>:
> Arrays are supported, but not objects.
Ah yes ofcourse, my apologies.
-- regards, Kalle Sommer Nielsen kalle@php.net

David Rodrigues

9 years ago
2017-06-28 13:59 GMT-03:00 Kalle Sommer Nielsen <kalle@php.net>:
> Hi David > > It seems like what you are looking for here is actually a constant[1]. > However constants do not support non scalar types, such as array or > objects, what would really solve it on the objects side of things > would be the introduction of a "readonly" keyword or similar, like > that of C#[2] >
​​ ​Not on reality.​ :( The "final" keyworks make a "local scope" variable value "blocked to rewrite" after instantiate it. Okay, it sounds like a "const", and it is, but "not as we known it". While constants are class member (or globals), a final variable is just a variable blocked to rewrite, and it could be initialized with a new data content every time that the context is called (eg. a function). For instance (note that the parameter is "final", then I can't modify the parameter variable, but it can receives a new value each time that I call this function): function write(final $message) { echo $message; } write("Hello"); write("World"); // Write "Hello World" on ouput. Or then: function randomNumber() { final $number = mt_rand(); echo $number; // $number = mt_rand_again(); // <-- will not be allowed! } randomNumber(); ​ // Write "0.123" randomNumber(); ​ // Write "0.456" ​
> > [1] http://php.net/constants > [2] https://docs.microsoft.com/en-us/dotnet/csharp/language- > reference/keywords/readonly >
-- David Rodrigues

David Rodrigues

9 years ago
​ Walter Parker <walterp@gmail.com> ​ wrote:​
> ​
Constant may not have been the right word (or idea), but I agree with Kalle. We should use readonly as the keyword here and not final as the keyword. We can keep the same scoping rules so the following would work:
>​
​> function randomNumber() { ​>​ *readonly *$number = mt_rand(); ​ ​I agree that "readonly" is a better keyword, but we have to points here: 1. "final" is used on some language (Java, C++ I guess); 2. "final" keyword does exists on PHP with a "similar" behaviour; If we can implements "readonly" is good too, because turn it more implicit. 2017-06-28 15:46 GMT-03:00 David Rodrigues <david.proweb@gmail.com>:
> 2017-06-28 13:59 GMT-03:00 Kalle Sommer Nielsen <kalle@php.net>: > >> Hi David >> >> It seems like what you are looking for here is actually a constant[1]. >> However constants do not support non scalar types, such as array or >> objects, what would really solve it on the objects side of things >> would be the introduction of a "readonly" keyword or similar, like >> that of C#[2] >> > ​​ > > ​Not on reality.​ :( > > The "final" keyworks make a "local scope" variable value "blocked to > rewrite" after instantiate it. > Okay, it sounds like a "const", and it is, but "not as we known it". > > While constants are class member (or globals), a final variable is just a > variable blocked to rewrite, and it could be initialized with a new data > content every time that the context is called (eg. a function). > > For instance (note that the parameter is "final", then I can't modify the > parameter variable, but it can receives a new value each time that I call > this function): > > function write(final $message) { echo $message; } > write("Hello"); > write("World"); > // Write "Hello World" on ouput. > > Or then: > > function randomNumber() { > final $number = mt_rand(); > echo $number; > > // $number = mt_rand_again(); // <-- will not be allowed! > } > randomNumber(); > ​ // Write "0.123" > randomNumber(); > ​ // Write "0.456" > ​ > > > >> >> [1] http://php.net/constants >> [2] https://docs.microsoft.com/en-us/dotnet/csharp/language-refe >> rence/keywords/readonly >> > > -- > David Rodrigues >
-- David Rodrigues

Kalle Sommer Nielsen

9 years ago
2017-06-29 1:07 GMT+02:00 David Rodrigues <david.proweb@gmail.com>:
> readonly $number = mt_rand(); > I agree that "readonly" is a better keyword, but we have to points here: > 1. "final" is used on some language (Java, C++ I guess); > 2. "final" keyword does exists on PHP with a "similar" behaviour;
> If we can implements "readonly" is good too, because turn it more implicit.
True we could re-use the final keyword, but think about it this way, we got final methods and final classes which means it cannot be overridden/extended respectively, if the final keyword then would also apply to variables but it would mean they could not be written to, that would create a WTF-factor. The readonly keyword would work for any visibility modifiers, so inherited classes or extending classes may read a protected property, but not modify it as well: class A { protected readonly $b; public function __construct() { $this->b = 'C'; } } class B extends A { public function read() { echo $this->b; } public function write() { $this->b = 'D'; } } $b = new B; $b->read(); // "C" $b->write(); // <- error
-- regards, Kalle Sommer Nielsen kalle@php.net

Marco Pivetta

9 years ago
The `final` modifier would likely clash with property type definitions, if those ever make it to the language. Still, a few pain-points remain: * References to properties (you cannot reference final properties anymore, and that is effectively a BC break for anything using reflection and closure scope binding) * Arrays and resources as properties * Named (Factory) constructors * Property nullability - how do you define if a property was written to the "last valid time" before freezing it * Unset properties and providing an alternative mechanism to distinguish "uninitiated", "undefined" and "frozen" in he event of final properties usage, without breaking current property access semantics This is the exact same set of problems we had with the typed properties RFC, and which caused the RFC to fail: please take all these in consideration while designing the feature. On 29 Jun 2017 4:57 AM, "Kalle Sommer Nielsen" <kalle@php.net> wrote:

Dan Ackroyd

9 years ago
On 29 June 2017 at 05:20, Marco Pivetta <ocramius@gmail.com> wrote:
> References to properties ...is effectively a BC break for anything > using reflection and closure scope binding
No it's not. If that feature was brought into PHP, any code that is currently valid, that the library is capable of reflecting, would still be valid code, that the library would still be capable of reflecting. Yes, there would be some new syntax which the library wouldn't be aware of, which wouldn't be supported in the library, but that is not a BC break. Adding new syntax to a language does mean that people who have libraries that inspect code based on the current syntax would need to choose between: * Updating the library to support the new syntax, or asking for a PR for that. * Saying that the library can inspect code up to the version before the new syntax was introduced, but doesn't support later code. I can see that would be annoying for the authors of those libraries, as they would obviously prefer to not have to make that choice, but it isn't a BC break.
> *snip - lots of difficult to solve technical problems* > > This is the exact same set of problems we had with the typed properties > RFC, and which caused the RFC to fail: please take all these in > consideration while designing the feature.
Yup. I think any discussion without at least some kind of plan for dealing with the technical problems for implementing this isn't likely to be productive. cheers Dan

Marco Pivetta

9 years ago
Hey Dan, On Thu, Jun 29, 2017 at 12:07 PM, Dan Ackroyd <danack@basereality.com> wrote:
> On 29 June 2017 at 05:20, Marco Pivetta <ocramius@gmail.com> wrote: > > > References to properties ...is effectively a BC break for anything > > using reflection and closure scope binding > > No it's not. > > If that feature was brought into PHP, any code that is currently > valid, that the library is capable of reflecting, would still be valid > code, that the library would still be capable of reflecting. >
We discussed this before: any library designed around the capability of dealing with a generic object would break in these scenarios, which is why I always bring it up. It needs to be considered in an RFC, including possible mitigation and alternatives. Marco Pivetta http://twitter.com/Ocramius http://ocramius.github.com/

Kalle Sommer Nielsen

9 years ago
Hi 2017-06-28 20:46 GMT+02:00 David Rodrigues <david.proweb@gmail.com>:
> The "final" keyworks make a "local scope" variable value "blocked to > rewrite" after instantiate it. > Okay, it sounds like a "const", and it is, but "not as we known it".
I get that, but I still don't understand why you would forcefully need it to be a variable still then if you know the value is gonna be constant, of course besides global visibility or in iterations
-- regards, Kalle Sommer Nielsen kalle@php.net

lists@rhsoft.net

9 years ago
Am 29.06.2017 um 04:50 schrieb Kalle Sommer Nielsen:
> 2017-06-28 20:46 GMT+02:00 David Rodrigues <david.proweb@gmail.com>: >> The "final" keyworks make a "local scope" variable value "blocked to >> rewrite" after instantiate it. >> Okay, it sounds like a "const", and it is, but "not as we known it". > > I get that, but I still don't understand why you would forcefully need > it to be a variable still then if you know the value is gonna be > constant, of course besides global visibility or in iterations
because constants are expensive in PHP when "define()" is a function call and "const" is very limited for no good reason "no good reason" because if it really would be compile time the following won't work and so why can't you use 'const' within a if-statement when you in fact can CONCAT two with define() set constant which are part of a if-statement themself ____________________________________________________ if(PHP_SAPI !== 'cli') { define('MY_PHP_SELF', $_SERVER['SCRIPT_NAME']); define('rh_serverurl', PROTOCOL_PREFIX . MY_SERVER_NAME . $rh_port); } else { define('MY_PHP_SELF', '/' . basename($_SERVER['SCRIPT_NAME'])); define('rh_serverurl', 'http://localhost'); } const rh_phpself = rh_serverurl . MY_PHP_SELF;

Marco Pivetta

9 years ago
Final is about having immutable data. Immutable doesn't mean that it's a system wide constant, it means that it's referentially pure in its scope. On 29 Jun 2017 11:05 AM, "lists@rhsoft.net" <lists@rhsoft.net> wrote:

lists@rhsoft.net

9 years ago
Am 29.06.2017 um 11:08 schrieb Marco Pivetta:
> Final is about having immutable data. Immutable doesn't mean that it's a > system wide constant, it means that it's referentially pure in its scope.
i refered to "why you would forcefully need it to be a variable still then if you know the value is gonna be constant" and as long constants in PHP are a) very expensive and b) "const" pretends to be compile time which is provable wrong with my sample code are terrible to use in other languages like Visual Basic constants are fast, in PHP they are slow, both in define and access

Marco Pivetta

9 years ago
On Thu, Jun 29, 2017 at 11:19 AM, lists@rhsoft.net <lists@rhsoft.net> wrote:
> in other languages like Visual Basic constants are fast, in PHP they are > slow, both in define and access >
Two things here: 1. don't ever consider visual basic for any comparison of any sort: it's basically (ha!) the worst example of a programming language that I can think of before malborge 2. speed is not relevant in this scope: program correctness is. Final properties would allow switching value object representations from accessor (getter) based logic (extremely slow) to public property based (less overhead, also in writing). Marco Pivetta http://twitter.com/Ocramius http://ocramius.github.com/

lists@rhsoft.net

9 years ago
Am 29.06.2017 um 11:25 schrieb Marco Pivetta:
> On Thu, Jun 29, 2017 at 11:19 AM, lists@rhsoft.net > <mailto:lists@rhsoft.net> <lists@rhsoft.net <mailto:lists@rhsoft.net>> > wrote: > > in other languages like Visual Basic constants are fast, in PHP they > are slow, both in define and access > > > Two things here: > > 1. don't ever consider visual basic for any comparison of any sort: > it's basically (ha!) the worst example of a programming language that I > can think of before malborge
stop it - many people say the same about PHP
> 2. speed is not relevant in this scope: program correctness is. Final > properties would allow switching value object representations from > accessor (getter) based logic (extremely slow) to public property based > (less overhead, also in writing)
in which scope is speed not relevant? depends on your application, in my scopes i try to avoid expensive opcodes and useless function calls, program correctness is a completly different thing and it's not one or the other but both

David Rodrigues

9 years ago
I will try answer the questions:
> I get that, but I still don't understand why you would forcefully need > it to be a variable still then if you know the value is gonna be > constant, of course besides global visibility or in iterations
"final" is not "const". "final" mean a initial state that will never be modified, but it could be initialized with different values on same runtime. It is like I say "my name is David and will be all the time David when I am on an informal context" and "my name is Rodrigues and will be all the time Rodrigues when I am on a forma context", on same "document". https://pastebin.com/QCrmrZQe
> True we could re-use the final keyword, but think about it this way, > we got final methods and final classes which means it cannot be > overridden/extended respectively, if the final keyword then would also > apply to variables but it would mean they could not be written to, > that would create a WTF-factor.
It is done on Java, for instance. We can use "final" on classes or functions that mean that it cannot be overrided, or "final" in variables, that mean that it cannot be re-referenced (rewrite after initialization). And not seems strange to me. We could "abstract the term" to understand that in any of cases (for classes, function or variables) the information could not be overrided after defined.
> The readonly keyword would work for any visibility modifiers, so > inherited classes or extending classes may read a protected property, > but not modify it as well:
For me, the readonly keyword should be implemented on future to another case: make a public property writeable only by the own class. But it is for another discussion.
> The `final` modifier would likely clash with property type definitions,
if those ever make it to the language.
> Still, a few pain-points remain: > * References to properties (you cannot reference final properties
anymore, and that is effectively a BC break for anything using reflection and closure scope binding) I don't know if I understand, but for properties you could redefine a final property on constructor (as Java). https://pastebin.com/bTZcUT33
> * Arrays and resources as properties
You can modify arrays or resources on final variables, since that you don't modify the reference. https://pastebin.com/D38wL8x7
> * Named (Factory) constructors
I don't understand.
> * Property nullability - how do you define if a property was written to
the "last valid time" before freezing it Because PHP don't have support to "variable initialization" (eg. "$name;") then you should only initializate it when you will write the initial (and freezed) value. https://pastebin.com/Ua6DFUC1
> * Unset properties and providing an alternative mechanism to distinguish
"uninitiated", "undefined" and "frozen" in he event of final properties usage, without breaking current property access semantics It will be modified. Final variables still are a variable, but with the status that "never change after initializate" (a flag). We can just implement a new reflection method to properties to identify if it isFinal(), or even for variables like "is_final($var)". By other side, this last doesn't make sense because the final variable is just a "code documentation" to the own dev knows that is not allowed modify it anymore. Maybe we need implements that only for ReflectionProperty (first case), because it could be part of a code that the user have not modify access (eg. vendor code). https://pastebin.com/jSjNQACd 2017-06-29 6:25 GMT-03:00 Marco Pivetta <ocramius@gmail.com>:
> On Thu, Jun 29, 2017 at 11:19 AM, lists@rhsoft.net <lists@rhsoft.net> > wrote: > > > in other languages like Visual Basic constants are fast, in PHP they are > > slow, both in define and access > > > > Two things here: > > 1. don't ever consider visual basic for any comparison of any sort: it's > basically (ha!) the worst example of a programming language that I can > think of before malborge > 2. speed is not relevant in this scope: program correctness is. Final > properties would allow switching value object representations from accessor > (getter) based logic (extremely slow) to public property based (less > overhead, also in writing). > > Marco Pivetta > > http://twitter.com/Ocramius > > http://ocramius.github.com/ >
-- David Rodrigues

Marco Pivetta

9 years ago
Hi David, On Thu, Jun 29, 2017 at 11:58 AM, David Rodrigues <david.proweb@gmail.com> wrote:
> > The `final` modifier would likely clash with property type definitions, > if those ever make it to the language. > > Still, a few pain-points remain: > > * References to properties (you cannot reference final properties > anymore, and that is effectively a BC break for anything using reflection > and closure scope binding) > > I don't know if I understand, but for properties you could redefine a > final property on constructor (as Java). https://pastebin.com/bTZcUT33 >
No, the problem is following: class Foo { final public $bar = 'baz'; } $foo = new Foo; $bar = & $foo->bar; $bar = 'taz'; var_dump($foo->bar); This kind of ugly code will break.
> * Arrays and resources as properties > > You can modify arrays or resources on final variables, since that you > don't modify the reference. https://pastebin.com/D38wL8x7 >
That doesn't make it "final", although I get your point, and yes, it basically prevents decreasing the reference count on that particular property. I think this kills the usefulness of the feature, but it's indeed still useful.
> > * Named (Factory) constructors > > I don't understand. >
Relatively common pattern: class Foo { private final $bar; private function __construct() { /* hammertime */ } public static function create($something) : self { $instance = new self(); $instance->bar = $something; return $instance; } } See also http://verraes.net/2014/06/named-constructors-in-php/
> > * Property nullability - how do you define if a property was written to > the "last valid time" before freezing it > > Because PHP don't have support to "variable initialization" (eg. "$name;") > then you should only initializate it when you will write the initial (and > freezed) value. https://pastebin.com/Ua6DFUC1 >
Got it, so a "first write" would be equivalent to a freeze operation? When do you seal the property if the value isn't overwritten? Would you disable default values for properties that are declared `final`?
> > > * Unset properties and providing an alternative mechanism to > distinguish "uninitiated", "undefined" and "frozen" in he event of final > properties usage, without breaking current property access semantics > > It will be modified. Final variables still are a variable, but with the > status that "never change after initializate" (a flag). We can just > implement a new reflection method to properties to identify if it > isFinal(), or even for variables like "is_final($var)". By other side, this > last doesn't make sense because the final variable is just a "code > documentation" to the own dev knows that is not allowed modify it anymore. > Maybe we need implements that only for ReflectionProperty (first case), > because it could be part of a code that the user have not modify access > (eg. vendor code). https://pastebin.com/jSjNQACd >
The reflection API is a given, but the problem is still the same: class Foo { private final $foo; private $lazyLoad; public function saySomething() { return 'Saying ' . $this->foo; } private function __construct() { } public static function new() : self { $instance = new self(); $instance->foo = 'something'; return $instance; } public static function newLazy() : self { $instance = new self(); $instance->lazyLoad = function () use ($instance) { $instance->foo = 'something lazy'; }; unset($instance->foo); return $instance; } public function __get($name) { ($this->lazyLoad)(); return $this->foo; } } $foo = Foo::new(); var_dump($foo->saySomething()); $lazyFoo = Foo::newLazy(); var_dump($lazyFoo->saySomething()); https://3v4l.org/UPVWF Hope that clarifies it. Marco Pivetta http://twitter.com/Ocramius http://ocramius.github.com/