[POC] Support calling functions from constant expressions #5139
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
I can think of two main approaches to adding function call
support to PHP. This implements the latter.
Only allow functions that are actually deterministic and don't depend on ini
settings to be used in constant declarations.
For example, allow
\count()
,\strlen()
,\array_merge()
, and\in_array()
,but possibly don't allow functions such as
strtolower()
(different in Turkish locale),sprintf() (Depends on
ini_get('precision')
, but so does(string)EXPR
),json_encode()
(Thejson
extension can be disabled),or calls which aren't unambiguously resolved with
\
oruse function
.Allow any function (user-defined or internal) to be called,
leave it to coding practice guidelines to assert that constants are only
used in safe ways.
I'd imagine most developers would want many of the functions in the former,
though there are many arguments for/against supporting the latter.
The draft RFC is https://wiki.php.net/rfc/calls_in_constant_expressions
This POC can handle fully qualified, namespace relative, and not-FQ calls.
Argument unpacking is supported
Parameter defaults are evaluated every time if the expression
contains function calls.
This handles recursive definitions.
It throws if a function call being evaluated ends up reaching the same call.
Static method calls with known classes (other than static::)
are probably easy to implement, but omitted from this RFC.
This also constrains function return values to be valid constants,
(i.e. they can be the same values that define() would accept)
and throws an Error otherwise.
In the future,
const X = [my_call()->x];
may be possible,but returning an object in any call is forbidden here.
Variables and functions such as
func_get_args()
aren't supported,because they depend on the declaration's scope. They throw an Error at runtime.
It turns out that function calls can already be invoked when evaluating a
constant, e.g. from php's error handler.
So it seems viable for PHP's engine to support regular calls,
which this POC does
(Imagine an error handler defining SOME_DYNAMIC_CONST123 to be a dynamic
value if a notice is emitted for the expression
const X = [[]['undefined_index'], SOME_DYNAMIC_CONST123][1];
)Function calls are allowed in the following types of constant expressions
due to changes required to the PHP internals expanding the scope
of this RFC too much.