Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,12 @@ PHP NEWS
TCP_USER_TIMEOUT, and SO_LINGER options. (Weilin Du)
. Fixed various memory related issues in ext/sockets. (David Carlier)

- Standard:
. Fixed setlocale() to reject locale names containing NUL bytes instead of
silently truncating them, and to reject arrays passed after the $locales
argument or additional arguments passed after an array $locales argument.
(Weilin Du)

- Streams:
. Added a new IO copy API used by php_stream_copy_to_stream_ex() that
leverages platform primitives (sendfile, splice, copy_file_range,
Expand Down
6 changes: 6 additions & 0 deletions UPGRADING
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,12 @@ PHP 8.6 UPGRADE NOTES
bytes.
. parse_str() now raises a ValueError when the $string argument contains NUL
bytes.
. setlocale() now raises a ValueError when a locale name contains NUL bytes,
instead of silently truncating it.
Arrays are now accepted only for the $locales argument. Passing an array as
a later variadic locale argument now throws a TypeError. Passing any
additional locale arguments when $locales is an array now throws an
ArgumentCountError.
. linkinfo() now raises a ValueError when the $path argument is empty.
. pathinfo() now raises a ValueError when an invalid $flag argument value is
passed.
Expand Down
3 changes: 2 additions & 1 deletion Zend/Optimizer/compact_literals.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "zend_execute.h"
#include "zend_vm.h"
#include "zend_extensions.h"
#include "zend_partial.h"

#define DEBUG_COMPACT_LITERALS 0

Expand Down Expand Up @@ -747,7 +748,7 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx
}
break;
case ZEND_CALLABLE_CONVERT_PARTIAL:
opline->op1.num = cache_size;
opline->extended_value = cache_size | (opline->extended_value & ZEND_PARTIAL_FLAGS);
cache_size += 2 * sizeof(void *);
break;
}
Expand Down
28 changes: 28 additions & 0 deletions Zend/Optimizer/dfa_pass.c
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,34 @@ static uint32_t zend_dfa_optimize_calls(zend_op_array *op_array, zend_ssa *ssa)
}
}
}

if (call_info->caller_call_opline && call_info->caller_call_opline->opcode == ZEND_CALLABLE_CONVERT_PARTIAL) {
/* Build a bitset of constant pre-bound PFA args: These are args whose value is alway the same for all
* instances of a PFA. */
uint32_t const_args = 0;
for (uint32_t i = 0, l = MIN(sizeof(const_args)*CHAR_BIT, call_info->num_args); i < l; i++) {
zend_op *send_opline = call_info->arg_info[i].opline;
if (send_opline->op1_type == IS_CONST) {
zval *value = CT_CONSTANT_EX(op_array, send_opline->op1.constant);
if (Z_TYPE_P(value) == IS_CONSTANT_AST) {
/* Const exprs can evaluate to non-const zvals (e.g. objects), and are not idempotent */
continue;
}
const_args |= (UINT32_C(1) << i);
}
}

/* Pass the bitset to the ZEND_CALLABLE_CONVERT_PARTIAL opline. */
zend_op *call_opline = call_info->caller_call_opline;
if (call_opline->op2_type == IS_UNUSED) {
call_opline->op2.num = const_args;
} else {
ZEND_ASSERT(call_opline->op2_type == IS_CONST);
zval *zv = CT_CONSTANT_EX(op_array, call_opline->op2.constant);
Z_EXTRA_P(zv) = const_args;
}
}

call_info = call_info->next_callee;
} while (call_info);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,6 @@ test('Proxy', $obj);
?>
--EXPECT--
# Ghost
ReflectionException: Can not use setRawValueWithoutLazyInitialization on dynamic property C::$dyn
ReflectionException: Cannot use setRawValueWithoutLazyInitialization() on dynamic property C::$dyn
# Proxy
ReflectionException: Can not use setRawValueWithoutLazyInitialization on dynamic property C::$dyn
ReflectionException: Cannot use setRawValueWithoutLazyInitialization() on dynamic property C::$dyn
16 changes: 8 additions & 8 deletions Zend/tests/lazy_objects/skipLazyInitialization.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -198,10 +198,10 @@ getValue(): string(5) "value"
## Property [ public static $static = 'static' ]

skipInitializerForProperty():
ReflectionException: Can not use skipLazyInitialization on static property A::$static
ReflectionException: Cannot use skipLazyInitialization() on static property A::$static

setRawValueWithoutLazyInitialization():
ReflectionException: Can not use setRawValueWithoutLazyInitialization on static property A::$static
ReflectionException: Cannot use setRawValueWithoutLazyInitialization() on static property A::$static

## Property [ public $noDefault = NULL ]

Expand Down Expand Up @@ -238,10 +238,10 @@ getValue(): string(5) "value"
## Property [ public virtual $virtual { get; set; } ]

skipInitializerForProperty():
ReflectionException: Can not use skipLazyInitialization on virtual property A::$virtual
ReflectionException: Cannot use skipLazyInitialization() on virtual property A::$virtual

setRawValueWithoutLazyInitialization():
ReflectionException: Can not use setRawValueWithoutLazyInitialization on virtual property A::$virtual
ReflectionException: Cannot use setRawValueWithoutLazyInitialization() on virtual property A::$virtual

## Property [ $dynamicProp ]

Expand Down Expand Up @@ -295,10 +295,10 @@ getValue(): string(5) "value"
## Property [ public static $static = 'static' ]

skipInitializerForProperty():
ReflectionException: Can not use skipLazyInitialization on static property A::$static
ReflectionException: Cannot use skipLazyInitialization() on static property A::$static

setRawValueWithoutLazyInitialization():
ReflectionException: Can not use setRawValueWithoutLazyInitialization on static property A::$static
ReflectionException: Cannot use setRawValueWithoutLazyInitialization() on static property A::$static

## Property [ public $noDefault = NULL ]

Expand Down Expand Up @@ -335,10 +335,10 @@ getValue(): string(5) "value"
## Property [ public virtual $virtual { get; set; } ]

skipInitializerForProperty():
ReflectionException: Can not use skipLazyInitialization on virtual property A::$virtual
ReflectionException: Cannot use skipLazyInitialization() on virtual property A::$virtual

setRawValueWithoutLazyInitialization():
ReflectionException: Can not use setRawValueWithoutLazyInitialization on virtual property A::$virtual
ReflectionException: Cannot use setRawValueWithoutLazyInitialization() on virtual property A::$virtual

## Property [ $dynamicProp ]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,6 @@ test('Proxy', $obj);
?>
--EXPECT--
# Ghost
ReflectionException: Can not use skipLazyInitialization on dynamic property C::$dyn
ReflectionException: Cannot use skipLazyInitialization() on dynamic property C::$dyn
# Proxy
ReflectionException: Can not use skipLazyInitialization on dynamic property C::$dyn
ReflectionException: Cannot use skipLazyInitialization() on dynamic property C::$dyn
162 changes: 162 additions & 0 deletions Zend/tests/partial_application/const_arg_opt.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
--TEST--
Constant argument optimization
--DESCRIPTION--
Pre-bound arguments that are constant can be burned into the generated
op_array instead of being passed via the Closure's lexical vars.
--ENV--
A=1
--INI--
opcache.enable=1
opcache.enable_cli=1
opcache.optimization_level=-1
--FILE--
<?php

function f($a, $b) {
var_dump([$a, $b]);
}

function g() {
return 3;
}

function h($a, $b, $c) {
var_dump([$a, $b, $c]);
}

function i($a1, $a2, $a3, $a4, $a5, $a6, $a7, $a8, $a9, $a10, $a11, $a12, $a13, $a14, $a15, $a16, $a17, $a18, $a19, $a20, $a21, $a22, $a23, $a24, $a25, $a26, $a27, $a28, $a29, $a30, $a31, $a32, $a33) {
var_dump($a33);
}

function print_lexical_vars($f) {
$vars = new ReflectionFunction($f)->getClosureUsedVariables();
if ($vars === []) {
echo "no lexical vars\n";
} else {
$varNames = array_keys($vars);
echo 'lexical vars: ', implode(', ', $varNames), "\n";
}
}

echo "# Non-constant pre-bound argument:\n";
$f = f(getenv('A'), ?);
print_lexical_vars($f);
$f(2);

echo "# Constant pre-bound argument:\n";
$f = f(2, ?);
print_lexical_vars($f);
$f(2);

echo "# Constant pre-bound argument (inverted):\n";
$f = f(?, 2);
print_lexical_vars($f);
$f(1);

echo "# Inlined pre-bound argument:\n";
$f = f(g(), ?);
print_lexical_vars($f);
$f(2);

echo "# Constexpr pre-bound argument:\n";
const B = 4;
$f = f(B, ?);
print_lexical_vars($f);
$f(2);

echo "# Mixed arguments:\n";
$f = h(5, getenv('A'), ?);
print_lexical_vars($f);
$f(2);

echo "# Constant array pre-bound argument:\n";
$f = f(['foo' => 'bar'], ?);
print_lexical_vars($f);
$f(2);

echo "# Named arguments:\n";
$f = h(1, c: ?, b: ?);
print_lexical_vars($f);
$f(2, 3);

echo "# Many arguments (optimization can not be applied for all args) :\n";
$f = i(?, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33);
print_lexical_vars($f);
$f(33);

?>
--EXPECT--
# Non-constant pre-bound argument:
lexical vars: a
array(2) {
[0]=>
string(1) "1"
[1]=>
int(2)
}
# Constant pre-bound argument:
no lexical vars
array(2) {
[0]=>
int(2)
[1]=>
int(2)
}
# Constant pre-bound argument (inverted):
no lexical vars
array(2) {
[0]=>
int(1)
[1]=>
int(2)
}
# Inlined pre-bound argument:
no lexical vars
array(2) {
[0]=>
int(3)
[1]=>
int(2)
}
# Constexpr pre-bound argument:
lexical vars: a
array(2) {
[0]=>
int(4)
[1]=>
int(2)
}
# Mixed arguments:
lexical vars: b
array(3) {
[0]=>
int(5)
[1]=>
string(1) "1"
[2]=>
int(2)
}
# Constant array pre-bound argument:
no lexical vars
array(2) {
[0]=>
array(1) {
["foo"]=>
string(3) "bar"
}
[1]=>
int(2)
}
# Named arguments:
no lexical vars
array(3) {
[0]=>
int(1)
[1]=>
int(3)
[2]=>
int(2)
}
# Many arguments (optimization can not be applied for all args) :
lexical vars: a33
int(33)
54 changes: 54 additions & 0 deletions Zend/tests/partial_application/constexpr_001.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
--TEST--
PFA in constexpr 001
--FILE--
<?php

function f($a = g("foo", ?)) {
return $a(1);
}

function g(string $a, int $b) {
return [$a, $b];
}

class C {
static function f($a = self::g("foo", ?)) {
return $a(1);
}

static function g(string $a, int $b) {
return [$a, $b];
}
}

var_dump(f());
var_dump(f());
var_dump(C::f());
var_dump(C::f());

?>
--EXPECT--
array(2) {
[0]=>
string(3) "foo"
[1]=>
int(1)
}
array(2) {
[0]=>
string(3) "foo"
[1]=>
int(1)
}
array(2) {
[0]=>
string(3) "foo"
[1]=>
int(1)
}
array(2) {
[0]=>
string(3) "foo"
[1]=>
int(1)
}
22 changes: 22 additions & 0 deletions Zend/tests/partial_application/constexpr_002.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
--TEST--
PFA in constexpr: non-constexpr arg
--FILE--
<?php

function f($a = g(new stdClass, $x, ?)) {
return $a(1);
}

function g(string $a, int $b) {
return [$a, $b];
}

try {
var_dump(f());
} catch (Error $e) {
echo $e::class, ": ", $e->getMessage(), " on line ", $e->getLine();
}

?>
--EXPECTF--
Fatal error: Constant expression contains invalid operations in %s on line %d
Loading