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
8 changes: 8 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ PHP NEWS
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
?? ??? ????, PHP 8.6.0RC1

- Core:
. Fixed incorrect internal pointer and foreach iterator positions when
compacting arrays with holes. (Weilin Du)

- DOM:
. Fixed use-after-free when re-constructing a DOMXPath whose php:function
registrations are freed while still reachable from the cycle collector.
Expand All @@ -26,6 +30,10 @@ PHP NEWS
. Fixed bug GH-23242 (PHP development server does not support Expect
100-continue flow control). (Sjoerd Langkemper)

- Calendar:
. Fixed *tojd() functions and cal_to_jd() truncating arguments outside the
int range instead of rejecting them. (lacatoire)

- DOM:
. Fixed NamedNodeMap::getNamedItemNS() with an empty URI not matching
the null namespace in spec-following mode. (Ilia Alshanetsky)
Expand Down
45 changes: 45 additions & 0 deletions Zend/tests/array_dup_internal_pointer_hole.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
--TEST--
Array duplication relocates an internal pointer on a hole, with and without foreach iterators
--FILE--
<?php
function duplicate(array &$values): void {
$copy = $values;
$values['i'] = 18;
echo 'current: ', key($values), '=>', current($values), "\n";
next($values);
echo 'next: ', key($values), '=>', current($values), "\n";
echo 'copy current: ', key($copy), '=>', current($copy), "\n";
echo 'copy keys: ', implode(' ', array_keys($copy)), "\n";
}

foreach ([false, true] as $withIterator) {
echo $withIterator ? "With iterator:\n" : "Without iterator:\n";
$values = ['a' => 10, 'b' => 11, 'c' => 12, 'd' => 13,
'e' => 14, 'f' => 15, 'g' => 16, 'h' => 17];
next($values);
next($values);
// Leave the internal pointer on a hole before several surviving elements.
unset($values['a'], $values['b'], $values['c'], $values['d']);

if ($withIterator) {
foreach ($values as &$value) {
duplicate($values);
break;
}
unset($value);
} else {
duplicate($values);
}
}
?>
--EXPECT--
Without iterator:
current: e=>14
next: f=>15
copy current: e=>14
copy keys: e f g h
With iterator:
current: e=>14
next: f=>15
copy current: e=>14
copy keys: e f g h
23 changes: 23 additions & 0 deletions Zend/tests/array_dup_iterator_past_end.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
--TEST--
Array duplication preserves past-the-end iterators when compacting holes
--FILE--
<?php
$values = ['a' => 10, 'b' => 11, 'c' => 12, 'd' => 13];
unset($values['a'], $values['b']);

foreach ($values as $key => &$value) {
echo "$key=>$value\n";
if ($key === 'd') {
// The iterator is one past the end; COW compacts the preceding holes.
$copy = $values;
$values['e'] = 14;
}
}
unset($value);
echo 'copy: ', implode(' ', array_keys($copy)), "\n";
?>
--EXPECT--
c=>12
d=>13
e=>14
copy: c d
38 changes: 38 additions & 0 deletions Zend/tests/array_dup_multiple_iterators.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
--TEST--
Array duplication updates iterators at both a hole and the next defined element
--FILE--
<?php
function test(array $values): void {
$outerVisits = [];
$innerVisits = [];
$first = true;
foreach ($values as $outerKey => &$outerValue) {
$outerVisits[] = "$outerKey=>$outerValue";
if ($first) {
$first = false;
foreach ($values as $innerKey => &$innerValue) {
$innerVisits[] = "$innerKey=>$innerValue";
if ($innerValue === 11) {
// The outer cursor is at a hole, the inner at the next value.
unset($values['a'], $values['b']);
$copy = $values;
// Trigger copy-on-write duplication, which compacts the holes.
$values['i'] = 18;
}
}
unset($innerValue);
}
}
unset($outerValue);
echo 'outer: ', implode(' ', $outerVisits), "\n";
echo 'inner: ', implode(' ', $innerVisits), "\n";
echo 'copy: ', implode(' ', array_keys($copy)), "\n";
}

test(['a' => 10, 'b' => 11, 'c' => 12, 'd' => 13,
'e' => 14, 'f' => 15, 'g' => 16, 'h' => 17]);
?>
--EXPECT--
outer: a=>10 c=>12 d=>13 e=>14 f=>15 g=>16 h=>17 i=>18
inner: a=>10 b=>11 c=>12 d=>13 e=>14 f=>15 g=>16 h=>17 i=>18
copy: c d e f g h
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
--TEST--
Using "_" as a class name alias is deprecated
--FILE--
<?php

class_alias('stdClass', '_');

class_alias('stdClass', 'Foo\\_');

?>
--EXPECTF--
Deprecated: Using "_" as a class alias is deprecated since 8.4 in %s on line %d

Deprecated: Using "_" as a class alias is deprecated since 8.4 in %s on line %d
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
--TEST--
Using "_" as a class name in use statements is deprecated
--FILE--
<?php

namespace Foo\Bar {
use stdClass as _;
}

namespace {
use stdClass as _;
}

?>
--EXPECTF--
Deprecated: Using "_" as a class name is deprecated in %s on line %d

Deprecated: Using "_" as a class name is deprecated in %s on line %d
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
--TEST--
Using "_" as a constant name is deprecated
--FILE--
<?php

namespace Foo\Bar {
const _ = 5;
}

namespace {
const _ = 5;

define('_', 'x');

define('foo\\_', 'x');
}

?>
--EXPECTF--
Deprecated: Calling a constant "_" is deprecated since 8.6 in %s on line %d

Deprecated: Calling a constant "_" is deprecated since 8.6 in %s on line %d

Deprecated: Calling a constant "_" is deprecated since 8.6 in %s on line %d

Warning: Constant _ already defined, this will be an error in PHP 9 in %s on line %d

Deprecated: Calling a constant "_" is deprecated since 8.6 in %s on line %d
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
--TEST--
Using "_" as a constant in use statements is deprecated
--FILE--
<?php

namespace Foo\Bar {
use const PHP_INT_MAX as _;
}

namespace {
use const PHP_INT_MAX as _;
}

?>
--EXPECTF--
Deprecated: Using "_" as a constant name is deprecated since 8.6 in %s on line %d

Deprecated: Using "_" as a constant name is deprecated since 8.6 in %s on line %d
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
--TEST--
Using "_" as a function name is NOT deprecated
--SKIPIF--
<?php
if (extension_loaded('gettext')) {
die("skip gettext extension defines the _ function");
}
?>
--FILE--
<?php

namespace Foo\Bar {
function _() {}
}

namespace {
function _() {}
echo "OK";
}

?>
--EXPECT--
OK
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
--TEST--
Using "_" as a function in use statements is NOT deprecated
--FILE--
<?php

namespace Foo\Bar {
use function strlen as _;
}

namespace {
use function strlen as _;

echo "OK";
}

?>
--EXPECT--
OK
40 changes: 40 additions & 0 deletions Zend/tests/rehash_multiple_iterators.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
--TEST--
Rehashing updates iterators at both a hole and the next defined element
--FILE--
<?php
function test(array $values, $firstKey, $secondKey, $newKey, int $newValue): void {
$outerVisits = [];
$innerVisits = [];
$first = true;
foreach ($values as $outerKey => &$outerValue) {
$outerVisits[] = "$outerKey=>$outerValue";
if ($first) {
$first = false;
foreach ($values as $innerKey => &$innerValue) {
$innerVisits[] = "$innerKey=>$innerValue";
if ($innerValue === 11) {
// The outer cursor is at a hole, the inner at the next value.
unset($values[$firstKey], $values[$secondKey]);
$values[$newKey] = $newValue;
}
}
unset($innerValue);
}
}
unset($outerValue);
echo 'outer: ', implode(' ', $outerVisits), "\n";
echo 'inner: ', implode(' ', $innerVisits), "\n";
}

// Adding a string key converts packed storage and compacts its holes.
test([10, 11, 12, 13, 14], 0, 1, 'new', 15);

// Inserting into a full mixed table compacts its holes without growing it.
test(['a' => 10, 'b' => 11, 'c' => 12, 'd' => 13,
'e' => 14, 'f' => 15, 'g' => 16, 'h' => 17], 'a', 'b', 'i', 18);
?>
--EXPECT--
outer: 0=>10 2=>12 3=>13 4=>14 new=>15
inner: 0=>10 1=>11 2=>12 3=>13 4=>14 new=>15
outer: a=>10 c=>12 d=>13 e=>14 f=>15 g=>16 h=>17 i=>18
inner: a=>10 b=>11 c=>12 d=>13 e=>14 f=>15 g=>16 h=>17 i=>18
16 changes: 15 additions & 1 deletion Zend/zend_compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ void zend_assert_valid_class_name(const zend_string *name, const char *type) /*
zend_error_noreturn(E_COMPILE_ERROR,
"Cannot use \"%s\" as %s as it is reserved", ZSTR_VAL(name), type);
}
if (zend_string_equals_literal(name, "_")) {
if (zend_string_equals_literal(name, "_") || zend_string_ends_with_literal(name, "\\_")) {
zend_error(E_DEPRECATED, "Using \"_\" as %s is deprecated since 8.4", type);
}
}
Expand Down Expand Up @@ -10213,6 +10213,20 @@ static void zend_compile_use(zend_ast *ast) /* {{{ */
"is a special class name", ZSTR_VAL(old_name), ZSTR_VAL(new_name), ZSTR_VAL(new_name));
}

if (zend_string_equals(new_name, ZSTR_CHAR('_'))) {
switch (type) {
case ZEND_SYMBOL_CLASS:
zend_error(E_DEPRECATED, "Using \"_\" as a class name is deprecated");
break;
case ZEND_SYMBOL_CONST:
zend_error(E_DEPRECATED, "Using \"_\" as a constant name is deprecated since 8.6");
break;
case ZEND_SYMBOL_FUNCTION:
break;
default: ZEND_UNREACHABLE();
}
}

if (current_ns) {
zend_string *ns_name = zend_string_alloc(ZSTR_LEN(current_ns) + 1 + ZSTR_LEN(new_name), 0);
zend_str_tolower_copy(ZSTR_VAL(ns_name), ZSTR_VAL(current_ns), ZSTR_LEN(current_ns));
Expand Down
6 changes: 6 additions & 0 deletions Zend/zend_constants.c
Original file line number Diff line number Diff line change
Expand Up @@ -538,6 +538,12 @@ ZEND_API zend_constant *zend_register_constant(zend_constant *c)

c->attributes = NULL;

if (
zend_string_equals(name, ZSTR_CHAR('_'))
|| (slash && zend_string_ends_with_literal(name, "\\_"))
) {
zend_error(E_DEPRECATED, "Calling a constant \"_\" is deprecated since 8.6");
}
/* Check if the user is trying to define any special constant */
if (zend_string_equals_literal(name, "__COMPILER_HALT_OFFSET__")
|| (!persistent && zend_get_special_const(ZSTR_VAL(name), ZSTR_LEN(name)))
Expand Down
10 changes: 6 additions & 4 deletions Zend/zend_hash.c
Original file line number Diff line number Diff line change
Expand Up @@ -1420,7 +1420,7 @@ ZEND_API void ZEND_FASTCALL zend_hash_rehash(HashTable *ht)
do {
zend_hash_iterators_update(ht, iter_pos, j);
iter_pos = zend_hash_iterators_lower_pos(ht, iter_pos + 1);
} while (iter_pos < i);
} while (iter_pos <= i);
}
q++;
j++;
Expand Down Expand Up @@ -2416,7 +2416,7 @@ static zend_always_inline uint32_t zend_array_dup_elements(const HashTable *sour
if (EXPECTED(!HT_HAS_ITERATORS(target))) {
while (p != end) {
if (zend_array_dup_element(source, target, target_idx, p, q, false, static_keys, with_holes)) {
if (source->nInternalPointer == idx) {
if (UNEXPECTED(target->nInternalPointer > target_idx && target->nInternalPointer <= idx)) {
target->nInternalPointer = target_idx;
}
target_idx++; q++;
Expand All @@ -2429,19 +2429,21 @@ static zend_always_inline uint32_t zend_array_dup_elements(const HashTable *sour

while (p != end) {
if (zend_array_dup_element(source, target, target_idx, p, q, false, static_keys, with_holes)) {
if (source->nInternalPointer == idx) {
if (UNEXPECTED(target->nInternalPointer > target_idx && target->nInternalPointer <= idx)) {
target->nInternalPointer = target_idx;
}
if (UNEXPECTED(idx >= iter_pos)) {
do {
zend_hash_iterators_update(target, iter_pos, target_idx);
iter_pos = zend_hash_iterators_lower_pos(target, iter_pos + 1);
} while (iter_pos < idx);
} while (iter_pos <= idx);
}
target_idx++; q++;
}
idx++; p++;
}
/* Move past-the-end iterators so they can pick up newly appended elements. */
_zend_hash_iterators_update(target, source->nNumUsed, target_idx);
}
return target_idx;
}
Expand Down
2 changes: 1 addition & 1 deletion ext/calendar/calendar.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ enum cal_name_type_t {
CAL_NUM_CALS
};

typedef zend_long (*cal_to_jd_func_t) (int month, int day, int year);
typedef zend_long (*cal_to_jd_func_t) (zend_long year, zend_long month, zend_long day);
typedef void (*cal_from_jd_func_t) (zend_long jd, int *year, int *month, int *day);
typedef char *(*cal_as_string_func_t) (int year, int month, int day);

Expand Down
Loading