rfc:arbitrary_static_variable_initializers

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
rfc:arbitrary_static_variable_initializers [2022/11/06 19:31] ilutovrfc:arbitrary_static_variable_initializers [2023/05/24 18:29] (current) – Move to implemented ilutov
Line 3: Line 3:
   * Date: 2022-11-06   * Date: 2022-11-06
   * Author: Ilija Tovilo, tovilo.ilija@gmail.com   * Author: Ilija Tovilo, tovilo.ilija@gmail.com
-  * Status: Under discussion+  * Status: Implemented
   * Target Version: PHP 8.3   * Target Version: PHP 8.3
   * Implementation: https://github.com/php/php-src/pull/9301   * Implementation: https://github.com/php/php-src/pull/9301
Line 26: Line 26:
 </code> </code>
  
-The right hand side of the assignment <php>static $i = 1;</php> must currently be a constant expressions. This means+The right hand side of the assignment <php>static $i = 1;</php> must currently be a constant expression. This means
 that it can't call functions, use parameters, amongst many other things. This limitation is hard to understand from a that it can't call functions, use parameters, amongst many other things. This limitation is hard to understand from a
 user perspective. This RFC suggests lifting this restriction by allowing the static variable initializer to contain user perspective. This RFC suggests lifting this restriction by allowing the static variable initializer to contain
Line 70: Line 70:
 </code> </code>
  
-The static variable is overridden at compile time resulting in both statements to refer to the same underlying static+The static variable is overridden at compile timeresulting in both statements referring to the same underlying static
 variable initializer. This is not useful or intuitive. The new implementation is not compatible with this behavior but variable initializer. This is not useful or intuitive. The new implementation is not compatible with this behavior but
 would instead result in the first initializer to win. Instead of switching from one dubious behavior to another, would instead result in the first initializer to win. Instead of switching from one dubious behavior to another,
Line 85: Line 85:
 ==== ReflectionFunction::getStaticVariables() ==== ==== ReflectionFunction::getStaticVariables() ====
  
-<php>ReflectionFunction::getStaticVariables()</php> can be used to inspect a functions static variables and their+<php>ReflectionFunction::getStaticVariables()</php> can be used to inspect a function'static variables and their
 current values. Currently, PHP automatically evaluates the underlying constant expression and initializes the static current values. Currently, PHP automatically evaluates the underlying constant expression and initializes the static
-variable if the function has never been called, since the initializer cannot depend on any runtime values of the +variable if the function has never been called. With this RFC this is no longer possible, as static variables may depend 
-functionThis is no longer possible with arbitrary initializers. Instead, static variables in +on values that are only known at runtime. Instead, the compiler will //attempt// to resolve the constant expression at 
-<php>ReflectionFunction::getStaticVariables()</php> will be initialized to <php>null</php>. After executing the function +compile time. If successful, the value will be embedded in the static variables table. Otherwise it will be initialized 
-and assigning to the static variables the contents of the variables will be reflectable.+to <php>null</php>. After executing the function and assigning to the static variable the contents of the variable will 
 +be reflectable through <php>ReflectionFunction::getStaticVariables()</php>.
  
 <code php> <code php>
Line 104: Line 105:
 foo(2); foo(2);
 var_dump((new ReflectionFunction('foo'))->getStaticVariables()['x']); var_dump((new ReflectionFunction('foo'))->getStaticVariables()['x']);
-// 2+// 1
 </code> </code>
  
 From the example above, it becomes more obvious why the initializer <php>$initialValue</php> cannot be evaluated before From the example above, it becomes more obvious why the initializer <php>$initialValue</php> cannot be evaluated before
 calling the function. calling the function.
- 
-//Side note:// It's been suggested that expressions that can be evaluated constantly continue to do so. This would mean that some expressions in <php>getStaticVariables</php> are evaluated and some are not. The upside is that this would only be the case for new expressions that are currently not valid, avoiding the backward incompatibility. 
  
 ===== Other semantics ===== ===== Other semantics =====
Line 159: Line 158:
 } }
  
-function foo() {+function foo($y) {
     $x = new Foo();     $x = new Foo();
-    static $x = 42;+    static $x = $y;
 } }
  
 try { try {
-    foo();+    foo(42);
 } catch (Exception) {} } catch (Exception) {}
  
Line 184: Line 183:
 function foo($i) { function foo($i) {
     static $x = $i < 3 ? foo($i + 1) : 'Done';     static $x = $i < 3 ? foo($i + 1) : 'Done';
-    var_dump($a); +    var_dump($x); 
-    return $cond;+    return $i;
 } }
  
 foo(1); foo(1);
-// string(4) "Done" // $i = 3 +// string(4) "Done"$i = 3 
-// string(4) "Done" // $i = 2 +// string(4) "Done"$i = 2 
-// string(4) "Done" // $i = 1+// string(4) "Done"$i = 1
  
 foo(5); foo(5);
-// string(4) "Done" // $i = 5, initializer not called+// string(4) "Done"$i = 5, initializer not called
 </code> </code>
 +
 +==== What initializers are known at compile-time? ====
 +
 +In the discussion the question arose whether static variables depending on other static variables are known at compile time.
 +
 +<code php>
 +function foo() {
 +   static $a = 0;
 +   static $b = $a + 1;
 +}
 +</code>
 +
 +The answer is no. In this example it's clear that <php>$a</php> holds the value <php>0</php> until the initialization of <php>$b</php>. However, that's not necessarily the case. If <php>$a</php> is modified at any point between the two initializations the initial value of <php>$b</php> also changes.
 +
 +Here's a quick explanation of how this is implemented: During compilation of static variables the initializer AST is passed to the <php>zend_eval_const_expr</php> function. It traverses the AST and tries to compile-time evaluate all nodes by evaluating their children first and then the node itself if the child nodes were successfully evaluated. If the evaluation fails the nodes stay AST nodes and will again be evaluated at runtime when more information is available (e.g. when class constants are declared). These expressions are currently considered for compile-time constant expression evaluation:
 +
 +  * Literals (strings, ints, bools, etc)
 +  * Binary operations
 +  * Binary comparisons
 +  * Unary operations
 +  * Coalesce operator
 +  * Ternary operator
 +  * Array access (<php>self::FOO['bar']</php>)
 +  * Array literals
 +  * Magic constants (e.g. <php>__FILE__</php>)
 +  * Global constants (that are known at compile time)
 +  * Class constants (that are known at compile time)
  
 ===== Vote ===== ===== Vote =====
  
-Voting starts ??? and ends ???+Voting starts 2023-03-21 and ends 2023-04-04
  
 As this is a language change, a 2/3 majority is required. As this is a language change, a 2/3 majority is required.
rfc/arbitrary_static_variable_initializers.1667763060.txt.gz · Last modified: 2022/11/06 19:31 by ilutov