# [POC] Support calling functions from constant expressions #5139

Open
wants to merge 3 commits into
from
Open

# [POC] Support calling functions from constant expressions#5139

wants to merge 3 commits into from
+797 −4

## Conversation

Contributor

### TysonAndre commented Feb 1, 2020 • edited

 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() (The json extension can be disabled), or calls which aren't unambiguously resolved with \ or use 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 Defaults of static properties, but not instance properties, due to changes required to the PHP internals expanding the scope of this RFC too much. Parameter defaults Global constants and class constants Defaults of static variables
force-pushed the TysonAndre:call-in-const branch 2 times, most recently from bd34c05 to f19c88a Feb 9, 2020
reviewed

 In the future, const X = [my_call()->x]; may be possible, this will be interesting Sir.
added 2 commits Jan 25, 2020
 [RFC] Support a calling whitelist of functions from most constant exp… 
 da33416 
…ressions

This is the implementation for
https://wiki.php.net/rfc/calls_in_constant_expressions

I can think of two main approaches to adding function call
support to PHP. This implements the former.

1. 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() (The json extension can be disabled),
or calls which aren't unambiguously resolved with \ or use function.

2. 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.

-------

- 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.
- TODO: Forbid backtick string syntax for shell_exec, which gets converted to
a ZEND_AST_CALL node.

-------

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

- Defaults of static properties, but not instance properties,
due to changes required to the PHP internals expanding the scope
of this RFC too much.
- Parameter defaults
- Global constants and class constants
- Defaults of static variables

Forbid use of method names as literals for function calls
 Use a whitelist instead, fix reference counting 
 8078028 
 [skip ci] fix phpt test 
 f7477b0