There are two main motivations to this:
a) The logic for handling internal and userland observation can be unified.
b) Unwinding of observed functions on a bailout does notably not include observers. Even if users of observers were to ensure such handling themselves, it would be impossible to retain the relative ordering - either the user has to unwind all internal observed frames before the automatic unwinding (zend_observer_fcall_end_all) or afterwards, but not properly interleaved.
Signed-off-by: Bob Weinand <bobwei9@hotmail.com>
While JMPZNZ can avoid execution of a separate JMP opcode in some
cases, it also prevents smart branch optimization, so creating
JMPZNZ may actually have a negative effect. It also adds additional
complexity for optimizations.
Drop JMPZNZ in favor of JMPZ+JMP or JMPNZ+JMP.
Closes GH-7857.
Support acquiring a Closure to a callable using the syntax
func(...), $obj->method(...), etc. This is essentially a
shortcut for Closure::fromCallable().
RFC: https://wiki.php.net/rfc/first_class_callable_syntax
Closes GH-7019.
Co-Authored-By: Nikita Popov <nikita.ppv@gmail.com>
The never type can be used to indicate that a function never
returns, for example because it always unwinds.
RFC: https://wiki.php.net/rfc/noreturn_type
Closes GH-6761.
Currently, dynamically declared functions and closures are inserted
into the function table under a runtime definition key, and then later
possibly renamed. When opcache is not used and a file containing a
closure is repeatedly included, this leads to a very large memory leak,
as the no longer needed closure declarations will never be freed
(https://bugs.php.net/bug.php?id=76982).
With this patch, dynamic functions are instead stored in a
dynamic_func_defs member on the op_array, which opcodes reference
by index. When the parent op_array is destroyed, the dynamic_func_defs
it contains are also destroyed (unless they are stilled used elsewhere,
e.g. because they have been bound, or are used by a live closure). This
resolves the fundamental part of the leak, though doesn't completely
fix it yet due to some arena allocations.
The main non-obvious change here is to static variable handling:
We can't destroy static_variables_ptr in destroy_op_array, as e.g.
that would clear the static variables in a dynamic function when
the op_array containing it is destroyed. Static variable destruction
is separated out for this reason (we already do static variable
destruction separately for normal functions, so we only need to
handle main scripts).
Closes GH-5595.
This restricts allowed usage of $GLOBALS, with the effect that
plain PHP arrays can no longer contain INDIRECT elements.
RFC: https://wiki.php.net/rfc/restrict_globals_usage
Closes GH-6487.
ZEND_HANDLE_EXCEPTION might call zend_observer_fcall_end() even if exception is cought by function. The fix moved zend_observer_fcall_end() into a right place and remove OBSERVER sepecialization for ZEND_HANDLE_EXCEPTION handler.
From an engine perspective, named parameters mainly add three
concepts:
* The SEND_* opcodes now accept a CONST op2, which is the
argument name. For now, it is looked up by linear scan and
runtime cached.
* This may leave UNDEF arguments on the stack. To avoid having
to deal with them in other places, a CHECK_UNDEF_ARGS opcode
is used to either replace them with defaults, or error.
* For variadic functions, EX(extra_named_params) are collected
and need to be freed based on ZEND_CALL_HAS_EXTRA_NAMED_PARAMS.
RFC: https://wiki.php.net/rfc/named_params
Closes GH-5357.
This reverts commit bb43a3822e.
After thinking about this a bit more, this is now going to be
a complete solution for the "readonly properties" case, for example:
unset($foo->readOnly->bar);
should also be legal and
$foo->readOnly['bar'] = 42;
should also be legal if $foo->readOnly is not an array but an
ArrayAccess object.
I think it may be better to distinguish better on the BP_VAR flag
level. Reverting for now.
$a->b->c = 'd';
is now compiled the same way as
$b = $a->b;
$b->c = 'd';
That is, we perform a read fetch on $a->b, rather than a write
fetch.
This is possible, because PHP 8 removed auto-vivification support
for objects, so $a->b->c = 'd' may no longer modify $a->b proper
(i.e. not counting interior mutability of the object).
Closes GH-5250.
Add a specialized opcode handler to use for `===`/`!==` when:
1. At least one side is a $cv, and the other is a $cv or CONST
(avoids the need to free operands)
2. Neither operand can be undefined or a reference
(avoids the need for error handling and dereferencing)
```
// Elapsed time decreased from 0.275 seconds to 0.243 seconds in combination
// with PR #4982
function count_same(array $values) {
$same = 0;
foreach ($values as $x) {
foreach ($values as $y) {
if ($y === $x) {
$same++;
}
}
}
return $same;
}
$values = range(0, 5000);
$values[] = new stdClass();
$values[] = null;
$values[] = 3;
$start = microtime(true);
$total = count_same($values);
```
Make sure we deref the OBJ_IS result, because we store it in a TMP
var, which is not allowed to contain references and will cause
assertion failures in the unspecialized VM.
This also partially reverts fd463a9a60,
which merged the TMP and VAR specializations of COALESCE to work
around this bug.
An alternative would be to change the result type of OBJ_IS back
to VAR.
RFC: https://wiki.php.net/rfc/null_coalesce_equal_operator
$a ??= $b is $a ?? ($a = $b), with the difference that $a is only
evaluated once, to the degree that this is possible. In particular
in $a[foo()] ?? $b function foo() is only ever called once.
However, the variable access themselves will be reevaluated.
RFC: https://wiki.php.net/rfc/typed_properties_v2
This is a squash of PR #3734, which is a squash of PR #3313.
Co-authored-by: Bob Weinand <bobwei9@hotmail.com>
Co-authored-by: Joe Watkins <krakjoe@php.net>
Co-authored-by: Dmitry Stogov <dmitry@zend.com>