Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[POC] Support calling functions from constant expressions #5139

Open
wants to merge 3 commits into
base: master
from

Conversation

@TysonAndre
Copy link
Contributor

TysonAndre commented Feb 1, 2020

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

  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.

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
@TysonAndre TysonAndre force-pushed the TysonAndre:call-in-const branch 2 times, most recently from bd34c05 to f19c88a Feb 9, 2020
Copy link

chamajcpradel left a comment

In the future, const X = [my_call()->x]; may be possible,

this will be interesting Sir.

TysonAndre added 2 commits Jan 25, 2020
…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
@TysonAndre TysonAndre force-pushed the TysonAndre:call-in-const branch from ce5aba5 to 8078028 Feb 14, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked issues

Successfully merging this pull request may close these issues.

None yet

2 participants
You can’t perform that action at this time.