* Zend: Make zend_strnlen available for use outside zend_compile
* exif: remove local php_strnlen, use zend_strnlen instead
* main: remove local strnlen, use zend_strnlen instead
* phar: remove local strnlen, use zend_strnlen
Evaluating constants at comptime can result in arrays that contain objects. This
is problematic for printing the default value of constant ASTs containing
objects, because we don't actually know what the constructor arguments were.
Avoid this by not propagating array constants.
Fixes GH-11937
Closes GH-11947
opnum_start denotes the start of the ZEND_FREE block of skipped consuming
opcodes. Storing the number before zend_compile_expr(..., label_ast) makes it
seem like it denotes the start of the label block. However, label_ast must only
be a zval string AST, and as such never results in an actual opcode.
We don't want to invoke calls twice, even if they are considered "variables",
i.e. might be writable if returning a reference. Function calls behave the same
in all BP contexts so they don't need to be invoked twice. The singular
exception to this is nullsafe coalesce in isset/empty, because it needs to
return false/true respectively when short-circuited. However, since nullsafe
calls are not allwed in write context we may ignore this problem.
Closes GH-11592
We transform the arrow function by nesting the expression into a return
statement. If we compile the arrow function twice this would be done twice,
leading to a compile assertion.
Fix oss-fuzz #60411
Closes GH-11632
Normally, PHP evaluates all expressions in offsets (property or array), as well
as the right hand side of assignments before actually fetching the offsets. This
is well explained in this blog post.
https://www.npopov.com/2017/04/14/PHP-7-Virtual-machine.html#writes-and-memory-safety
For ??= we have a bit of a problem in that the rhs must only be evaluated if the
lhs is null or undefined. Thus, we have to first compile the lhs with BP_VAR_IS,
conditionally run the rhs and then re-fetch the lhs with BP_VAR_W to to make
sure the offsets are valid if they have been invalidated.
However, we don't want to just re-evaluate the entire lhs because it may contain
side-effects, as in $array[$x++] ??= 42;. In this case, we don't want to
re-evaluate $x++ because it would result in writing to a different offset than
was previously tested. The same goes for function calls, like
$array[foo()] ??= 42;, where the second call to foo() might result in a
different value. PHP behaves correctly in these cases. This is implemented by
memoizing sub-expressions in the lhs of ??= and reusing them when compiling the
lhs for the second time. This is done for any expression that isn't a variable,
i.e. anything that can (potentially) be written to.
Unfortunately, this also means that function calls are considered writable due
to their return-by-reference semantics, and will thus not be memoized. The
expression foo()['bar'] ??= 42; will invoke foo() twice. Even worse,
foo(bar()) ??= 42; will call both foo() and bar() twice, but
foo(bar() + 1) ??= 42; will only call foo() twice. This is likely not by design,
and was just overlooked in the implementation. The RFC does not specify how
function calls in the lhs of the coalesce assignment behaves. This should
probably be improved in the future.
Now, the problem this commit actually fixes is that ??= may memoize expressions
inside assert() function calls that may not actually execute. This is not only
an issue when using the VAR in the second expression (which would usually also
be skipped) but also when freeing the VAR. For this reason, it is not safe to
memoize assert() sub-expressions.
There are two possible solutions:
1. Don't memoize any sub-expressions of assert(), meaning they will execute
twice.
2. Throw a compile error.
Option 2 is not quite simple, because we can't disallow all memoization inside
assert(), as that would break assertions like assert($array[foo()] ??= 'bar');.
Code like this is highly unlikely (and dubious) but possible. In this case, we
would need to make sure that a memoized value could not be used across the
assert boundary it was created in. The complexity for this is not worthwhile. So
we opt for option 1 and disable memoization immediately inside assert().
Fixes GH-11580
Closes GH-11581
Having this lineno on the same last compiled element can lead to an incorrectly
covered line number.
if (true) {
if (false) {
echo 'Never executed';
}
} else {
}
The echo will be reported as covered because the JMP from the if (true) branch
to the end of the else branch has the same lineno as the echo.
This is lacking a test because zend_dump.c does not have access to
ctx->debug_level and I don't think it's worth adjusting all the cases.
Closes GH-11598
Fixes GH-11388.
Following https://wiki.php.net/rfc/horizontalreuse which introduced traits,
this should be allowed.
The implementation was refactored in 3f8c729. That commit is the first time
the "final" check appears AFAICT, but no reason was given for why. That
commit seems to have landed in 5.4.11 and the NEWS for that version doesn't
seem to mention something relevant to the behaviour change.
This patch removes the restriction of the final modifier.
Closes GH-11394.
Supporting new constant expressions requires remembering to add them to
zend_eval_const_expr, even if it only evalutes its children. This is routinely
forgotten, at least by me. Use zend_ast_apply to solve this generically.
Normally, we add classes without parents (and no interfaces or traits) directly
to the class map, early binding the class. However, if the same class has
already been registered, we would instead just add a ZEND_DECLARE_CLASS
instruction and let the handler throw a duplicate class declaration exception.
However, with opcache, if on the next request the files are included in the
opposite order, we won't perform early binding. To fix this, create a
ZEND_DECLARE_CLASS_DELAYED instruction instead and handle classes without
parents accordingly, skipping any linking for classes that are already linked in
delayed early binding.
Fixes GH-8846
* PHP-8.2:
Fix GH-10983: State-dependant segfault in ReflectionObject::getProperties
Fix GH-10990: mail() throws TypeError after iterating over $additional_headers array by reference
Fix GH-8841: php-cli core dump calling a badly formed function
* PHP-8.1:
Fix GH-10990: mail() throws TypeError after iterating over $additional_headers array by reference
Fix GH-8841: php-cli core dump calling a badly formed function