## Re: [PHP-DEV] Planning an RFC to allow calls to global functions inconstant expressions

February 1, 2020 23:50
Hi!

> For example, allow \count(), \strlen(), \array_merge(), and \in_array(),
> but don't allow functions such as
> \strtolower() (different in Turkish locale),

What happens if a function like strlen() is applied to a non-string
argument? Conversion to string is certainly runtime-dependent even for
primitive types like floats.

> allow calls to all global functions

Not sure I understand here - when these calls will be happening? And if
you need a value that depends on runtime calculation, what's wrong with
using define()?

--
Stas Malyshev
smalyshev@gmail.com
February 2, 2020 00:27
> What happens if a function like strlen() is applied to a non-string
> argument? Conversion to string is certainly runtime-dependent even for
> primitive types like floats.

Good point. I thought that string casts were already allowed, but was mistaken.
It's possible to cast to string through concatenation right now, so it is a somewhat pre-existing issue.


php > const X = (string)2.3;
Fatal error: Constant expression contains invalid operations in php shell code on line 1
php > const Y = '' . 2.3;
php > var_export(Y);
'2.3'


Possible solutions:
1. Exclude functions with string parameters, strval(), strpos(), etc. from the limited set of functions in this RFC.

Subsequent RFCs can be created to vote on for adding those groups of functions.
2. Always treat parameters in calls in constants like strict_types=1 - It's doable and won't break existing code, but I don't want to add that inconsistency
3. Keep existing behavior in PR

Currently, the implementation is intended to respect the strict_types setting of the file containing the constant expression, so strpos, strlen, etc would have that issue.
I still need to add a test that the implementation is actually doing that.

> Not sure I understand here - when these calls will be happening? And if
> you need a value that depends on runtime calculation, what's wrong with
> using define()?

The constant expressions will be evaluated at the same time php currently evaluates constant
expressions.
For class constants and static variable defaults, they're evaluated once at the time of use
For global constants, they're evaluated immediately.

If you want to use the define()d constant for a class constant, that is possible right now,
but it's an additional constant that's only (ideally) used in one place,
and there's the possibility of choosing conflicting names if the code is copied.

Another problem with using define() is that opcache cannot optimize global constants,
because define() will emit a notice instead of raising an Error if the constant already exists.
So the performance is slightly worse.

And the code is slightly less clear because of the indirection.
February 2, 2020 02:23
Hi!

> The constant expressions will be evaluated at the same time php currently evaluates constant
> expressions.

But you essentially propose running arbitrary code at that time, which
is much bigger deal than evaluating simple constant expressions. While
simple functions like strlen() would probably be OK, running arbitrary
function means possibility for arbitrary side effects, which makes the
whole thing completely unpredictable.

> Another problem with using define() is that opcache cannot optimize global constants,

opcache can't also optimize runtime-evaluated constants, because results
and side-effects of an arbitrary function call is not guaranteed to be
the same. It could only optimize anything if it were guaranteed that the
function is pure, does not depend on anything but its arguments and has
no side effects.

> because define() will emit a notice instead of raising an Error if the constant already exists.

You can trivially check for this, and if it ever would be a problem,
define() error level can be changed, but I don't think it's a real issue
since it was never changed so far.

> So the performance is slightly worse.

If you try to optimize performance this way, you're optimizing
performance in a wrong way. No sane code would depend on performance of
define().
--
Stas Malyshev
smalyshev@gmail.com