diff --git a/NEWS b/NEWS
index b6ae41f72386d..661be47ada6e3 100644
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,66 @@
PHP NEWS
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
-?? ??? ????, PHP 8.2.1
+02 Feb 2023, PHP 8.2.2
+
+- Core:
+ . Fixed bug GH-10200 (zif_get_object_vars:
+ Assertion `!(((__ht)->u.flags & (1<<2)) != 0)' failed). (nielsdos)
+ . Fix GH-10251 (Assertion `(flag & (1<<3)) == 0' failed). (nielsdos)
+ . Fix GH-10240 (Assertion failure when adding more than 2**30 elements to an
+ unpacked array). (Arnaud)
+ . Fix GH-9735 (Fiber stack variables do not participate in cycle collector).
+ (Arnaud)
+ . Fix GH-9675 (Broken run_time_cache init for internal enum methods).
+ (Petar Obradović, Bob)
+
+- FPM:
+ . Fixed bug #77106 (Missing separator in FPM FastCGI errors). (Jakub Zelenka)
+ . Fixed bug GH-9981 (FPM does not reset fastcgi.error_header).
+ (Jakub Zelenka)
+ . Fixed bug #68591 (Configuration test does not perform UID lookups).
+ (Jakub Zelenka)
+ . Fixed memory leak when running FPM config test. (Jakub Zelenka)
+ . Fixed bug #67244 (Wrong owner:group for listening unix socket).
+ (Jakub Zelenka)
+
+- Hash:
+ . Handle exceptions from __toString in XXH3's initialization (nielsdos)
+
+- LDAP:
+ . Fixed bug GH-10112 (LDAP\Connection::__construct() refers to ldap_create()).
+ (cmb)
+
+- Opcache:
+ . Fix inverted bailout value in zend_runtime_jit() (Max Kellermann).
+ . Fix access to uninitialized variable in accel_preload(). (nielsdos)
+ . Fix zend_jit_find_trace() crashes. (Max Kellermann)
+ . Added missing lock for EXIT_INVALIDATE in zend_jit_trace_exit. (Max Kellermann)
+
+- Phar:
+ . Fix wrong flags check for compression method in phar_object.c (nielsdos)
+
+- PHPDBG:
+ . Fix undefined behaviour in phpdbg_load_module_or_extension(). (nielsdos)
+ . Fix NULL pointer dereference in phpdbg_create_conditional_breal(). (nielsdos)
+ . Fix GH-9710: phpdbg memory leaks by option "-h" (nielsdos)
+ . Fix phpdbg segmentation fault in case of malformed input (nielsdos)
+
+- Posix:
+ . Fix memory leak in posix_ttyname() (girgias)
+
+- Random:
+ . Fixed bug GH-10247 (Theoretical file descriptor leak for /dev/urandom). (timwolla)
+
+- Standard:
+ . Fix GH-10187 (Segfault in stripslashes() with arm64). (nielsdos)
+ . Fixed bug GH-10214 (Incomplete validation of object syntax during
+ unserialize()). (timwolla)
+ . Fix substr_replace with slots in repl_ht being UNDEF. (nielsdos)
+
+- XMLWriter
+ . Fix missing check for xmlTextWriterEndElement (nielsdos)
+
+05 Jan 2023, PHP 8.2.1
- Core:
. Fixed bug GH-9905 (constant() behaves inconsistent when class is undefined).
@@ -51,6 +111,10 @@ PHP NEWS
. Fixed bug GH-9971 (Incorrect NUMERIC value returned from PDO_Firebird).
(cmb)
+- PDO/SQLite:
+ . Fixed bug #81740 (PDO::quote() may return unquoted string). (CVE-2022-31631)
+ (cmb)
+
- Session:
. Fixed GH-9932 (session name silently fails with . and [). (David Carlier)
diff --git a/UPGRADING b/UPGRADING
index 5f69712a4b02b..bc92affa46e80 100644
--- a/UPGRADING
+++ b/UPGRADING
@@ -223,6 +223,10 @@ PHP 8.2 UPGRADE NOTES
widened to iterable from Iterator, allowing arrays to be passed.
RFC: https://wiki.php.net/rfc/iterator_xyz_accept_array
+- Standard
+ . unserialize() now performs a stricter validation of the structure of serialized
+ objects.
+
- XML
. xml_parser_set_option() now actually returns false when attempting to set a
negative tag start. Previously it returned true while emitting an E_WARNING.
diff --git a/Zend/Optimizer/block_pass.c b/Zend/Optimizer/block_pass.c
index b176ba2704e26..b3f2099fb1955 100644
--- a/Zend/Optimizer/block_pass.c
+++ b/Zend/Optimizer/block_pass.c
@@ -256,6 +256,13 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array
}
break;
+ case ZEND_MATCH_ERROR:
+ if (opline->op1_type == IS_TMP_VAR) {
+ src = VAR_SOURCE(opline->op1);
+ VAR_SOURCE(opline->op1) = NULL;
+ }
+ break;
+
case ZEND_FREE:
if (opline->op1_type == IS_TMP_VAR) {
src = VAR_SOURCE(opline->op1);
@@ -624,13 +631,13 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array
case ZEND_JMPNZ_EX:
while (1) {
if (opline->op1_type == IS_CONST) {
- if (zend_is_true(&ZEND_OP1_LITERAL(opline)) ==
- (opline->opcode == ZEND_JMPZ_EX)) {
+ bool is_jmpz_ex = opline->opcode == ZEND_JMPZ_EX;
+ if (zend_is_true(&ZEND_OP1_LITERAL(opline)) == is_jmpz_ex) {
++(*opt_count);
opline->opcode = ZEND_QM_ASSIGN;
zval_ptr_dtor_nogc(&ZEND_OP1_LITERAL(opline));
- ZVAL_BOOL(&ZEND_OP1_LITERAL(opline), opline->opcode == ZEND_JMPZ_EX);
+ ZVAL_BOOL(&ZEND_OP1_LITERAL(opline), is_jmpz_ex);
opline->op2.num = 0;
block->successors_count = 1;
block->successors[0] = block->successors[1];
diff --git a/Zend/Optimizer/dfa_pass.c b/Zend/Optimizer/dfa_pass.c
index 970557daf9a22..39c1bc12af902 100644
--- a/Zend/Optimizer/dfa_pass.c
+++ b/Zend/Optimizer/dfa_pass.c
@@ -1647,7 +1647,7 @@ void zend_dfa_optimize_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx
&& Z_TYPE_P(CT_CONSTANT_EX(op_array, opline->op2.constant)) == IS_LONG
&& Z_LVAL_P(CT_CONSTANT_EX(op_array, opline->op2.constant)) == 1
&& ssa->ops[op_1].op1_use >= 0
- && !(ssa->var_info[ssa->ops[op_1].op1_use].type & (MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {
+ && !(ssa->var_info[ssa->ops[op_1].op1_use].type & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {
// op_1: ASSIGN_SUB #?.CV [undef,null,int,foat] -> #v.CV, int(1) => PRE_DEC #?.CV ->#v.CV
diff --git a/Zend/tests/class_constants_007.phpt b/Zend/tests/class_constants_007.phpt
new file mode 100644
index 0000000000000..d09a12e8c17b8
--- /dev/null
+++ b/Zend/tests/class_constants_007.phpt
@@ -0,0 +1,13 @@
+--TEST--
+Ownership of constant expression inhereted from immutable class should be transfered to class
+--FILE--
+
+--EXPECT--
+string(2) " "
diff --git a/Zend/tests/fibers/gh10249.phpt b/Zend/tests/fibers/gh10249.phpt
new file mode 100644
index 0000000000000..efbef9c42a2ed
--- /dev/null
+++ b/Zend/tests/fibers/gh10249.phpt
@@ -0,0 +1,17 @@
+--TEST--
+GH-10249 (Assertion `size >= page_size + 1 * page_size' failed.)
+--FILE--
+start();
+} catch (Exception $e) {
+ echo $e->getMessage() . "\n";
+}
+
+?>
+--EXPECTF--
+Fiber stack size is too small, it needs to be at least %d bytes
diff --git a/Zend/tests/fibers/gh10340-001.phpt b/Zend/tests/fibers/gh10340-001.phpt
new file mode 100644
index 0000000000000..0c34b4a787bce
--- /dev/null
+++ b/Zend/tests/fibers/gh10340-001.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Bug GH-10340 001 (Assertion in zend_fiber_object_gc())
+--FILE--
+start();
+gc_collect_cycles();
+?>
+==DONE==
+--EXPECTF--
+Warning: Undefined variable $y in %s on line %d
+==DONE==
diff --git a/Zend/tests/fibers/gh10340-002.phpt b/Zend/tests/fibers/gh10340-002.phpt
new file mode 100644
index 0000000000000..6c8f8016cbf12
--- /dev/null
+++ b/Zend/tests/fibers/gh10340-002.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Bug GH-10340 002 (Assertion in zend_fiber_object_gc())
+--FILE--
+start();
+gc_collect_cycles();
+?>
+==DONE==
+--EXPECT--
+==DONE==
diff --git a/Zend/tests/fibers/gh10340-003.phpt b/Zend/tests/fibers/gh10340-003.phpt
new file mode 100644
index 0000000000000..6e59223a2a9c9
--- /dev/null
+++ b/Zend/tests/fibers/gh10340-003.phpt
@@ -0,0 +1,38 @@
+--TEST--
+Bug GH-10340 003 (Assertion in zend_fiber_object_gc())
+--FILE--
+start();
+
+print "1\n";
+
+$fiber = null;
+gc_collect_cycles();
+
+print "2\n";
+?>
+==DONE==
+--EXPECT--
+1
+C::__destruct
+2
+==DONE==
diff --git a/Zend/tests/fibers/gh9735-001.phpt b/Zend/tests/fibers/gh9735-001.phpt
new file mode 100644
index 0000000000000..327e74323924a
--- /dev/null
+++ b/Zend/tests/fibers/gh9735-001.phpt
@@ -0,0 +1,37 @@
+--TEST--
+Bug GH-9735 001 (Fiber stack variables do not participate in cycle collector)
+--FILE--
+start();
+gc_collect_cycles();
+
+print "2\n";
+
+$fiber = null;
+gc_collect_cycles();
+
+print "3\n";
+
+?>
+--EXPECT--
+1
+2
+C::__destruct
+3
diff --git a/Zend/tests/fibers/gh9735-002.phpt b/Zend/tests/fibers/gh9735-002.phpt
new file mode 100644
index 0000000000000..9a56c65f0abd9
--- /dev/null
+++ b/Zend/tests/fibers/gh9735-002.phpt
@@ -0,0 +1,41 @@
+--TEST--
+Bug GH-9735 002 (Fiber stack variables do not participate in cycle collector)
+--FILE--
+start();
+gc_collect_cycles();
+
+print "2\n";
+
+$fiber = null;
+gc_collect_cycles();
+
+print "3\n";
+
+?>
+--EXPECT--
+1
+2
+C::__destruct
+3
diff --git a/Zend/tests/fibers/gh9735-003.phpt b/Zend/tests/fibers/gh9735-003.phpt
new file mode 100644
index 0000000000000..03ea973b61dd4
--- /dev/null
+++ b/Zend/tests/fibers/gh9735-003.phpt
@@ -0,0 +1,40 @@
+--TEST--
+Bug GH-9735 003 (Fiber stack variables do not participate in cycle collector)
+--FILE--
+start();
+gc_collect_cycles();
+
+print "2\n";
+
+$fiber = null;
+gc_collect_cycles();
+
+print "3\n";
+
+?>
+--EXPECT--
+1
+2
+C::__destruct
+3
diff --git a/Zend/tests/fibers/gh9735-004.phpt b/Zend/tests/fibers/gh9735-004.phpt
new file mode 100644
index 0000000000000..cfcf381d83d08
--- /dev/null
+++ b/Zend/tests/fibers/gh9735-004.phpt
@@ -0,0 +1,40 @@
+--TEST--
+Bug GH-9735 004 (Fiber stack variables do not participate in cycle collector)
+--FILE--
+start();
+gc_collect_cycles();
+
+print "2\n";
+
+$fiber = null;
+gc_collect_cycles();
+
+print "3\n";
+
+?>
+--EXPECT--
+1
+2
+C::__destruct
+3
diff --git a/Zend/tests/fibers/gh9735-005.phpt b/Zend/tests/fibers/gh9735-005.phpt
new file mode 100644
index 0000000000000..c18a42fc37082
--- /dev/null
+++ b/Zend/tests/fibers/gh9735-005.phpt
@@ -0,0 +1,44 @@
+--TEST--
+Bug GH-9735 005 (Fiber stack variables do not participate in cycle collector)
+--FILE--
+start();
+gc_collect_cycles();
+
+print "2\n";
+
+$fiber = null;
+gc_collect_cycles();
+
+print "3\n";
+
+?>
+--EXPECTF--
+1
+2
+C::__destruct
+3
diff --git a/Zend/tests/fibers/gh9735-006.phpt b/Zend/tests/fibers/gh9735-006.phpt
new file mode 100644
index 0000000000000..59bb79d2404bc
--- /dev/null
+++ b/Zend/tests/fibers/gh9735-006.phpt
@@ -0,0 +1,47 @@
+--TEST--
+Bug GH-9735 006 (Fiber stack variables do not participate in cycle collector)
+--FILE--
+start();
+gc_collect_cycles();
+
+print "2\n";
+
+$fiber = null;
+gc_collect_cycles();
+
+print "3\n";
+
+?>
+--EXPECTF--
+1
+2
+C::__destruct
+3
diff --git a/Zend/tests/fibers/gh9735-007.phpt b/Zend/tests/fibers/gh9735-007.phpt
new file mode 100644
index 0000000000000..dbb6a33821119
--- /dev/null
+++ b/Zend/tests/fibers/gh9735-007.phpt
@@ -0,0 +1,47 @@
+--TEST--
+Bug GH-9735 007 (Fiber stack variables do not participate in cycle collector)
+--FILE--
+start();
+gc_collect_cycles();
+
+print "2\n";
+
+$fiber = null;
+gc_collect_cycles();
+
+print "3\n";
+
+?>
+--EXPECTF--
+1
+2
+C::__destruct
+3
diff --git a/Zend/tests/fibers/gh9735-008.phpt b/Zend/tests/fibers/gh9735-008.phpt
new file mode 100644
index 0000000000000..ec6f29fb79de4
--- /dev/null
+++ b/Zend/tests/fibers/gh9735-008.phpt
@@ -0,0 +1,42 @@
+--TEST--
+Bug GH-9735 008 (Fiber stack variables do not participate in cycle collector)
+--FILE--
+start();
+gc_collect_cycles();
+
+print "2\n";
+
+$fiber = null;
+gc_collect_cycles();
+
+print "3\n";
+
+?>
+--EXPECTF--
+1
+2
+C::__destruct
+3
diff --git a/Zend/tests/fibers/gh9735-009.phpt b/Zend/tests/fibers/gh9735-009.phpt
new file mode 100644
index 0000000000000..c471499443bd4
--- /dev/null
+++ b/Zend/tests/fibers/gh9735-009.phpt
@@ -0,0 +1,42 @@
+--TEST--
+Bug GH-9735 009 (Fiber stack variables do not participate in cycle collector)
+--FILE--
+start();
+gc_collect_cycles();
+
+print "2\n";
+
+$fiber = null;
+gc_collect_cycles();
+
+print "3\n";
+
+?>
+--EXPECTF--
+1
+2
+C::__destruct
+3
diff --git a/Zend/tests/gh10072-2.phpt b/Zend/tests/gh10072-2.phpt
new file mode 100644
index 0000000000000..3de75a2b6c09f
--- /dev/null
+++ b/Zend/tests/gh10072-2.phpt
@@ -0,0 +1,20 @@
+--TEST--
+GH-10072 (PHP crashes when execute_ex is overridden and a trampoline is used from internal code during shutdown)
+--EXTENSIONS--
+zend_test
+--INI--
+zend_test.replace_zend_execute_ex=1
+opcache.jit=disable
+--FILE--
+
+--EXPECT--
+Trampoline for shutdown
diff --git a/Zend/tests/gh10072.phpt b/Zend/tests/gh10072.phpt
new file mode 100644
index 0000000000000..95a0d43450525
--- /dev/null
+++ b/Zend/tests/gh10072.phpt
@@ -0,0 +1,106 @@
+--TEST--
+GH-10072 (PHP crashes when execute_ex is overridden and a trampoline is used from internal code)
+--EXTENSIONS--
+zend_test
+--INI--
+zend_test.replace_zend_execute_ex=1
+opcache.jit=disable
+--FILE--
+handle;
+ }
+
+
+ public function stream_close(): void
+ {
+ }
+
+ public function stream_open(string $path, string $mode, int $options = 0, ?string &$openedPath = null): bool
+ {
+ return true;
+ }
+
+
+ public function stream_read(int $count)
+ {
+ return 0;
+ }
+
+
+ public function stream_seek(int $offset, int $whence = SEEK_SET): bool
+ {
+ return true;
+ }
+
+
+ public function stream_set_option(int $option, int $arg1, ?int $arg2): bool
+ {
+ return false;
+ }
+
+
+ public function stream_stat()
+ {
+ return [];
+ }
+
+
+ public function stream_tell()
+ {
+ return [];
+ }
+
+
+ public function stream_truncate(int $newSize): bool
+ {
+ return true;
+ }
+
+
+ public function stream_write(string $data)
+ {
+ }
+
+
+ public function unlink(string $path): bool
+ {
+ return false;
+ }
+}
+
+class TrampolineTest {
+ /** @var resource|null */
+ public $context;
+
+ /** @var object|null */
+ private $wrapper;
+
+ public function __call(string $name, array $arguments) {
+ if (!$this->wrapper) {
+ $this->wrapper = new DummyStreamWrapper();
+ }
+ echo 'Trampoline for ', $name, PHP_EOL;
+ return $this->wrapper->$name(...$arguments);
+ }
+
+}
+
+stream_wrapper_register('custom', TrampolineTest::class);
+
+
+$fp = fopen("custom://myvar", "r+");
+?>
+--EXPECT--
+Trampoline for stream_open
+Trampoline for stream_close
diff --git a/Zend/tests/gh10251.phpt b/Zend/tests/gh10251.phpt
new file mode 100644
index 0000000000000..da8d8767a3656
--- /dev/null
+++ b/Zend/tests/gh10251.phpt
@@ -0,0 +1,25 @@
+--TEST--
+GH-10251 (Assertion `(flag & (1<<3)) == 0' failed.)
+--FILE--
+$p = $v;
+ }
+}
+$a = new A();
+$pp = "";
+$op = $pp & "";
+// Bitwise operators on strings don't compute the hash.
+// The code below previously assumed a hash was actually computed, leading to a crash.
+$a->$op = 0;
+echo "Done\n";
+?>
+--EXPECTF--
+Warning: Undefined variable $v in %s on line %d
+
+Warning: Undefined variable $p in %s on line %d
+Done
diff --git a/Zend/tests/gh10346.phpt b/Zend/tests/gh10346.phpt
new file mode 100644
index 0000000000000..74fce28e2307c
--- /dev/null
+++ b/Zend/tests/gh10346.phpt
@@ -0,0 +1,29 @@
+--TEST--
+GH-10346 (Observer: enum tryFrom() run_time_cache properly assigned)
+--CREDITS--
+Florian Sowade
+--EXTENSIONS--
+zend_test
+--INI--
+zend_test.observer.enabled=1
+zend_test.observer.observe_all=1
+--FILE--
+
+--EXPECTF--
+
+
+
+
+
+
+
+enum(Card::HEART)
+
+
diff --git a/Zend/tests/traits/constant_016.phpt b/Zend/tests/traits/constant_016.phpt
index 9cb2b29bcc095..0754c15716377 100644
--- a/Zend/tests/traits/constant_016.phpt
+++ b/Zend/tests/traits/constant_016.phpt
@@ -2,6 +2,8 @@
Compatibility of values of same name trait constants is checked after their constant expressions are evaluated
--ENV--
ENSURE_CONSTANT_IS_DEFINED_AT_RUNTIME=1
+--INI--
+variables_order=EGPCS
--FILE--
prototype = NULL;
internal_function->attributes = NULL;
if (EG(active)) { // at run-time: this ought to only happen if registered with dl() or somehow temporarily at runtime
- ZEND_MAP_PTR_INIT(internal_function->run_time_cache, zend_arena_alloc(&CG(arena), zend_internal_run_time_cache_reserved_size()));
+ ZEND_MAP_PTR_INIT(internal_function->run_time_cache, zend_arena_calloc(&CG(arena), 1, zend_internal_run_time_cache_reserved_size()));
} else {
ZEND_MAP_PTR_NEW(internal_function->run_time_cache);
}
diff --git a/Zend/zend_builtin_functions.c b/Zend/zend_builtin_functions.c
index 8813fa9788a97..5cebbbc560894 100644
--- a/Zend/zend_builtin_functions.c
+++ b/Zend/zend_builtin_functions.c
@@ -760,7 +760,7 @@ ZEND_FUNCTION(get_object_vars)
} else {
array_init_size(return_value, zend_hash_num_elements(properties));
- ZEND_HASH_MAP_FOREACH_KEY_VAL(properties, num_key, key, value) {
+ ZEND_HASH_FOREACH_KEY_VAL(properties, num_key, key, value) {
bool is_dynamic = 1;
if (Z_TYPE_P(value) == IS_INDIRECT) {
value = Z_INDIRECT_P(value);
diff --git a/Zend/zend_enum.c b/Zend/zend_enum.c
index 59befde732194..25261cf53f880 100644
--- a/Zend/zend_enum.c
+++ b/Zend/zend_enum.c
@@ -409,8 +409,11 @@ static void zend_enum_register_func(zend_class_entry *ce, zend_known_string_id n
zif->module = EG(current_module);
zif->scope = ce;
zif->T = ZEND_OBSERVER_ENABLED;
- ZEND_MAP_PTR_NEW(zif->run_time_cache);
- ZEND_MAP_PTR_SET(zif->run_time_cache, zend_arena_alloc(&CG(arena), zend_internal_run_time_cache_reserved_size()));
+ if (EG(active)) { // at run-time
+ ZEND_MAP_PTR_INIT(zif->run_time_cache, zend_arena_calloc(&CG(arena), 1, zend_internal_run_time_cache_reserved_size()));
+ } else {
+ ZEND_MAP_PTR_NEW(zif->run_time_cache);
+ }
if (!zend_hash_add_ptr(&ce->function_table, name, zif)) {
zend_error_noreturn(E_COMPILE_ERROR, "Cannot redeclare %s::%s()", ZSTR_VAL(ce->name), ZSTR_VAL(name));
diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c
index 487dcfc6bc881..de32aab8af94b 100644
--- a/Zend/zend_execute.c
+++ b/Zend/zend_execute.c
@@ -4414,6 +4414,71 @@ ZEND_API void zend_cleanup_unfinished_execution(zend_execute_data *execute_data,
cleanup_live_vars(execute_data, op_num, catch_op_num);
}
+ZEND_API HashTable *zend_unfinished_execution_gc(zend_execute_data *execute_data, zend_execute_data *call, zend_get_gc_buffer *gc_buffer)
+{
+ if (!EX(func) || !ZEND_USER_CODE(EX(func)->common.type)) {
+ return NULL;
+ }
+
+ zend_op_array *op_array = &EX(func)->op_array;
+
+ if (!(EX_CALL_INFO() & ZEND_CALL_HAS_SYMBOL_TABLE)) {
+ uint32_t i, num_cvs = EX(func)->op_array.last_var;
+ for (i = 0; i < num_cvs; i++) {
+ zend_get_gc_buffer_add_zval(gc_buffer, EX_VAR_NUM(i));
+ }
+ }
+
+ if (EX_CALL_INFO() & ZEND_CALL_FREE_EXTRA_ARGS) {
+ zval *zv = EX_VAR_NUM(op_array->last_var + op_array->T);
+ zval *end = zv + (EX_NUM_ARGS() - op_array->num_args);
+ while (zv != end) {
+ zend_get_gc_buffer_add_zval(gc_buffer, zv++);
+ }
+ }
+
+ if (EX_CALL_INFO() & ZEND_CALL_RELEASE_THIS) {
+ zend_get_gc_buffer_add_obj(gc_buffer, Z_OBJ(execute_data->This));
+ }
+ if (EX_CALL_INFO() & ZEND_CALL_CLOSURE) {
+ zend_get_gc_buffer_add_obj(gc_buffer, ZEND_CLOSURE_OBJECT(EX(func)));
+ }
+ if (EX_CALL_INFO() & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) {
+ zval extra_named_params;
+ ZVAL_ARR(&extra_named_params, EX(extra_named_params));
+ zend_get_gc_buffer_add_zval(gc_buffer, &extra_named_params);
+ }
+
+ if (call) {
+ /* -1 required because we want the last run opcode, not the next to-be-run one. */
+ uint32_t op_num = execute_data->opline - op_array->opcodes - 1;
+ zend_unfinished_calls_gc(execute_data, call, op_num, gc_buffer);
+ }
+
+ if (execute_data->opline != op_array->opcodes) {
+ uint32_t i, op_num = execute_data->opline - op_array->opcodes - 1;
+ for (i = 0; i < op_array->last_live_range; i++) {
+ const zend_live_range *range = &op_array->live_range[i];
+ if (range->start > op_num) {
+ break;
+ } else if (op_num < range->end) {
+ uint32_t kind = range->var & ZEND_LIVE_MASK;
+ uint32_t var_num = range->var & ~ZEND_LIVE_MASK;
+ zval *var = EX_VAR(var_num);
+ if (kind == ZEND_LIVE_TMPVAR || kind == ZEND_LIVE_LOOP) {
+ zend_get_gc_buffer_add_zval(gc_buffer, var);
+ }
+ }
+ }
+ }
+
+ if (EX_CALL_INFO() & ZEND_CALL_HAS_SYMBOL_TABLE) {
+ return execute_data->symbol_table;
+ } else {
+ return NULL;
+ }
+}
+
#if ZEND_VM_SPEC
static void zend_swap_operands(zend_op *op) /* {{{ */
{
diff --git a/Zend/zend_execute.h b/Zend/zend_execute.h
index a9b316b8bdbed..7a5837f4a0f7c 100644
--- a/Zend/zend_execute.h
+++ b/Zend/zend_execute.h
@@ -380,6 +380,7 @@ ZEND_API void zend_clean_and_cache_symbol_table(zend_array *symbol_table);
ZEND_API void ZEND_FASTCALL zend_free_compiled_variables(zend_execute_data *execute_data);
ZEND_API void zend_unfinished_calls_gc(zend_execute_data *execute_data, zend_execute_data *call, uint32_t op_num, zend_get_gc_buffer *buf);
ZEND_API void zend_cleanup_unfinished_execution(zend_execute_data *execute_data, uint32_t op_num, uint32_t catch_op_num);
+ZEND_API HashTable *zend_unfinished_execution_gc(zend_execute_data *execute_data, zend_execute_data *call, zend_get_gc_buffer *gc_buffer);
zval * ZEND_FASTCALL zend_handle_named_arg(
zend_execute_data **call_ptr, zend_string *arg_name,
diff --git a/Zend/zend_fibers.c b/Zend/zend_fibers.c
index 0bb45d1c3dc9c..93502fa00c3a0 100644
--- a/Zend/zend_fibers.c
+++ b/Zend/zend_fibers.c
@@ -25,6 +25,8 @@
#include "zend_builtin_functions.h"
#include "zend_observer.h"
#include "zend_mmap.h"
+#include "zend_compile.h"
+#include "zend_closures.h"
#include "zend_fibers.h"
#include "zend_fibers_arginfo.h"
@@ -175,8 +177,12 @@ static zend_fiber_stack *zend_fiber_stack_allocate(size_t size)
{
void *pointer;
const size_t page_size = zend_fiber_get_page_size();
+ const size_t minimum_stack_size = page_size + ZEND_FIBER_GUARD_PAGES * page_size;
- ZEND_ASSERT(size >= page_size + ZEND_FIBER_GUARD_PAGES * page_size);
+ if (size < minimum_stack_size) {
+ zend_throw_exception_ex(NULL, 0, "Fiber stack size is too small, it needs to be at least %zu bytes", minimum_stack_size);
+ return NULL;
+ }
const size_t stack_size = (size + page_size - 1) / page_size * page_size;
const size_t alloc_size = stack_size + ZEND_FIBER_GUARD_PAGES * page_size;
@@ -655,9 +661,32 @@ static HashTable *zend_fiber_object_gc(zend_object *object, zval **table, int *n
zend_get_gc_buffer_add_zval(buf, &fiber->fci.function_name);
zend_get_gc_buffer_add_zval(buf, &fiber->result);
+ if (fiber->context.status != ZEND_FIBER_STATUS_SUSPENDED) {
+ zend_get_gc_buffer_use(buf, table, num);
+ return NULL;
+ }
+
+ HashTable *lastSymTable = NULL;
+ zend_execute_data *ex = fiber->execute_data;
+ for (; ex; ex = ex->prev_execute_data) {
+ HashTable *symTable = zend_unfinished_execution_gc(ex, ex->call, buf);
+ if (symTable) {
+ if (lastSymTable) {
+ zval *val;
+ ZEND_HASH_FOREACH_VAL(lastSymTable, val) {
+ if (EXPECTED(Z_TYPE_P(val) == IS_INDIRECT)) {
+ val = Z_INDIRECT_P(val);
+ }
+ zend_get_gc_buffer_add_zval(buf, val);
+ } ZEND_HASH_FOREACH_END();
+ }
+ lastSymTable = symTable;
+ }
+ }
+
zend_get_gc_buffer_use(buf, table, num);
- return NULL;
+ return lastSymTable;
}
ZEND_METHOD(Fiber, __construct)
diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c
index 48f228b956e9e..f47f008bfabff 100644
--- a/Zend/zend_generators.c
+++ b/Zend/zend_generators.c
@@ -332,7 +332,7 @@ static HashTable *zend_generator_get_gc(zend_object *object, zval **table, int *
{
zend_generator *generator = (zend_generator*)object;
zend_execute_data *execute_data = generator->execute_data;
- zend_op_array *op_array;
+ zend_execute_data *call = NULL;
if (!execute_data) {
/* If the generator has been closed, it can only hold on to three values: The value, key
@@ -352,7 +352,6 @@ static HashTable *zend_generator_get_gc(zend_object *object, zval **table, int *
return NULL;
}
- op_array = &EX(func)->op_array;
zend_get_gc_buffer *gc_buffer = zend_get_gc_buffer_create();
zend_get_gc_buffer_add_zval(gc_buffer, &generator->value);
@@ -360,59 +359,17 @@ static HashTable *zend_generator_get_gc(zend_object *object, zval **table, int *
zend_get_gc_buffer_add_zval(gc_buffer, &generator->retval);
zend_get_gc_buffer_add_zval(gc_buffer, &generator->values);
- if (!(EX_CALL_INFO() & ZEND_CALL_HAS_SYMBOL_TABLE)) {
- uint32_t i, num_cvs = EX(func)->op_array.last_var;
- for (i = 0; i < num_cvs; i++) {
- zend_get_gc_buffer_add_zval(gc_buffer, EX_VAR_NUM(i));
- }
- }
-
- if (EX_CALL_INFO() & ZEND_CALL_FREE_EXTRA_ARGS) {
- zval *zv = EX_VAR_NUM(op_array->last_var + op_array->T);
- zval *end = zv + (EX_NUM_ARGS() - op_array->num_args);
- while (zv != end) {
- zend_get_gc_buffer_add_zval(gc_buffer, zv++);
- }
+ if (UNEXPECTED(generator->frozen_call_stack)) {
+ /* The frozen stack is linked in reverse order */
+ call = zend_generator_revert_call_stack(generator->frozen_call_stack);
}
- if (EX_CALL_INFO() & ZEND_CALL_RELEASE_THIS) {
- zend_get_gc_buffer_add_obj(gc_buffer, Z_OBJ(execute_data->This));
- }
- if (EX_CALL_INFO() & ZEND_CALL_CLOSURE) {
- zend_get_gc_buffer_add_obj(gc_buffer, ZEND_CLOSURE_OBJECT(EX(func)));
- }
- if (EX_CALL_INFO() & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) {
- zval extra_named_params;
- ZVAL_ARR(&extra_named_params, EX(extra_named_params));
- zend_get_gc_buffer_add_zval(gc_buffer, &extra_named_params);
- }
+ zend_unfinished_execution_gc(execute_data, call, gc_buffer);
if (UNEXPECTED(generator->frozen_call_stack)) {
- /* The frozen stack is linked in reverse order */
- zend_execute_data *call = zend_generator_revert_call_stack(generator->frozen_call_stack);
- /* -1 required because we want the last run opcode, not the next to-be-run one. */
- uint32_t op_num = execute_data->opline - op_array->opcodes - 1;
- zend_unfinished_calls_gc(execute_data, call, op_num, gc_buffer);
zend_generator_revert_call_stack(call);
}
- if (execute_data->opline != op_array->opcodes) {
- uint32_t i, op_num = execute_data->opline - op_array->opcodes - 1;
- for (i = 0; i < op_array->last_live_range; i++) {
- const zend_live_range *range = &op_array->live_range[i];
- if (range->start > op_num) {
- break;
- } else if (op_num < range->end) {
- uint32_t kind = range->var & ZEND_LIVE_MASK;
- uint32_t var_num = range->var & ~ZEND_LIVE_MASK;
- zval *var = EX_VAR(var_num);
- if (kind == ZEND_LIVE_TMPVAR || kind == ZEND_LIVE_LOOP) {
- zend_get_gc_buffer_add_zval(gc_buffer, var);
- }
- }
- }
- }
-
if (generator->node.parent) {
zend_get_gc_buffer_add_obj(gc_buffer, &generator->node.parent->std);
}
diff --git a/Zend/zend_hash.c b/Zend/zend_hash.c
index 9498bd3718c86..eae03e4491fc6 100644
--- a/Zend/zend_hash.c
+++ b/Zend/zend_hash.c
@@ -166,6 +166,8 @@ static zend_always_inline void zend_hash_real_init_mixed_ex(HashTable *ht)
void *data;
uint32_t nSize = ht->nTableSize;
+ ZEND_ASSERT(HT_SIZE_TO_MASK(nSize));
+
if (UNEXPECTED(GC_FLAGS(ht) & IS_ARRAY_PERSISTENT)) {
data = pemalloc(HT_SIZE_EX(nSize, HT_SIZE_TO_MASK(nSize)), 1);
} else if (EXPECTED(nSize == HT_MIN_SIZE)) {
@@ -338,6 +340,8 @@ ZEND_API void ZEND_FASTCALL zend_hash_packed_to_hash(HashTable *ht)
uint32_t i;
uint32_t nSize = ht->nTableSize;
+ ZEND_ASSERT(HT_SIZE_TO_MASK(nSize));
+
HT_ASSERT_RC1(ht);
HT_FLAGS(ht) &= ~HASH_FLAG_PACKED;
new_data = pemalloc(HT_SIZE_EX(nSize, HT_SIZE_TO_MASK(nSize)), GC_FLAGS(ht) & IS_ARRAY_PERSISTENT);
@@ -380,7 +384,11 @@ ZEND_API void ZEND_FASTCALL zend_hash_to_packed(HashTable *ht)
ZEND_API void ZEND_FASTCALL zend_hash_extend(HashTable *ht, uint32_t nSize, bool packed)
{
HT_ASSERT_RC1(ht);
+
if (nSize == 0) return;
+
+ ZEND_ASSERT(HT_SIZE_TO_MASK(nSize));
+
if (UNEXPECTED(HT_FLAGS(ht) & HASH_FLAG_UNINITIALIZED)) {
if (nSize > ht->nTableSize) {
ht->nTableSize = zend_hash_check_size(nSize);
@@ -1227,6 +1235,8 @@ static void ZEND_FASTCALL zend_hash_do_resize(HashTable *ht)
uint32_t nSize = ht->nTableSize + ht->nTableSize;
Bucket *old_buckets = ht->arData;
+ ZEND_ASSERT(HT_SIZE_TO_MASK(nSize));
+
ht->nTableSize = nSize;
new_data = pemalloc(HT_SIZE_EX(nSize, HT_SIZE_TO_MASK(nSize)), GC_FLAGS(ht) & IS_ARRAY_PERSISTENT);
ht->nTableMask = HT_SIZE_TO_MASK(ht->nTableSize);
diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c
index 2d9970975785f..6b168a8b41106 100644
--- a/Zend/zend_inheritance.c
+++ b/Zend/zend_inheritance.c
@@ -1385,6 +1385,7 @@ static void do_inherit_class_constant(zend_string *name, zend_class_constant *pa
c = zend_arena_alloc(&CG(arena), sizeof(zend_class_constant));
memcpy(c, parent_const, sizeof(zend_class_constant));
parent_const = c;
+ Z_CONSTANT_FLAGS(c->value) |= CONST_OWNED;
}
}
if (ce->type & ZEND_INTERNAL_CLASS) {
diff --git a/Zend/zend_object_handlers.c b/Zend/zend_object_handlers.c
index d0677f0fe4e96..e44a3822dd22f 100644
--- a/Zend/zend_object_handlers.c
+++ b/Zend/zend_object_handlers.c
@@ -553,9 +553,8 @@ ZEND_API uint32_t *zend_get_property_guard(zend_object *zobj, zend_string *membe
if (EXPECTED(Z_TYPE_P(zv) == IS_STRING)) {
zend_string *str = Z_STR_P(zv);
if (EXPECTED(str == member) ||
- /* "str" always has a pre-calculated hash value here */
- (EXPECTED(ZSTR_H(str) == zend_string_hash_val(member)) &&
- EXPECTED(zend_string_equal_content(str, member)))) {
+ /* str and member don't necessarily have a pre-calculated hash value here */
+ EXPECTED(zend_string_equal_content(str, member))) {
return &Z_PROPERTY_GUARD_P(zv);
} else if (EXPECTED(Z_PROPERTY_GUARD_P(zv) == 0)) {
zval_ptr_dtor_str(zv);
diff --git a/Zend/zend_portability.h b/Zend/zend_portability.h
index d0b272686e339..d59fc6d07c1db 100644
--- a/Zend/zend_portability.h
+++ b/Zend/zend_portability.h
@@ -692,7 +692,7 @@ extern "C++" {
# define ZEND_VOIDP(ptr) (ptr)
#endif
-#if defined(__GNUC__) && ZEND_GCC_VERSION >= 9000
+#if __has_attribute(__indirect_return__)
# define ZEND_INDIRECT_RETURN __attribute__((__indirect_return__))
#else
# define ZEND_INDIRECT_RETURN
diff --git a/Zend/zend_types.h b/Zend/zend_types.h
index df64541749d4c..e6505d8b62ad1 100644
--- a/Zend/zend_types.h
+++ b/Zend/zend_types.h
@@ -411,8 +411,15 @@ struct _zend_array {
#define HT_MIN_MASK ((uint32_t) -2)
#define HT_MIN_SIZE 8
+/* HT_MAX_SIZE is chosen to satisfy the following constraints:
+ * - HT_SIZE_TO_MASK(HT_MAX_SIZE) != 0
+ * - HT_SIZE_EX(HT_MAX_SIZE, HT_SIZE_TO_MASK(HT_MAX_SIZE)) does not overflow or
+ * wrapparound, and is <= the addressable space size
+ * - HT_MAX_SIZE must be a power of two:
+ * (nTableSizetype) || (call_info & ZEND_CALL_TOP)) {
+ if (!execute_data || !EX(func) || !ZEND_USER_CODE(EX(func)->type) || (call_info & ZEND_CALL_TOP)) {
ZEND_VM_RETURN();
}
diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h
index 9303953e4e85e..02842be27f6ee 100644
--- a/Zend/zend_vm_execute.h
+++ b/Zend/zend_vm_execute.h
@@ -3409,7 +3409,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CALL_TRAMPOLINE_SPEC_HANDLER(Z
SAVE_OPLINE_EX();
execute_data = EX(prev_execute_data);
- LOAD_OPLINE();
+ if (execute_data) {
+ LOAD_OPLINE();
+ }
ZEND_ADD_CALL_FLAG(call, ZEND_CALL_TOP);
zend_execute_ex(call);
}
@@ -3460,7 +3462,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CALL_TRAMPOLINE_SPEC_HANDLER(Z
execute_data = EG(current_execute_data);
- if (!EX(func) || !ZEND_USER_CODE(EX(func)->type) || (call_info & ZEND_CALL_TOP)) {
+ if (!execute_data || !EX(func) || !ZEND_USER_CODE(EX(func)->type) || (call_info & ZEND_CALL_TOP)) {
ZEND_VM_RETURN();
}
@@ -3547,7 +3549,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CALL_TRAMPOLINE_SPEC_OBSERVER_
SAVE_OPLINE_EX();
zend_observer_fcall_begin(execute_data);
execute_data = EX(prev_execute_data);
- LOAD_OPLINE();
+ if (execute_data) {
+ LOAD_OPLINE();
+ }
ZEND_ADD_CALL_FLAG(call, ZEND_CALL_TOP);
zend_execute_ex(call);
}
@@ -3599,7 +3603,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CALL_TRAMPOLINE_SPEC_OBSERVER_
execute_data = EG(current_execute_data);
- if (!EX(func) || !ZEND_USER_CODE(EX(func)->type) || (call_info & ZEND_CALL_TOP)) {
+ if (!execute_data || !EX(func) || !ZEND_USER_CODE(EX(func)->type) || (call_info & ZEND_CALL_TOP)) {
ZEND_VM_RETURN();
}
diff --git a/build/gen_stub.php b/build/gen_stub.php
index 8cf319ceaea39..9e6a945a5c4e7 100755
--- a/build/gen_stub.php
+++ b/build/gen_stub.php
@@ -1487,14 +1487,10 @@ public function getMethodSynopsisElement(array $funcMap, array $aliasMap, DOMDoc
$methodSynopsis = $doc->createElement($synopsisType);
- $aliasedFunc = $this->aliasType === "alias" && isset($funcMap[$this->alias->__toString()]) ? $funcMap[$this->alias->__toString()] : null;
- $aliasFunc = $aliasMap[$this->name->__toString()] ?? null;
-
- if (($this->aliasType === "alias" && $aliasedFunc !== null && $aliasedFunc->isMethod() !== $this->isMethod()) ||
- ($aliasFunc !== null && $aliasFunc->isMethod() !== $this->isMethod())
- ) {
+ if ($this->isMethod()) {
+ assert($this->name instanceof MethodName);
$role = $doc->createAttribute("role");
- $role->value = $this->isMethod() ? "oop" : "procedural";
+ $role->value = addslashes($this->name->className->__toString());
$methodSynopsis->appendChild($role);
}
@@ -2704,17 +2700,18 @@ public function getClassSynopsisElement(DOMDocument $doc, array $classMap, itera
}
$classSynopsisInfo->appendChild(new DOMText("\n "));
- /** @var Name[] $parentsWithInheritedConstants */
+ /** @var array $parentsWithInheritedConstants */
$parentsWithInheritedConstants = [];
- /** @var Name[] $parentsWithInheritedProperties */
+ /** @var array $parentsWithInheritedProperties */
$parentsWithInheritedProperties = [];
- /** @var Name[] $parentsWithInheritedMethods */
+ /** @var array $parentsWithInheritedMethods */
$parentsWithInheritedMethods = [];
$this->collectInheritedMembers(
$parentsWithInheritedConstants,
$parentsWithInheritedProperties,
$parentsWithInheritedMethods,
+ $this->hasConstructor(),
$classMap
);
@@ -2767,12 +2764,13 @@ public function getClassSynopsisElement(DOMDocument $doc, array $classMap, itera
$classSynopsis->appendChild($classSynopsisInfo);
$classReference = self::getClassSynopsisReference($this->name);
+ $escapedName = addslashes($this->name->__toString());
if ($this->hasConstructor()) {
$classSynopsis->appendChild(new DOMText("\n "));
$includeElement = $this->createIncludeElement(
$doc,
- "xmlns(db=http://docbook.org/ns/docbook) xpointer(id('$classReference')/db:refentry/db:refsect1[@role='description']/descendant::db:constructorsynopsis[not(@role='procedural')])"
+ "xmlns(db=http://docbook.org/ns/docbook) xpointer(id('$classReference')/db:refentry/db:refsect1[@role='description']/descendant::db:constructorsynopsis[@role='$escapedName'])"
);
$classSynopsis->appendChild($includeElement);
}
@@ -2781,7 +2779,7 @@ public function getClassSynopsisElement(DOMDocument $doc, array $classMap, itera
$classSynopsis->appendChild(new DOMText("\n "));
$includeElement = $this->createIncludeElement(
$doc,
- "xmlns(db=http://docbook.org/ns/docbook) xpointer(id('$classReference')/db:refentry/db:refsect1[@role='description']/descendant::db:methodsynopsis[not(@role='procedural')])"
+ "xmlns(db=http://docbook.org/ns/docbook) xpointer(id('$classReference')/db:refentry/db:refsect1[@role='description']/descendant::db:methodsynopsis[@role='$escapedName'])"
);
$classSynopsis->appendChild($includeElement);
}
@@ -2790,7 +2788,7 @@ public function getClassSynopsisElement(DOMDocument $doc, array $classMap, itera
$classSynopsis->appendChild(new DOMText("\n "));
$includeElement = $this->createIncludeElement(
$doc,
- "xmlns(db=http://docbook.org/ns/docbook) xpointer(id('$classReference')/db:refentry/db:refsect1[@role='description']/descendant::db:destructorsynopsis[not(@role='procedural')])"
+ "xmlns(db=http://docbook.org/ns/docbook) xpointer(id('$classReference')/db:refentry/db:refsect1[@role='description']/descendant::db:destructorsynopsis[@role='$escapedName'])"
);
$classSynopsis->appendChild($includeElement);
}
@@ -2803,13 +2801,21 @@ public function getClassSynopsisElement(DOMDocument $doc, array $classMap, itera
$classSynopsis->appendChild($classSynopsisInfo);
foreach ($parentsWithInheritedMethods as $parent) {
- $classSynopsis->appendChild(new DOMText("\n "));
- $parentReference = self::getClassSynopsisReference($parent);
- $includeElement = $this->createIncludeElement(
- $doc,
- "xmlns(db=http://docbook.org/ns/docbook) xpointer(id('$parentReference')/db:refentry/db:refsect1[@role='description']/descendant::db:methodsynopsis[not(@role='procedural')])"
- );
- $classSynopsis->appendChild($includeElement);
+ $parentName = $parent["name"];
+ $parentMethodsynopsisTypes = $parent["types"];
+
+ $parentReference = self::getClassSynopsisReference($parentName);
+ $escapedParentName = addslashes($parentName->__toString());
+
+ foreach ($parentMethodsynopsisTypes as $parentMethodsynopsisType) {
+ $classSynopsis->appendChild(new DOMText("\n "));
+ $includeElement = $this->createIncludeElement(
+ $doc,
+ "xmlns(db=http://docbook.org/ns/docbook) xpointer(id('$parentReference')/db:refentry/db:refsect1[@role='description']/descendant::db:{$parentMethodsynopsisType}[@role='$escapedParentName'])"
+ );
+
+ $classSynopsis->appendChild($includeElement);
+ }
}
}
@@ -2867,39 +2873,54 @@ public static function getClassSynopsisReference(Name $name): string {
}
/**
- * @param Name[] $parentsWithInheritedConstants
- * @param Name[] $parentsWithInheritedProperties
- * @param Name[] $parentsWithInheritedMethods
+ * @param array $parentsWithInheritedConstants
+ * @param array $parentsWithInheritedProperties
+ * @param array $parentsWithInheritedMethods
* @param array $classMap
*/
private function collectInheritedMembers(
array &$parentsWithInheritedConstants,
array &$parentsWithInheritedProperties,
array &$parentsWithInheritedMethods,
+ bool $hasConstructor,
array $classMap
): void {
foreach ($this->extends as $parent) {
$parentInfo = $classMap[$parent->toString()] ?? null;
+ $parentName = $parent->toString();
+
if (!$parentInfo) {
- throw new Exception("Missing parent class " . $parent->toString());
+ throw new Exception("Missing parent class $parentName");
}
- if (!empty($parentInfo->constInfos) && !isset($parentsWithInheritedConstants[$parent->toString()])) {
- $parentsWithInheritedConstants[$parent->toString()] = $parent;
+ if (!empty($parentInfo->constInfos) && !isset($parentsWithInheritedConstants[$parentName])) {
+ $parentsWithInheritedConstants[] = $parent;
+ }
+
+ if (!empty($parentInfo->propertyInfos) && !isset($parentsWithInheritedProperties[$parentName])) {
+ $parentsWithInheritedProperties[$parentName] = $parent;
+ }
+
+ if (!$hasConstructor && $parentInfo->hasNonPrivateConstructor()) {
+ $parentsWithInheritedMethods[$parentName]["name"] = $parent;
+ $parentsWithInheritedMethods[$parentName]["types"][] = "constructorsynopsis";
}
- if (!empty($parentInfo->propertyInfos) && !isset($parentsWithInheritedProperties[$parent->toString()])) {
- $parentsWithInheritedProperties[$parent->toString()] = $parent;
+ if ($parentInfo->hasMethods()) {
+ $parentsWithInheritedMethods[$parentName]["name"] = $parent;
+ $parentsWithInheritedMethods[$parentName]["types"][] = "methodsynopsis";
}
- if (!isset($parentsWithInheritedMethods[$parent->toString()]) && $parentInfo->hasMethods()) {
- $parentsWithInheritedMethods[$parent->toString()] = $parent;
+ if ($parentInfo->hasDestructor()) {
+ $parentsWithInheritedMethods[$parentName]["name"] = $parent;
+ $parentsWithInheritedMethods[$parentName]["types"][] = "destructorsynopsis";
}
$parentInfo->collectInheritedMembers(
$parentsWithInheritedConstants,
$parentsWithInheritedProperties,
$parentsWithInheritedMethods,
+ $hasConstructor,
$classMap
);
}
@@ -2921,6 +2942,7 @@ private function collectInheritedMembers(
$parentsWithInheritedConstants,
$unusedParentsWithInheritedProperties,
$unusedParentsWithInheritedMethods,
+ $hasConstructor,
$classMap
);
}
@@ -2937,6 +2959,17 @@ private function hasConstructor(): bool
return false;
}
+ private function hasNonPrivateConstructor(): bool
+ {
+ foreach ($this->funcInfos as $funcInfo) {
+ if ($funcInfo->name->isConstructor() && !($funcInfo->flags & Class_::MODIFIER_PRIVATE)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
private function hasDestructor(): bool
{
foreach ($this->funcInfos as $funcInfo) {
@@ -4058,14 +4091,14 @@ static function (FuncInfo $funcInfo) use ($allConstInfos) {
);
}
-/** @param FuncInfo $funcInfos */
-function generateOptimizerInfo(array $funcInfos): string {
+/** @param array $funcMap */
+function generateOptimizerInfo(array $funcMap): string {
$code = "/* This is a generated file, edit the .stub.php files instead. */\n\n";
$code .= "static const func_info_t func_infos[] = {\n";
- $code .= generateCodeWithConditions($funcInfos, "", static function (FuncInfo $funcInfo) {
+ $code .= generateCodeWithConditions($funcMap, "", static function (FuncInfo $funcInfo) {
return $funcInfo->getOptimizerInfo();
});
@@ -4261,16 +4294,18 @@ function replaceClassSynopses(string $targetDirectory, array $classMap, iterable
$replacedXml = preg_replace(
[
"/REPLACED-ENTITY-([A-Za-z0-9._{}%-]+?;)/",
- "//i",
- "//i",
- "//i",
- "//i",
+ '//i',
+ '//i',
+ '//i',
+ '//i',
+ '//i',
],
[
"&$1",
"",
"",
"",
+ "",
"",
],
$replacedXml
@@ -4494,8 +4529,8 @@ function replaceMethodSynopses(string $targetDirectory, array $funcMap, array $a
$replacedXml = preg_replace(
[
"/REPLACED-ENTITY-([A-Za-z0-9._{}%-]+?;)/",
- "//i",
- "//i",
+ '//i',
+ '//i',
],
[
"&$1",
@@ -4686,7 +4721,6 @@ function initPhpParser() {
foreach ($fileInfos as $fileInfo) {
foreach ($fileInfo->getAllFuncInfos() as $funcInfo) {
- /** @var FuncInfo $funcInfo */
$funcMap[$funcInfo->name->__toString()] = $funcInfo;
// TODO: Don't use aliasMap for methodsynopsis?
diff --git a/configure.ac b/configure.ac
index 3c257b6bdfac0..5696e562d2110 100644
--- a/configure.ac
+++ b/configure.ac
@@ -17,7 +17,7 @@ dnl Basic autoconf initialization, generation of config.nice.
dnl ----------------------------------------------------------------------------
AC_PREREQ([2.68])
-AC_INIT([PHP],[8.2.1-dev],[https://github.com/php/php-src/issues],[php],[https://www.php.net])
+AC_INIT([PHP],[8.2.2],[https://github.com/php/php-src/issues],[php],[https://www.php.net])
AC_CONFIG_SRCDIR([main/php_version.h])
AC_CONFIG_AUX_DIR([build])
AC_PRESERVE_HELP_ORDER
diff --git a/ext/date/lib/parse_date.c b/ext/date/lib/parse_date.c
index fde143646992f..63411f5123ae0 100644
--- a/ext/date/lib/parse_date.c
+++ b/ext/date/lib/parse_date.c
@@ -1,4 +1,4 @@
-/* Generated by re2c 0.15.3 on Thu Dec 1 10:59:46 2022 */
+/* Generated by re2c 0.15.3 on Tue Jan 10 15:16:26 2023 */
#line 1 "ext/date/lib/parse_date.re"
/*
* The MIT License (MIT)
@@ -787,7 +787,7 @@ static timelib_long timelib_lookup_abbr(const char **ptr, int *dst, char **tz_ab
(**ptr >= 'A' && **ptr <= 'Z') ||
(**ptr >= 'a' && **ptr <= 'z') ||
(**ptr >= '0' && **ptr <= '9') ||
- **ptr == '/' || **ptr == '_' || **ptr == '-'
+ **ptr == '/' || **ptr == '_' || **ptr == '-' || **ptr == '+'
) {
++*ptr;
}
diff --git a/ext/date/lib/parse_date.re b/ext/date/lib/parse_date.re
index 800cd830fbd68..81dfcec8511af 100644
--- a/ext/date/lib/parse_date.re
+++ b/ext/date/lib/parse_date.re
@@ -785,7 +785,7 @@ static timelib_long timelib_lookup_abbr(const char **ptr, int *dst, char **tz_ab
(**ptr >= 'A' && **ptr <= 'Z') ||
(**ptr >= 'a' && **ptr <= 'z') ||
(**ptr >= '0' && **ptr <= '9') ||
- **ptr == '/' || **ptr == '_' || **ptr == '-'
+ **ptr == '/' || **ptr == '_' || **ptr == '-' || **ptr == '+'
) {
++*ptr;
}
diff --git a/ext/date/php_date.c b/ext/date/php_date.c
index 32ef3771ad230..fd13e98100848 100644
--- a/ext/date/php_date.c
+++ b/ext/date/php_date.c
@@ -3052,6 +3052,10 @@ static bool php_date_modify(zval *object, char *modify, size_t modify_len) /* {{
dateobj->time->us = tmp_time->us;
}
+ if (tmp_time->have_zone && tmp_time->zone_type == TIMELIB_ZONETYPE_OFFSET) {
+ timelib_set_timezone_from_offset(dateobj->time, tmp_time->z);
+ }
+
timelib_time_dtor(tmp_time);
timelib_update_ts(dateobj->time, NULL);
diff --git a/ext/date/tests/gh10218.phpt b/ext/date/tests/gh10218.phpt
new file mode 100644
index 0000000000000..ff1b493ce7f01
--- /dev/null
+++ b/ext/date/tests/gh10218.phpt
@@ -0,0 +1,15 @@
+--TEST--
+Bug GH-10218 (DateTimeZone fails to parse time zones that contain the "+" character)
+--FILE--
+
+--EXPECTF--
+object(DateTime)#%d (%d) {
+ ["date"]=>
+ string(%d) "%s"
+ ["timezone_type"]=>
+ int(3)
+ ["timezone"]=>
+ string(9) "Etc/GMT+1"
+}
diff --git a/ext/date/tests/gh9891.phpt b/ext/date/tests/gh9891.phpt
new file mode 100644
index 0000000000000..6d8832b0d9237
--- /dev/null
+++ b/ext/date/tests/gh9891.phpt
@@ -0,0 +1,30 @@
+--TEST--
+Bug GH-9891 (DateTime modify with unixtimestamp (@) must work like setTimestamp)
+--FILE--
+modify('@1234567890');
+var_dump($m->getTimeStamp());
+
+echo "=======\n";
+
+$a = new DateTime('2022-11-01 13:30:00', new DateTimezone('America/Lima'));
+$b = clone $a;
+echo '$a: ', $a->format(DateTime::ATOM), "\n";
+echo '$b: ', $b->format(DateTime::ATOM), "\n";
+echo '$a: @', $a->getTimestamp(), "\n";
+echo '$b: setTimestamp(', $b->getTimestamp(), ")\n";
+$a->modify('@' . $a->getTimestamp());
+$b->setTimestamp($b->getTimestamp());
+echo '$a: ', $a->format(DateTime::ATOM), "\n";
+echo '$b: ', $b->format(DateTime::ATOM), "\n";
+?>
+--EXPECT--
+int(1234567890)
+=======
+$a: 2022-11-01T13:30:00-05:00
+$b: 2022-11-01T13:30:00-05:00
+$a: @1667327400
+$b: setTimestamp(1667327400)
+$a: 2022-11-01T18:30:00+00:00
+$b: 2022-11-01T13:30:00-05:00
diff --git a/ext/gmp/tests/bug74670.phpt b/ext/gmp/tests/bug74670.phpt
index 5f9de9330e591..1d09509a91316 100644
--- a/ext/gmp/tests/bug74670.phpt
+++ b/ext/gmp/tests/bug74670.phpt
@@ -8,5 +8,5 @@ $str = 'C:3:"GMP":4:{s:6666666666:""}';
var_dump(unserialize($str));
?>
--EXPECTF--
-Notice: unserialize(): Error at offset 13 of 29 bytes in %s on line %d
+Notice: unserialize(): Error at offset 17 of 29 bytes in %s on line %d
bool(false)
diff --git a/ext/gmp/tests/gmp_dynamic_property.phpt b/ext/gmp/tests/gmp_dynamic_property.phpt
index 547fe51a7f6a3..ff3d4ac94e012 100644
--- a/ext/gmp/tests/gmp_dynamic_property.phpt
+++ b/ext/gmp/tests/gmp_dynamic_property.phpt
@@ -5,7 +5,7 @@ gmp
--FILE--
{1} = 123;
$serialized = serialize($g);
diff --git a/ext/hash/hash_xxhash.c b/ext/hash/hash_xxhash.c
index 7ecedd81287ce..8a155c9862271 100644
--- a/ext/hash/hash_xxhash.c
+++ b/ext/hash/hash_xxhash.c
@@ -174,7 +174,9 @@ zend_always_inline static void _PHP_XXH3_Init(PHP_XXH3_64_CTX *ctx, HashTable *a
func_init_seed(&ctx->s, (XXH64_hash_t)Z_LVAL_P(_seed));
return;
} else if (_secret) {
- convert_to_string(_secret);
+ if (!try_convert_to_string(_secret)) {
+ return;
+ }
size_t len = Z_STRLEN_P(_secret);
if (len < PHP_XXH3_SECRET_SIZE_MIN) {
zend_throw_error(NULL, "%s: Secret length must be >= %u bytes, %zu bytes passed", algo_name, XXH3_SECRET_SIZE_MIN, len);
diff --git a/ext/hash/tests/xxhash_secret.phpt b/ext/hash/tests/xxhash_secret.phpt
index 91e7d929d323d..6f1efbe6c90a6 100644
--- a/ext/hash/tests/xxhash_secret.phpt
+++ b/ext/hash/tests/xxhash_secret.phpt
@@ -3,6 +3,13 @@ Hash: xxHash secret
--FILE--
getMessage());
}
+ try {
+ $ctx = hash_init($a, options: ["secret" => new StringableThrowingClass()]);
+ } catch (Throwable $e) {
+ var_dump($e->getMessage());
+ }
+
try {
$ctx = hash_init($a, options: ["secret" => str_repeat('a', 17)]);
} catch (Throwable $e) {
@@ -35,8 +48,10 @@ foreach (["xxh3", "xxh128"] as $a) {
?>
--EXPECT--
string(67) "xxh3: Only one of seed or secret is to be passed for initialization"
+string(23) "exception in __toString"
string(57) "xxh3: Secret length must be >= 136 bytes, 17 bytes passed"
8028aa834c03557a == 8028aa834c03557a == true
string(69) "xxh128: Only one of seed or secret is to be passed for initialization"
+string(23) "exception in __toString"
string(59) "xxh128: Secret length must be >= 136 bytes, 17 bytes passed"
54279097795e7218093a05d4d781cbb9 == 54279097795e7218093a05d4d781cbb9 == true
diff --git a/ext/intl/tests/dateformat_calendars_variant3.phpt b/ext/intl/tests/dateformat_calendars_variant3.phpt
index d189361bcffb6..1ad970d07d0e6 100644
--- a/ext/intl/tests/dateformat_calendars_variant3.phpt
+++ b/ext/intl/tests/dateformat_calendars_variant3.phpt
@@ -6,6 +6,7 @@ date.timezone=Atlantic/Azores
intl
--SKIPIF--
= 54.1'); ?>
+= 0) die('skip for ICU < 72.1'); ?>
--FILE--
= 72.1'); ?>
+--FILE--
+format(strtotime('2012-01-01 00:00:00 +0000')));
+var_dump($fmt2->format(strtotime('2012-01-01 00:00:00 +0000')));
+var_dump($fmt3->format(strtotime('2012-01-01 00:00:00 +0000')));
+
+new IntlDateFormatter('en_US@calendar=hebrew',
+ IntlDateFormatter::FULL,
+ IntlDateFormatter::FULL,
+ 'GMT+05:12',
+ -1);
+?>
+==DONE==
+--EXPECTF--
+string(49) "Sunday, January 1, 2012 at 5:12:00 AM GMT+05:12"
+string(49) "Sunday, January 1, 2012 at 5:12:00 AM GMT+05:12"
+string(46) "Sunday, 6 Tevet 5772 at 5:12:00 AM GMT+05:12"
+
+Fatal error: Uncaught IntlException: IntlDateFormatter::__construct(): datefmt_create: Invalid value for calendar type; it must be one of IntlDateFormatter::TRADITIONAL (locale's default calendar) or IntlDateFormatter::GREGORIAN. Alternatively, it can be an IntlCalendar object in %s:%d
+Stack trace:
+#0 %s(%d): IntlDateFormatter->__construct('en_US@calendar=...', 0, 0, 'GMT+05:12', -1)
+#1 {main}
+ thrown in %s on line %d
diff --git a/ext/intl/tests/dateformat_create_default.phpt b/ext/intl/tests/dateformat_create_default.phpt
index eddac36f5ea39..1479ee9a98476 100644
--- a/ext/intl/tests/dateformat_create_default.phpt
+++ b/ext/intl/tests/dateformat_create_default.phpt
@@ -4,6 +4,8 @@ IntlDateFormatter::create() with default date and time types
intl
--INI--
date.timezone=UTC
+--SKIPIF--
+= 0) die('skip for ICU < 72.1'); ?>
--FILE--
= 72.1'); ?>
+--FILE--
+format($ts), "\n";
+
+$fmt = new IntlDateFormatter('en_US');
+echo $fmt->format($ts), "\n";
+
+$fmt = datefmt_create('en_US');
+echo $fmt->format($ts), "\n";
+
+?>
+--EXPECT--
+Sunday, January 1, 2012 at 12:00:00 AM Coordinated Universal Time
+Sunday, January 1, 2012 at 12:00:00 AM Coordinated Universal Time
+Sunday, January 1, 2012 at 12:00:00 AM Coordinated Universal Time
diff --git a/ext/intl/tests/dateformat_formatObject_calendar_variant5.phpt b/ext/intl/tests/dateformat_formatObject_calendar_variant5.phpt
index 494b66768906f..9eb9f39a917a7 100644
--- a/ext/intl/tests/dateformat_formatObject_calendar_variant5.phpt
+++ b/ext/intl/tests/dateformat_formatObject_calendar_variant5.phpt
@@ -4,6 +4,7 @@ IntlDateFormatter::formatObject(): IntlCalendar tests
intl
--SKIPIF--
= 55.1'); ?>
+= 0) die('skip for ICU < 72.1'); ?>
--FILE--
= 72.1'); ?>
+--FILE--
+setTime(strtotime('2012-01-01 00:00:00')*1000.);
+echo IntlDateFormatter::formatObject($cal), "\n";
+echo IntlDateFormatter::formatObject($cal, IntlDateFormatter::FULL, "en-US"), "\n";
+
+?>
+--EXPECTF--
+01/01/2012, 00:00:00
+domingo, 1 de janeiro de 2012 às 00:00:00 Hora padrão %Sda Europa Ocidental
+Jan 1, 2012, 12:00:00 AM
+1/1/12, 12:00:00 AM Western European Standard Time
+Sun 2012-01-1 00,00,00.000 Portugal Time
+domingo, 1 de janeiro de 2012 às 05:00:00 GMT+03:00
+06/02/1433, 00:00:00
+Sunday, Safar 6, 1433 at 12:00:00 AM Western European Standard Time
diff --git a/ext/intl/tests/dateformat_formatObject_datetime_variant5.phpt b/ext/intl/tests/dateformat_formatObject_datetime_variant5.phpt
index b51a46d7f3976..b3c2ecc8e7932 100644
--- a/ext/intl/tests/dateformat_formatObject_datetime_variant5.phpt
+++ b/ext/intl/tests/dateformat_formatObject_datetime_variant5.phpt
@@ -4,6 +4,7 @@ IntlDateFormatter::formatObject(): DateTime tests
intl
--SKIPIF--
= 55.1'); ?>
+= 0) die('skip for ICU < 72.1'); ?>
--FILE--
+--FILE--
+
+--EXPECTF--
+01/01/2012, 00:00:00
+domingo, 1 de janeiro de 2012 às 00:00:00 Hora padrão %Sda Europa Ocidental
+Jan 1, 2012, 12:00:00 AM
+1/1/12, 12:00:00 AM Western European Standard Time
+Sun 2012-01-1 00,00,00.000 Portugal Time
+domingo, 1 de janeiro de 2012 às 05:00:00 GMT+03:00
diff --git a/ext/intl/tests/dateformat_format_parse_version2.phpt b/ext/intl/tests/dateformat_format_parse_version2.phpt
index 19e7d914adcfd..1293d29c383bf 100644
--- a/ext/intl/tests/dateformat_format_parse_version2.phpt
+++ b/ext/intl/tests/dateformat_format_parse_version2.phpt
@@ -4,6 +4,7 @@ datefmt_format_code() and datefmt_parse_code()
intl
--SKIPIF--
= 51.2'); ?>
+= 0) die('skip for ICU < 72.1'); ?>
--FILE--
= 72.1'); ?>
+--FILE--
+ 24 ,
+ 'tm_min' => 3,
+ 'tm_hour' => 19,
+ 'tm_mday' => 3,
+ 'tm_mon' => 3,
+ 'tm_year' => 105,
+ );
+ $localtime_arr2 = array (
+ 'tm_sec' => 21,
+ 'tm_min' => 5,
+ 'tm_hour' => 7,
+ 'tm_mday' => 13,
+ 'tm_mon' => 7,
+ 'tm_year' => 205,
+ );
+ $localtime_arr3 = array (
+ 'tm_sec' => 11,
+ 'tm_min' => 13,
+ 'tm_hour' => 0,
+ 'tm_mday' => 17,
+ 'tm_mon' => 11,
+ 'tm_year' => -5
+ );
+
+ $localtime_arr = array (
+ $localtime_arr1,
+ $localtime_arr2,
+ $localtime_arr3
+ );
+
+ //Test format and parse with a timestamp : long
+ foreach( $time_arr as $timestamp_entry){
+ $res_str .= "\n------------\n";
+ $res_str .= "\nInput timestamp is : $timestamp_entry";
+ $res_str .= "\n------------\n";
+ foreach( $locale_arr as $locale_entry ){
+ foreach( $datetype_arr as $datetype_entry ) {
+ $res_str .= "\nIntlDateFormatter locale= $locale_entry ,datetype = $datetype_entry ,timetype =$datetype_entry ";
+ $fmt = ut_datefmt_create( $locale_entry , $datetype_entry ,$datetype_entry,$timezone);
+ $formatted = ut_datefmt_format( $fmt , $timestamp_entry);
+ $res_str .= "\nFormatted timestamp is : $formatted";
+ $parsed = ut_datefmt_parse( $fmt , $formatted);
+ if( intl_get_error_code() == U_ZERO_ERROR){
+ $res_str .= "\nParsed timestamp is : $parsed";
+ }else{
+ $res_str .= "\nError while parsing as: '".intl_get_error_message()."'";
+ }
+ }
+ }
+ }
+
+ //Test format and parse with a localtime :array
+ foreach( $localtime_arr as $localtime_entry){
+ $res_str .= "\n------------\n";
+ $res_str .= "\nInput localtime is : ";
+ foreach( $localtime_entry as $key => $value){
+ $res_str .= "$key : '$value' , ";
+ }
+
+ $res_str .= "\n------------\n";
+ foreach( $locale_arr as $locale_entry ){
+ foreach( $datetype_arr as $datetype_entry ) {
+ $res_str .= "\nIntlDateFormatter locale= $locale_entry ,datetype = $datetype_entry ,timetype =$datetype_entry ";
+ $fmt = ut_datefmt_create( $locale_entry , $datetype_entry ,$datetype_entry,$timezone);
+ $formatted1 = ut_datefmt_format( $fmt , $localtime_entry);
+ if( intl_get_error_code() == U_ZERO_ERROR){
+ $res_str .= "\nFormatted localtime_array is : $formatted1";
+ }else{
+ $res_str .= "\nError while formatting as: '".intl_get_error_message()."'";
+ }
+ //Parsing
+ $parsed_arr = ut_datefmt_localtime( $fmt, $formatted1 );
+
+ if( $parsed_arr){
+ $res_str .= "\nParsed array is: ";
+ foreach( $parsed_arr as $key => $value){
+ $res_str .= "$key : '$value' , ";
+ }
+ }
+/*
+ else{
+ //$res_str .= "No values found from LocaleTime parsing.";
+ $res_str .= "\tError : '".intl_get_error_message()."'";
+ }
+*/
+ }
+ }
+ }
+
+ return $res_str;
+
+}
+
+include_once( 'ut_common.inc' );
+
+// Run the test
+ut_run();
+?>
+--EXPECT--
+------------
+
+Input timestamp is : 0
+------------
+
+IntlDateFormatter locale= en_US ,datetype = 0 ,timetype =0
+Formatted timestamp is : Thursday, January 1, 1970 at 5:00:00 AM GMT+05:00
+Parsed timestamp is : 0
+IntlDateFormatter locale= en_US ,datetype = 1 ,timetype =1
+Formatted timestamp is : January 1, 1970 at 5:00:00 AM GMT+5
+Parsed timestamp is : 0
+IntlDateFormatter locale= en_US ,datetype = 2 ,timetype =2
+Formatted timestamp is : Jan 1, 1970, 5:00:00 AM
+Parsed timestamp is : 0
+------------
+
+Input timestamp is : -1200000
+------------
+
+IntlDateFormatter locale= en_US ,datetype = 0 ,timetype =0
+Formatted timestamp is : Thursday, December 18, 1969 at 7:40:00 AM GMT+05:00
+Parsed timestamp is : -1200000
+IntlDateFormatter locale= en_US ,datetype = 1 ,timetype =1
+Formatted timestamp is : December 18, 1969 at 7:40:00 AM GMT+5
+Parsed timestamp is : -1200000
+IntlDateFormatter locale= en_US ,datetype = 2 ,timetype =2
+Formatted timestamp is : Dec 18, 1969, 7:40:00 AM
+Parsed timestamp is : -1200000
+------------
+
+Input timestamp is : 1200000
+------------
+
+IntlDateFormatter locale= en_US ,datetype = 0 ,timetype =0
+Formatted timestamp is : Thursday, January 15, 1970 at 2:20:00 AM GMT+05:00
+Parsed timestamp is : 1200000
+IntlDateFormatter locale= en_US ,datetype = 1 ,timetype =1
+Formatted timestamp is : January 15, 1970 at 2:20:00 AM GMT+5
+Parsed timestamp is : 1200000
+IntlDateFormatter locale= en_US ,datetype = 2 ,timetype =2
+Formatted timestamp is : Jan 15, 1970, 2:20:00 AM
+Parsed timestamp is : 1200000
+------------
+
+Input timestamp is : 2200000000
+------------
+
+IntlDateFormatter locale= en_US ,datetype = 0 ,timetype =0
+Formatted timestamp is : Monday, September 19, 2039 at 4:06:40 AM GMT+05:00
+Parsed timestamp is : 2200000000
+IntlDateFormatter locale= en_US ,datetype = 1 ,timetype =1
+Formatted timestamp is : September 19, 2039 at 4:06:40 AM GMT+5
+Parsed timestamp is : 2200000000
+IntlDateFormatter locale= en_US ,datetype = 2 ,timetype =2
+Formatted timestamp is : Sep 19, 2039, 4:06:40 AM
+Parsed timestamp is : 2200000000
+------------
+
+Input timestamp is : -2200000000
+------------
+
+IntlDateFormatter locale= en_US ,datetype = 0 ,timetype =0
+Formatted timestamp is : Sunday, April 15, 1900 at 5:53:20 AM GMT+05:00
+Parsed timestamp is : -2200000000
+IntlDateFormatter locale= en_US ,datetype = 1 ,timetype =1
+Formatted timestamp is : April 15, 1900 at 5:53:20 AM GMT+5
+Parsed timestamp is : -2200000000
+IntlDateFormatter locale= en_US ,datetype = 2 ,timetype =2
+Formatted timestamp is : Apr 15, 1900, 5:53:20 AM
+Parsed timestamp is : -2200000000
+------------
+
+Input timestamp is : 90099999
+------------
+
+IntlDateFormatter locale= en_US ,datetype = 0 ,timetype =0
+Formatted timestamp is : Thursday, November 9, 1972 at 12:46:39 AM GMT+05:00
+Parsed timestamp is : 90099999
+IntlDateFormatter locale= en_US ,datetype = 1 ,timetype =1
+Formatted timestamp is : November 9, 1972 at 12:46:39 AM GMT+5
+Parsed timestamp is : 90099999
+IntlDateFormatter locale= en_US ,datetype = 2 ,timetype =2
+Formatted timestamp is : Nov 9, 1972, 12:46:39 AM
+Parsed timestamp is : 90099999
+------------
+
+Input timestamp is : 3600
+------------
+
+IntlDateFormatter locale= en_US ,datetype = 0 ,timetype =0
+Formatted timestamp is : Thursday, January 1, 1970 at 6:00:00 AM GMT+05:00
+Parsed timestamp is : 3600
+IntlDateFormatter locale= en_US ,datetype = 1 ,timetype =1
+Formatted timestamp is : January 1, 1970 at 6:00:00 AM GMT+5
+Parsed timestamp is : 3600
+IntlDateFormatter locale= en_US ,datetype = 2 ,timetype =2
+Formatted timestamp is : Jan 1, 1970, 6:00:00 AM
+Parsed timestamp is : 3600
+------------
+
+Input timestamp is : -3600
+------------
+
+IntlDateFormatter locale= en_US ,datetype = 0 ,timetype =0
+Formatted timestamp is : Thursday, January 1, 1970 at 4:00:00 AM GMT+05:00
+Parsed timestamp is : -3600
+IntlDateFormatter locale= en_US ,datetype = 1 ,timetype =1
+Formatted timestamp is : January 1, 1970 at 4:00:00 AM GMT+5
+Parsed timestamp is : -3600
+IntlDateFormatter locale= en_US ,datetype = 2 ,timetype =2
+Formatted timestamp is : Jan 1, 1970, 4:00:00 AM
+Parsed timestamp is : -3600
+------------
+
+Input localtime is : tm_sec : '24' , tm_min : '3' , tm_hour : '19' , tm_mday : '3' , tm_mon : '3' , tm_year : '105' ,
+------------
+
+IntlDateFormatter locale= en_US ,datetype = 0 ,timetype =0
+Formatted localtime_array is : Sunday, April 3, 2005 at 7:03:24 PM GMT+05:00
+Parsed array is: tm_sec : '24' , tm_min : '3' , tm_hour : '19' , tm_year : '105' , tm_mday : '3' , tm_wday : '0' , tm_yday : '93' , tm_mon : '3' , tm_isdst : '0' ,
+IntlDateFormatter locale= en_US ,datetype = 1 ,timetype =1
+Formatted localtime_array is : April 3, 2005 at 7:03:24 PM GMT+5
+Parsed array is: tm_sec : '24' , tm_min : '3' , tm_hour : '19' , tm_year : '105' , tm_mday : '3' , tm_wday : '0' , tm_yday : '93' , tm_mon : '3' , tm_isdst : '0' ,
+IntlDateFormatter locale= en_US ,datetype = 2 ,timetype =2
+Formatted localtime_array is : Apr 3, 2005, 7:03:24 PM
+Parsed array is: tm_sec : '24' , tm_min : '3' , tm_hour : '19' , tm_year : '105' , tm_mday : '3' , tm_wday : '0' , tm_yday : '93' , tm_mon : '3' , tm_isdst : '0' ,
+------------
+
+Input localtime is : tm_sec : '21' , tm_min : '5' , tm_hour : '7' , tm_mday : '13' , tm_mon : '7' , tm_year : '205' ,
+------------
+
+IntlDateFormatter locale= en_US ,datetype = 0 ,timetype =0
+Formatted localtime_array is : Thursday, August 13, 2105 at 7:05:21 AM GMT+05:00
+Parsed array is: tm_sec : '21' , tm_min : '5' , tm_hour : '7' , tm_year : '205' , tm_mday : '13' , tm_wday : '4' , tm_yday : '225' , tm_mon : '7' , tm_isdst : '0' ,
+IntlDateFormatter locale= en_US ,datetype = 1 ,timetype =1
+Formatted localtime_array is : August 13, 2105 at 7:05:21 AM GMT+5
+Parsed array is: tm_sec : '21' , tm_min : '5' , tm_hour : '7' , tm_year : '205' , tm_mday : '13' , tm_wday : '4' , tm_yday : '225' , tm_mon : '7' , tm_isdst : '0' ,
+IntlDateFormatter locale= en_US ,datetype = 2 ,timetype =2
+Formatted localtime_array is : Aug 13, 2105, 7:05:21 AM
+Parsed array is: tm_sec : '21' , tm_min : '5' , tm_hour : '7' , tm_year : '205' , tm_mday : '13' , tm_wday : '4' , tm_yday : '225' , tm_mon : '7' , tm_isdst : '0' ,
+------------
+
+Input localtime is : tm_sec : '11' , tm_min : '13' , tm_hour : '0' , tm_mday : '17' , tm_mon : '11' , tm_year : '-5' ,
+------------
+
+IntlDateFormatter locale= en_US ,datetype = 0 ,timetype =0
+Formatted localtime_array is : Tuesday, December 17, 1895 at 12:13:11 AM GMT+05:00
+Parsed array is: tm_sec : '11' , tm_min : '13' , tm_hour : '0' , tm_year : '-5' , tm_mday : '17' , tm_wday : '2' , tm_yday : '351' , tm_mon : '11' , tm_isdst : '0' ,
+IntlDateFormatter locale= en_US ,datetype = 1 ,timetype =1
+Formatted localtime_array is : December 17, 1895 at 12:13:11 AM GMT+5
+Parsed array is: tm_sec : '11' , tm_min : '13' , tm_hour : '0' , tm_year : '-5' , tm_mday : '17' , tm_wday : '2' , tm_yday : '351' , tm_mon : '11' , tm_isdst : '0' ,
+IntlDateFormatter locale= en_US ,datetype = 2 ,timetype =2
+Formatted localtime_array is : Dec 17, 1895, 12:13:11 AM
+Parsed array is: tm_sec : '11' , tm_min : '13' , tm_hour : '0' , tm_year : '-5' , tm_mday : '17' , tm_wday : '2' , tm_yday : '351' , tm_mon : '11' , tm_isdst : '0' ,
diff --git a/ext/intl/tests/dateformat_set_timezone_id3.phpt b/ext/intl/tests/dateformat_set_timezone_id3.phpt
index e0d7cd0e52607..a4952eb5a3dcb 100644
--- a/ext/intl/tests/dateformat_set_timezone_id3.phpt
+++ b/ext/intl/tests/dateformat_set_timezone_id3.phpt
@@ -6,6 +6,7 @@ date.timezone=Atlantic/Azores
intl
--SKIPIF--
= 51.2'); ?>
+= 0) die('skip for ICU < 72.1'); ?>
--FILE--
= 4.8
+--INI--
+date.timezone=Atlantic/Azores
+--EXTENSIONS--
+intl
+--SKIPIF--
+= 72.1'); ?>
+--FILE--
+
+--EXPECTF--
+Warning: IntlDateFormatter::setTimeZone(): datefmt_set_timezone: No such time zone: 'CN' in %sut_common.inc on line %d
+
+Warning: datefmt_set_timezone(): datefmt_set_timezone: No such time zone: 'CN' in %sut_common.inc on line %d
+
+After creation of the dateformatter : timezone_id= US/Pacific
+-----------
+Trying to set timezone_id= America/New_York
+After call to set_timezone_id : timezone_id= America/New_York
+Formatting timestamp=0 resulted in Wednesday, December 31, 1969 at 7:00:00 PM Eastern Standard Time
+Formatting timestamp=3600 resulted in Wednesday, December 31, 1969 at 8:00:00 PM Eastern Standard Time
+-----------
+Trying to set timezone_id= America/Los_Angeles
+After call to set_timezone_id : timezone_id= America/Los_Angeles
+Formatting timestamp=0 resulted in Wednesday, December 31, 1969 at 4:00:00 PM Pacific Standard Time
+Formatting timestamp=3600 resulted in Wednesday, December 31, 1969 at 5:00:00 PM Pacific Standard Time
+-----------
+Trying to set timezone_id= America/Chicago
+After call to set_timezone_id : timezone_id= America/Chicago
+Formatting timestamp=0 resulted in Wednesday, December 31, 1969 at 6:00:00 PM Central Standard Time
+Formatting timestamp=3600 resulted in Wednesday, December 31, 1969 at 7:00:00 PM Central Standard Time
+-----------
+Trying to set timezone_id= CN
+After call to set_timezone_id : timezone_id= America/Chicago
+Formatting timestamp=0 resulted in Wednesday, December 31, 1969 at 6:00:00 PM Central Standard Time
+Formatting timestamp=3600 resulted in Wednesday, December 31, 1969 at 7:00:00 PM Central Standard Time
diff --git a/ext/intl/tests/datepatterngenerator_get_best_pattern.phpt b/ext/intl/tests/datepatterngenerator_get_best_pattern.phpt
index 9e29bc90493e9..279554123b795 100644
--- a/ext/intl/tests/datepatterngenerator_get_best_pattern.phpt
+++ b/ext/intl/tests/datepatterngenerator_get_best_pattern.phpt
@@ -2,6 +2,8 @@
IntlDatePatternGenerator::getBestPattern()
--EXTENSIONS--
intl
+--SKIPIF--
+= 0) die('skip for ICU < 72.1'); ?>
--FILE--
= 72.1'); ?>
+--FILE--
+getBestPattern("jjmm"), "\n";
+echo $dtpg2->getBestPattern("jjmm"), "\n";
+echo $dtpg3->getBestPattern("YYYYMMMdd"), "\n";
+echo $dtpg4->getBestPattern("YYYYMMMdd"), "\n";
+
+echo $dtpg->getBestPattern(""), "\n";
+
+try {
+ $dtpg->getBestPattern();
+} catch(\ArgumentCountError $e) {
+ echo $e->getMessage(), "\n";
+}
+
+?>
+--EXPECT--
+h:mm a
+HH:mm
+MMM dd, YYYY
+dd. MMM YYYY
+
+IntlDatePatternGenerator::getBestPattern() expects exactly 1 argument, 0 given
diff --git a/ext/intl/tests/msgfmt_format_datetime.phpt b/ext/intl/tests/msgfmt_format_datetime.phpt
index 8b3e37ab30f5d..e6a7451906f77 100644
--- a/ext/intl/tests/msgfmt_format_datetime.phpt
+++ b/ext/intl/tests/msgfmt_format_datetime.phpt
@@ -4,6 +4,8 @@ MessageFormatter::format(): DateTime accepted to format dates and times
date.timezone=Atlantic/Azores
--EXTENSIONS--
intl
+--SKIPIF--
+= 0) die('skip for ICU < 72.1'); ?>
--FILE--
= 72.1'); ?>
+--FILE--
+format(array($dt)));
+var_dump($mf->format(array($dti)));
+
+?>
+--EXPECT--
+string(24) "May 6, 2012 5:00:42 PM"
+string(24) "May 6, 2012 5:00:42 PM"
diff --git a/ext/intl/tests/msgfmt_format_simple_types_numeric_strings.phpt b/ext/intl/tests/msgfmt_format_simple_types_numeric_strings.phpt
index 9bc38b683cd0a..f92eaa2bdb54a 100644
--- a/ext/intl/tests/msgfmt_format_simple_types_numeric_strings.phpt
+++ b/ext/intl/tests/msgfmt_format_simple_types_numeric_strings.phpt
@@ -4,6 +4,8 @@ MessageFormatter::format(): simple types handling with numeric strings
date.timezone=Atlantic/Azores
--EXTENSIONS--
intl
+--SKIPIF--
+= 0) die('skip for ICU < 72.1'); ?>
--FILE--
= 72.1'); ?>
+--FILE--
+format(array(
+'a' => $ex,
+'b' => $ex,
+'c' => $ex,
+'d' => $ex,
+'e' => $ex,
+'f' => " 1336317965.5",
+'g' => " 1336317965.5",
+'h' => $ex,
+'i' => $ex,
+'j' => $ex,
+)));
+
+?>
+--EXPECTF--
+string(%d) "
+ none 1336317965.5 str
+ number 1,336,317,965.5
+ number integer 1,336,317,965
+ number currency $1,336,317,965.50
+ number percent 133,631,796,550%
+ date May %d, 2012
+ time 3:26:05 PM
+ spellout one billion three hundred thirty-six million three hundred seventeen thousand nine hundred sixty-five point five
+ ordinal 1,336,317,966th
+ duration 371,199:26:06
+"
diff --git a/ext/intl/tests/resourcebundle_null_mandatory_args_variant2.phpt b/ext/intl/tests/resourcebundle_null_mandatory_args_variant2.phpt
index 31250027184b3..5aefcf268dff5 100644
--- a/ext/intl/tests/resourcebundle_null_mandatory_args_variant2.phpt
+++ b/ext/intl/tests/resourcebundle_null_mandatory_args_variant2.phpt
@@ -6,6 +6,7 @@ date.timezone=Atlantic/Azores
intl
--SKIPIF--
= 51.2'); ?>
+= 0) die('skip for ICU < 72.1'); ?>
--FILE--
= 72.1'); ?>
+--FILE--
+get('calendar')->get('gregorian')->get('DateTimePatterns')->get(0);
+var_dump($c);
+
+ini_set('intl.default_locale', 'pt_PT');
+$r = new ResourceBundle(NULL, NULL);
+$c = $r->get('calendar')->get('gregorian')->get('DateTimePatterns')->get(0);
+var_dump($c);
+?>
+--EXPECT--
+string(16) "h:mm:ss a zzzz"
+string(13) "HH:mm:ss zzzz"
diff --git a/ext/ldap/ldap.c b/ext/ldap/ldap.c
index b8fb4274215ff..51eca3d7cc4de 100644
--- a/ext/ldap/ldap.c
+++ b/ext/ldap/ldap.c
@@ -121,7 +121,7 @@ static zend_object *ldap_link_create_object(zend_class_entry *class_type) {
}
static zend_function *ldap_link_get_constructor(zend_object *object) {
- zend_throw_error(NULL, "Cannot directly construct LDAP\\Connection, use ldap_create() instead");
+ zend_throw_error(NULL, "Cannot directly construct LDAP\\Connection, use ldap_connect() instead");
return NULL;
}
diff --git a/ext/ldap/tests/ldap_constructor.phpt b/ext/ldap/tests/ldap_constructor.phpt
index 37db6112dcee5..6c79dee8b3982 100644
--- a/ext/ldap/tests/ldap_constructor.phpt
+++ b/ext/ldap/tests/ldap_constructor.phpt
@@ -11,4 +11,4 @@ try {
echo "Exception: ", $ex->getMessage(), "\n";
}
--EXPECT--
-Exception: Cannot directly construct LDAP\Connection, use ldap_create() instead
+Exception: Cannot directly construct LDAP\Connection, use ldap_connect() instead
diff --git a/ext/mbstring/libmbfl/filters/mbfilter_sjis.c b/ext/mbstring/libmbfl/filters/mbfilter_sjis.c
index 1f52e0aa97786..f23a8b08aceab 100644
--- a/ext/mbstring/libmbfl/filters/mbfilter_sjis.c
+++ b/ext/mbstring/libmbfl/filters/mbfilter_sjis.c
@@ -40,7 +40,7 @@ static int mbfl_filt_conv_sjis_wchar_flush(mbfl_convert_filter *filter);
static size_t mb_sjis_to_wchar(unsigned char **in, size_t *in_len, uint32_t *buf, size_t bufsize, unsigned int *state);
static void mb_wchar_to_sjis(uint32_t *in, size_t len, mb_convert_buf *buf, bool end);
-const unsigned char mblen_table_sjis[] = { /* 0x80-0x9f,0xE0-0xFF */
+const unsigned char mblen_table_sjis[] = { /* 0x81-0x9F,0xE0-0xEF */
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
@@ -49,14 +49,14 @@ const unsigned char mblen_table_sjis[] = { /* 0x80-0x9f,0xE0-0xFF */
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
};
static const char *mbfl_encoding_sjis_aliases[] = {"x-sjis", "SHIFT-JIS", NULL};
diff --git a/ext/mbstring/libmbfl/filters/mbfilter_sjis_2004.c b/ext/mbstring/libmbfl/filters/mbfilter_sjis_2004.c
index 78d4bd431071f..737871eda8a31 100644
--- a/ext/mbstring/libmbfl/filters/mbfilter_sjis_2004.c
+++ b/ext/mbstring/libmbfl/filters/mbfilter_sjis_2004.c
@@ -41,7 +41,7 @@
#include "unicode_table_jis2004.h"
#include "unicode_table_jis.h"
-extern const unsigned char mblen_table_sjis[];
+extern const unsigned char mblen_table_sjis_mobile[];
extern const unsigned char mblen_table_eucjp[];
static size_t mb_sjis2004_to_wchar(unsigned char **in, size_t *in_len, uint32_t *buf, size_t bufsize, unsigned int *state);
@@ -62,7 +62,7 @@ const mbfl_encoding mbfl_encoding_sjis2004 = {
"SJIS-2004",
"Shift_JIS",
mbfl_encoding_sjis2004_aliases,
- mblen_table_sjis,
+ mblen_table_sjis_mobile, /* Leading byte values used for SJIS-2004 are the same as mobile SJIS variants */
MBFL_ENCTYPE_GL_UNSAFE,
&vtbl_sjis2004_wchar,
&vtbl_wchar_sjis2004,
diff --git a/ext/mbstring/libmbfl/filters/mbfilter_sjis_mac.c b/ext/mbstring/libmbfl/filters/mbfilter_sjis_mac.c
index 0cb93cc38e8c7..0ff2a198d36c8 100644
--- a/ext/mbstring/libmbfl/filters/mbfilter_sjis_mac.c
+++ b/ext/mbstring/libmbfl/filters/mbfilter_sjis_mac.c
@@ -35,7 +35,24 @@
#include "sjis_mac2uni.h"
-extern const unsigned char mblen_table_sjis[];
+const unsigned char mblen_table_sjismac[] = { /* 0x81-0x9F,0xE0-0xED */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+};
static int mbfl_filt_conv_wchar_sjis_mac_flush(mbfl_convert_filter *filter);
static int mbfl_filt_conv_sjis_mac_wchar_flush(mbfl_convert_filter *filter);
@@ -49,7 +66,7 @@ const mbfl_encoding mbfl_encoding_sjis_mac = {
"SJIS-mac",
"Shift_JIS",
mbfl_encoding_sjis_mac_aliases,
- mblen_table_sjis,
+ mblen_table_sjismac,
MBFL_ENCTYPE_GL_UNSAFE,
&vtbl_sjis_mac_wchar,
&vtbl_wchar_sjis_mac,
diff --git a/ext/mbstring/libmbfl/filters/mbfilter_sjis_mobile.c b/ext/mbstring/libmbfl/filters/mbfilter_sjis_mobile.c
index 31a1e3a4d777f..448e0a74ca3aa 100644
--- a/ext/mbstring/libmbfl/filters/mbfilter_sjis_mobile.c
+++ b/ext/mbstring/libmbfl/filters/mbfilter_sjis_mobile.c
@@ -35,8 +35,26 @@
#include "emoji2uni.h"
+const unsigned char mblen_table_sjis_mobile[] = { /* 0x81-0x9F,0xE0-0xFC */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1
+};
+
extern int mbfl_bisec_srch2(int w, const unsigned short tbl[], int n);
-extern const unsigned char mblen_table_sjis[];
static int mbfl_filt_conv_sjis_wchar_flush(mbfl_convert_filter *filter);
static size_t mb_sjis_docomo_to_wchar(unsigned char **in, size_t *in_len, uint32_t *buf, size_t bufsize, unsigned int *state);
@@ -55,7 +73,7 @@ const mbfl_encoding mbfl_encoding_sjis_docomo = {
"SJIS-Mobile#DOCOMO",
"Shift_JIS",
mbfl_encoding_sjis_docomo_aliases,
- mblen_table_sjis,
+ mblen_table_sjis_mobile,
MBFL_ENCTYPE_GL_UNSAFE,
&vtbl_sjis_docomo_wchar,
&vtbl_wchar_sjis_docomo,
@@ -68,7 +86,7 @@ const mbfl_encoding mbfl_encoding_sjis_kddi = {
"SJIS-Mobile#KDDI",
"Shift_JIS",
mbfl_encoding_sjis_kddi_aliases,
- mblen_table_sjis,
+ mblen_table_sjis_mobile,
MBFL_ENCTYPE_GL_UNSAFE,
&vtbl_sjis_kddi_wchar,
&vtbl_wchar_sjis_kddi,
@@ -81,7 +99,7 @@ const mbfl_encoding mbfl_encoding_sjis_sb = {
"SJIS-Mobile#SOFTBANK",
"Shift_JIS",
mbfl_encoding_sjis_sb_aliases,
- mblen_table_sjis,
+ mblen_table_sjis_mobile,
MBFL_ENCTYPE_GL_UNSAFE,
&vtbl_sjis_sb_wchar,
&vtbl_wchar_sjis_sb,
diff --git a/ext/mbstring/mbstring.c b/ext/mbstring/mbstring.c
index 9735bfef80bb2..34678ea3d0f7f 100644
--- a/ext/mbstring/mbstring.c
+++ b/ext/mbstring/mbstring.c
@@ -3185,10 +3185,7 @@ PHP_FUNCTION(mb_convert_kana)
* or all FW hiragana to FW katakana, or all FW katakana to FW hiragana, but not
* more than one of these */
if (opt & MBFL_ZEN2HAN_HIRAGANA) {
- if (opt & MBFL_ZEN2HAN_KATAKANA) {
- zend_argument_value_error(2, "must not combine 'h' and 'k' flags");
- RETURN_THROWS();
- } else if (opt & MBFL_ZENKAKU_HIRA2KATA) {
+ if (opt & MBFL_ZENKAKU_HIRA2KATA) {
zend_argument_value_error(2, "must not combine 'h' and 'C' flags");
RETURN_THROWS();
} else if (opt & MBFL_ZENKAKU_KATA2HIRA) {
diff --git a/ext/mbstring/tests/mb_convert_kana.phpt b/ext/mbstring/tests/mb_convert_kana.phpt
index 69ea5560d4803..b263fec0f1b67 100644
--- a/ext/mbstring/tests/mb_convert_kana.phpt
+++ b/ext/mbstring/tests/mb_convert_kana.phpt
@@ -78,6 +78,7 @@ echo bin2hex(mb_convert_kana("\x30\x9B", 'k', 'UTF-16BE')), "\n";
echo bin2hex(mb_convert_kana("\x30\x9C", 'k', 'UTF-16BE')), "\n";
echo bin2hex(mb_convert_kana("\x30\xFC", 'k', 'UTF-16BE')), "\n";
echo bin2hex(mb_convert_kana("\x30\xFB", 'k', 'UTF-16BE')), "\n";
+echo bin2hex(mb_convert_kana("fooあいうエオ", "rnaskh", "UTF-8")), "\n";
echo "Including one which will expand to two codepoints:\n";
echo bin2hex(mb_convert_kana("\x30\x52", 'h', 'UTF-16BE')), "\n\n";
@@ -117,7 +118,6 @@ tryIncompatibleFlags('R', 'r');
tryIncompatibleFlags('N', 'n');
tryIncompatibleFlags('S', 's');
tryIncompatibleFlags('K', 'H');
-tryIncompatibleFlags('k', 'h');
tryIncompatibleFlags('C', 'c');
tryIncompatibleFlags('M', 'm');
tryIncompatibleFlags('h', 'C');
@@ -204,6 +204,7 @@ ff9e
ff9f
ff70
ff65
+666f6fefbdb1efbdb2efbdb3efbdb4efbdb5
Including one which will expand to two codepoints:
ff79ff9e
@@ -237,8 +238,6 @@ mb_convert_kana(): Argument #2 ($mode) must not combine 'S' and 's' flags
mb_convert_kana(): Argument #2 ($mode) must not combine 'S' and 's' flags
mb_convert_kana(): Argument #2 ($mode) must not combine 'H' and 'K' flags
mb_convert_kana(): Argument #2 ($mode) must not combine 'H' and 'K' flags
-mb_convert_kana(): Argument #2 ($mode) must not combine 'h' and 'k' flags
-mb_convert_kana(): Argument #2 ($mode) must not combine 'h' and 'k' flags
mb_convert_kana(): Argument #2 ($mode) must not combine 'C' and 'c' flags
mb_convert_kana(): Argument #2 ($mode) must not combine 'C' and 'c' flags
mb_convert_kana(): Argument #2 ($mode) must not combine 'M' and 'm' flags
diff --git a/ext/mbstring/tests/mb_str_split_jp.phpt b/ext/mbstring/tests/mb_str_split_jp.phpt
index 22f39539608c3..41a123d58975f 100644
--- a/ext/mbstring/tests/mb_str_split_jp.phpt
+++ b/ext/mbstring/tests/mb_str_split_jp.phpt
@@ -69,6 +69,17 @@ if(end($array) !== $enc){
last array element: %s expected: %s\n", unpack("H*", end($array))[1],unpack("H*", $enc)[1]);
}
+/* SJIS byte 0x80 was previously wrongly treated as the starting byte for a 2-byte character */
+echo "== Regression test for SJIS byte 0x80 ==\n";
+foreach (['SJIS', 'SJIS-2004', 'MacJapanese', 'SJIS-Mobile#DOCOMO', 'SJIS-Mobile#KDDI', 'SJIS-Mobile#SoftBank'] as $encoding) {
+ $array = mb_str_split("\x80\xA1abc\x80\xA1", 2, $encoding);
+ echo "$encoding: [" . implode(', ', array_map('bin2hex', $array)) . "]\n";
+
+ // Also try bytes 0xFD, 0xFE, and 0xFF
+ $array = mb_str_split("abc\xFD\xFE\xFFab\xFD\xFE\xFF", 2, $encoding);
+ echo "$encoding: [" . implode(', ', array_map('bin2hex', $array)) . "]\n";
+}
+
?>
--EXPECT--
BIG-5: a4e9 a5bb
@@ -80,3 +91,16 @@ UTF-16LE: e565 2c67
UTF-32BE: 000065e5 0000672c
UTF-32LE: e5650000 2c670000
UTF-8: e697a5 e69cac
+== Regression test for SJIS byte 0x80 ==
+SJIS: [80a1, 6162, 6380, a1]
+SJIS: [6162, 63fd, feff, 6162, fdfe, ff]
+SJIS-2004: [80a1, 6162, 6380, a1]
+SJIS-2004: [6162, 63fd, feff, 6162, fdfe, ff]
+MacJapanese: [80a1, 6162, 6380, a1]
+MacJapanese: [6162, 63fd, feff, 6162, fdfe, ff]
+SJIS-Mobile#DOCOMO: [80a1, 6162, 6380, a1]
+SJIS-Mobile#DOCOMO: [6162, 63fd, feff, 6162, fdfe, ff]
+SJIS-Mobile#KDDI: [80a1, 6162, 6380, a1]
+SJIS-Mobile#KDDI: [6162, 63fd, feff, 6162, fdfe, ff]
+SJIS-Mobile#SoftBank: [80a1, 6162, 6380, a1]
+SJIS-Mobile#SoftBank: [6162, 63fd, feff, 6162, fdfe, ff]
diff --git a/ext/mbstring/tests/mb_strlen.phpt b/ext/mbstring/tests/mb_strlen.phpt
index 11225917140c2..5ebfcd1aec065 100644
--- a/ext/mbstring/tests/mb_strlen.phpt
+++ b/ext/mbstring/tests/mb_strlen.phpt
@@ -13,43 +13,59 @@ include_once('common.inc');
mb_detect_order('auto');
// Test string
-$euc_jp = '0123����ʸ��������ܸ�Ǥ���EUC-JP��ȤäƤ��ޤ���0123���ܸ�����ݽ�����';
+$euc_jp = mb_convert_encoding("0123この文字列は日本語です。EUC-JPを使っています。0123日本語は面倒臭い。", 'EUC-JP', 'UTF-8');
$ascii = 'abcdefghijklmnopqrstuvwxyz;]=#0123456789';
-// ASCII
echo "== ASCII ==\n";
-print mb_strlen($ascii,'ASCII') . "\n";
-print strlen($ascii) . "\n";
+print mb_strlen($ascii,'ASCII') . "\n";
+print strlen($ascii) . "\n";
-// EUC-JP
echo "== EUC-JP ==\n";
-print mb_strlen($euc_jp,'EUC-JP') . "\n";
+print mb_strlen($euc_jp,'EUC-JP') . "\n";
mb_internal_encoding('EUC-JP') or print("mb_internal_encoding() failed\n");
-print strlen($euc_jp) . "\n";
+print strlen($euc_jp) . "\n";
-// SJIS
echo "== SJIS ==\n";
$sjis = mb_convert_encoding($euc_jp, 'SJIS','EUC-JP');
-print mb_strlen($sjis,'SJIS') . "\n";
+print mb_strlen($sjis,'SJIS') . "\n";
mb_internal_encoding('SJIS') or print("mb_internal_encoding() failed\n");
-print strlen($sjis) . "\n";
+print strlen($sjis) . "\n";
+print "-- Testing illegal bytes 0x80,0xFD-FF --\n";
+// mb_strlen used to wrongly treat 0x80 as the starting byte of a 2-byte SJIS character
+print mb_strlen("\x80\xA1", 'SJIS') . "\n";
+print mb_strlen("abc\xFD\xFE\xFF", 'SJIS') . "\n";
+
+echo "== MacJapanese ==\n";
+print mb_strlen("\x80\xA1", 'MacJapanese') . "\n";
+print mb_strlen("abc\xFD\xFE\xFF", 'MacJapanese') . "\n";
+
+echo "== SJIS-2004 ==\n";
+print mb_strlen("\x80\xA1", 'SJIS-2004') . "\n";
+print mb_strlen("abc\xFD\xFE\xFF", 'SJIS-2004') . "\n";
+
+echo "== SJIS-Mobile#DOCOMO ==\n";
+print mb_strlen("\x80\xA1", 'SJIS-Mobile#DOCOMO') . "\n";
+print mb_strlen("abc\xFD\xFE\xFF", 'SJIS-Mobile#DOCOMO') . "\n";
+
+echo "== SJIS-Mobile#KDDI ==\n";
+print mb_strlen("\x80\xA1", 'SJIS-Mobile#KDDI') . "\n";
+print mb_strlen("abc\xFD\xFE\xFF", 'SJIS-Mobile#KDDI') . "\n";
+
+echo "== SJIS-Mobile#SoftBank ==\n";
+print mb_strlen("\x80\xA1", 'SJIS-Mobile#SoftBank') . "\n";
+print mb_strlen("abc\xFD\xFE\xFF", 'SJIS-Mobile#SoftBank') . "\n";
-// JIS
-// Note: either convert_encoding or strlen has problem
echo "== JIS ==\n";
$jis = mb_convert_encoding($euc_jp, 'JIS','EUC-JP');
-print mb_strlen($jis,'JIS') . "\n";
+print mb_strlen($jis,'JIS') . "\n";
mb_internal_encoding('JIS') or print("mb_internal_encoding() failed\n");
-print strlen($jis) . "\n";
+print strlen($jis) . "\n";
-// UTF-8
-// Note: either convert_encoding or strlen has problem
echo "== UTF-8 ==\n";
$utf8 = mb_convert_encoding($euc_jp, 'UTF-8','EUC-JP');
-print mb_strlen($utf8,'UTF-8') . "\n";
+print mb_strlen($utf8,'UTF-8') . "\n";
mb_internal_encoding('UTF-8') or print("mb_internal_encoding() failed\n");
-print strlen($utf8) . "\n";
-
+print strlen($utf8) . "\n";
// Wrong Parameters
echo "== WRONG PARAMETERS ==\n";
@@ -72,6 +88,24 @@ try {
== SJIS ==
43
72
+-- Testing illegal bytes 0x80,0xFD-FF --
+2
+6
+== MacJapanese ==
+2
+6
+== SJIS-2004 ==
+2
+6
+== SJIS-Mobile#DOCOMO ==
+2
+6
+== SJIS-Mobile#KDDI ==
+2
+6
+== SJIS-Mobile#SoftBank ==
+2
+6
== JIS ==
43
90
diff --git a/ext/mbstring/tests/mb_substr.phpt b/ext/mbstring/tests/mb_substr.phpt
index 6d5e9d42ac0da..5bd4a5e67f4e2 100644
--- a/ext/mbstring/tests/mb_substr.phpt
+++ b/ext/mbstring/tests/mb_substr.phpt
@@ -8,13 +8,13 @@ ini_set('include_path','.');
include_once('common.inc');
// EUC-JP
-$euc_jp = "0123\xA4\xB3\xA4\xCE\xCA\xB8\xBB\xFA\xCE\xF3\xA4\xCF\xC6\xFC\xCB\xDC\xB8\xEC\xA4\xC7\xA4\xB9\xA1\xA3EUC-JP\xA4\xF2\xBB\xC8\xA4\xC3\xA4\xC6\xA4\xA4\xA4\xDE\xA4\xB9\xA1\xA3\xC6\xFC\xCB\xDC\xB8\xEC\xA4\xCF\xCC\xCC\xC5\xDD\xBD\xAD\xA4\xA4\xA1\xA3";
+$euc_jp = mb_convert_encoding('0123この文字列は日本語です。EUC-JPを使っています。日本語は面倒臭い。', 'EUC-JP', 'UTF-8');
// SJIS
-$sjis = "\x93\xFA\x96{\x8C\xEA\x83e\x83L\x83X\x83g\x82\xC5\x82\xB7\x81B01234\x82T\x82U\x82V\x82W\x82X\x81B";
+$sjis = mb_convert_encoding('日本語テキストです。0123456789。', 'SJIS', 'UTF-8');
// ISO-2022-JP
$iso2022jp = "\x1B\$B\x21\x21!r\x1B(BABC";
// GB-18030
-$gb18030 = "\xC3\xDC\xC2\xEB\xD3\xC3\xBB\xA7\xC3\xFB\xC3\xDC\xC2\xEB\xC3\xFB\xB3\xC6\xC3\xFB\xB3\xC6";
+$gb18030 = mb_convert_encoding('密码用户名密码名称名称', 'GB18030', 'UTF-8');
// HZ
$hz = "The next sentence is in GB.~{<:Ky2;S{#,NpJ)l6HK!#~}Bye.";
// UTF-8
@@ -40,6 +40,29 @@ print "2: " . bin2hex(mb_substr($sjis, -1, null, 'SJIS')) . "\n";
print "3: " . bin2hex(mb_substr($sjis, -5, 3, 'SJIS')) . "\n";
print "4: " . bin2hex(mb_substr($sjis, 1, null, 'SJIS')) . "\n";
print "5:" . bin2hex(mb_substr($sjis, 10, 0, 'SJIS')) . "\n";
+echo "-- Testing illegal SJIS byte 0x80 --\n";
+print bin2hex(mb_substr("\x80abc\x80\xA1", 3, 2, 'SJIS')) . "\n";
+print bin2hex(mb_substr("\x80abc\x80\xA1", 0, 3, 'SJIS')) . "\n";
+
+echo "SJIS-2004:\n";
+print bin2hex(mb_substr("\x80abc\x80\xA1", 3, 2, 'SJIS-2004')) . "\n";
+print bin2hex(mb_substr("\x80abc\x80\xA1", 0, 3, 'SJIS-2004')) . "\n";
+
+echo "MacJapanese:\n";
+print bin2hex(mb_substr("\x80abc\x80\xA1", 3, 2, 'MacJapanese')) . "\n";
+print bin2hex(mb_substr("\x80abc\x80\xA1", 0, 3, 'MacJapanese')) . "\n";
+
+echo "SJIS-Mobile#DOCOMO:\n";
+print bin2hex(mb_substr("\x80abc\x80\xA1", 3, 2, 'SJIS-Mobile#DOCOMO')) . "\n";
+print bin2hex(mb_substr("\x80abc\x80\xA1", 0, 3, 'SJIS-Mobile#DOCOMO')) . "\n";
+
+echo "SJIS-Mobile#KDDI:\n";
+print bin2hex(mb_substr("\x80abc\x80\xA1", 3, 2, 'SJIS-Mobile#KDDI')) . "\n";
+print bin2hex(mb_substr("\x80abc\x80\xA1", 0, 3, 'SJIS-Mobile#KDDI')) . "\n";
+
+echo "SJIS-Mobile#SoftBank:\n";
+print bin2hex(mb_substr("\x80abc\x80\xA1", 3, 2, 'SJIS-Mobile#SoftBank')) . "\n";
+print bin2hex(mb_substr("\x80abc\x80\xA1", 0, 3, 'SJIS-Mobile#SoftBank')) . "\n";
echo "ISO-2022-JP:\n";
print "1: " . bin2hex(mb_substr($iso2022jp, 0, 3, 'ISO-2022-JP')) . "\n";
@@ -98,6 +121,24 @@ SJIS:
3: 825582568257
4: 967b8cea8365834c8358836782c582b781423031323334825482558256825782588142
5:
+-- Testing illegal SJIS byte 0x80 --
+6380
+806162
+SJIS-2004:
+6380
+806162
+MacJapanese:
+6380
+806162
+SJIS-Mobile#DOCOMO:
+6380
+806162
+SJIS-Mobile#KDDI:
+6380
+806162
+SJIS-Mobile#SoftBank:
+6380
+806162
ISO-2022-JP:
1: 1b2442212121721b284241
2: 43
diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c
index 2d0dcee76fc52..e26303fb5f43e 100644
--- a/ext/opcache/ZendAccelerator.c
+++ b/ext/opcache/ZendAccelerator.c
@@ -4412,6 +4412,8 @@ static int accel_preload(const char *config, bool in_child)
if (PG(auto_globals_jit)) {
ping_auto_globals_mask = zend_accel_get_auto_globals();
+ } else {
+ ping_auto_globals_mask = 0;
}
if (EG(zend_constants)) {
diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c
index 181ab11615c82..429d8dbd49f5a 100644
--- a/ext/opcache/jit/zend_jit.c
+++ b/ext/opcache/jit/zend_jit.c
@@ -4259,7 +4259,7 @@ static int ZEND_FASTCALL zend_runtime_jit(void)
/* perform real JIT for this function */
zend_real_jit_func(op_array, NULL, NULL);
} zend_catch {
- do_bailout = 0;
+ do_bailout = true;
} zend_end_try();
zend_jit_protect();
diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c
index ff7c84e07c6eb..d47539926b2c8 100644
--- a/ext/opcache/jit/zend_jit_trace.c
+++ b/ext/opcache/jit/zend_jit_trace.c
@@ -247,6 +247,13 @@ static void zend_jit_trace_add_code(const void *start, uint32_t size)
t->code_size = size;
}
+/**
+ * Locate a trace in the #zend_jit_traces array with the specified
+ * #code_start address.
+ *
+ * @return the #zend_jit_traces index or 0 if no such #code_start
+ * address was found
+ */
static uint32_t zend_jit_find_trace(const void *addr)
{
uint32_t i;
@@ -256,7 +263,6 @@ static uint32_t zend_jit_find_trace(const void *addr)
return i;
}
}
- ZEND_UNREACHABLE();
return 0;
}
@@ -6832,6 +6838,15 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
const void *timeout_exit_addr = NULL;
t->link = zend_jit_find_trace(p->opline->handler);
+ if (t->link == 0) {
+ /* this can happen if ZEND_JIT_EXIT_INVALIDATE was handled
+ * by zend_jit_trace_exit() in another thread after this
+ * thread set ZEND_JIT_TRACE_STOP_LINK in zend_jit_trace_execute();
+ * ZEND_JIT_EXIT_INVALIDATE resets the opline handler to one of
+ * the "_counter_handler" functions, and these are not registered
+ * tracer functions */
+ goto jit_failure;
+ }
if ((zend_jit_traces[t->link].flags & ZEND_JIT_TRACE_USES_INITIAL_IP)
&& !zend_jit_set_ip(&dasm_state, p->opline)) {
goto jit_failure;
@@ -7088,6 +7103,7 @@ static zend_jit_trace_stop zend_jit_compile_root_trace(zend_jit_trace_rec *trace
if (t->stack_map_size) {
zend_jit_trace_stack *shared_stack_map = (zend_jit_trace_stack*)zend_shared_alloc(t->stack_map_size * sizeof(zend_jit_trace_stack));
if (!shared_stack_map) {
+ efree(t->stack_map);
ret = ZEND_JIT_TRACE_STOP_NO_SHM;
goto exit;
}
@@ -8187,22 +8203,34 @@ int ZEND_FASTCALL zend_jit_trace_exit(uint32_t exit_num, zend_jit_registers_buf
t = &zend_jit_traces[num];
}
- SHM_UNPROTECT();
- zend_jit_unprotect();
+ zend_shared_alloc_lock();
jit_extension = (zend_jit_op_array_trace_extension*)ZEND_FUNC_INFO(t->op_array);
- if (ZEND_OP_TRACE_INFO(t->opline, jit_extension->offset)->trace_flags & ZEND_JIT_TRACE_START_LOOP) {
- ((zend_op*)(t->opline))->handler = (const void*)zend_jit_loop_trace_counter_handler;
- } else if (ZEND_OP_TRACE_INFO(t->opline, jit_extension->offset)->trace_flags & ZEND_JIT_TRACE_START_ENTER) {
- ((zend_op*)(t->opline))->handler = (const void*)zend_jit_func_trace_counter_handler;
- } else if (ZEND_OP_TRACE_INFO(t->opline, jit_extension->offset)->trace_flags & ZEND_JIT_TRACE_START_RETURN) {
- ((zend_op*)(t->opline))->handler = (const void*)zend_jit_ret_trace_counter_handler;
+
+ /* Checks under lock, just in case something has changed while we were waiting for the lock */
+ if (!(ZEND_OP_TRACE_INFO(t->opline, jit_extension->offset)->trace_flags & (ZEND_JIT_TRACE_JITED|ZEND_JIT_TRACE_BLACKLISTED))) {
+ /* skip: not JIT-ed nor blacklisted */
+ } else if (ZEND_JIT_TRACE_NUM >= JIT_G(max_root_traces)) {
+ /* skip: too many root traces */
+ } else {
+ SHM_UNPROTECT();
+ zend_jit_unprotect();
+
+ if (ZEND_OP_TRACE_INFO(t->opline, jit_extension->offset)->trace_flags & ZEND_JIT_TRACE_START_LOOP) {
+ ((zend_op*)(t->opline))->handler = (const void*)zend_jit_loop_trace_counter_handler;
+ } else if (ZEND_OP_TRACE_INFO(t->opline, jit_extension->offset)->trace_flags & ZEND_JIT_TRACE_START_ENTER) {
+ ((zend_op*)(t->opline))->handler = (const void*)zend_jit_func_trace_counter_handler;
+ } else if (ZEND_OP_TRACE_INFO(t->opline, jit_extension->offset)->trace_flags & ZEND_JIT_TRACE_START_RETURN) {
+ ((zend_op*)(t->opline))->handler = (const void*)zend_jit_ret_trace_counter_handler;
+ }
+ ZEND_OP_TRACE_INFO(t->opline, jit_extension->offset)->trace_flags &=
+ ZEND_JIT_TRACE_START_LOOP|ZEND_JIT_TRACE_START_ENTER|ZEND_JIT_TRACE_START_RETURN;
+
+ zend_jit_protect();
+ SHM_PROTECT();
}
- ZEND_OP_TRACE_INFO(t->opline, jit_extension->offset)->trace_flags &=
- ZEND_JIT_TRACE_START_LOOP|ZEND_JIT_TRACE_START_ENTER|ZEND_JIT_TRACE_START_RETURN;
- zend_jit_protect();
- SHM_PROTECT();
+ zend_shared_alloc_unlock();
return 0;
}
diff --git a/ext/opcache/jit/zend_jit_x86.dasc b/ext/opcache/jit/zend_jit_x86.dasc
index c2f2772208be5..f2bf8c2a9aceb 100644
--- a/ext/opcache/jit/zend_jit_x86.dasc
+++ b/ext/opcache/jit/zend_jit_x86.dasc
@@ -16178,11 +16178,17 @@ static zend_regset zend_jit_get_scratch_regset(const zend_op *opline, const zend
}
}
if ((op1_info & MAY_BE_LONG) && (op2_info & MAY_BE_DOUBLE)) {
+ if (opline->op1_type == IS_CONST) {
+ ZEND_REGSET_INCL(regset, ZREG_R0);
+ }
if (ssa_op->result_def != current_var) {
ZEND_REGSET_INCL(regset, ZREG_XMM0);
}
}
if ((op1_info & MAY_BE_DOUBLE) && (op2_info & MAY_BE_LONG)) {
+ if (opline->op2_type == IS_CONST) {
+ ZEND_REGSET_INCL(regset, ZREG_R0);
+ }
if (zend_is_commutative(opline->opcode)) {
if (ssa_op->result_def != current_var) {
ZEND_REGSET_INCL(regset, ZREG_XMM0);
diff --git a/ext/opcache/tests/gh9968-1.inc b/ext/opcache/tests/gh9968-1.inc
index 468178c78483f..0f2b14673513c 100644
--- a/ext/opcache/tests/gh9968-1.inc
+++ b/ext/opcache/tests/gh9968-1.inc
@@ -1,3 +1,3 @@
$x,'y'=>$y];
+}
+?>
+--EXPECT--
+0: In;-0.000516528926;-0.000912408759;968.000000000000;548.000000000000;Out;967.500000000004;547.500000000009
+1: In;-0.000516528926;-0.000912408759;968.000000000000;548.000000000000;Out;967.500000000004;547.500000000009
+2: In;-0.000516528926;-0.000912408759;968.000000000000;548.000000000000;Out;967.500000000004;547.500000000009
diff --git a/ext/opcache/tests/opt/assign_op_002.phpt b/ext/opcache/tests/opt/assign_op_002.phpt
new file mode 100644
index 0000000000000..63746ac1c86b6
--- /dev/null
+++ b/ext/opcache/tests/opt/assign_op_002.phpt
@@ -0,0 +1,18 @@
+--TEST--
+ASSIGN_OP 002: Incorrect optimization of ASSIGN_OP may lead to incorrect result (sub assign -> pre dec conversion for null values)
+--INI--
+opcache.enable=1
+opcache.enable_cli=1
+opcache.optimization_level=-1
+--FILE--
+
+--EXPECT--
+int(1)
+int(-1)
diff --git a/ext/opcache/tests/opt/match_002.phpt b/ext/opcache/tests/opt/match_002.phpt
new file mode 100644
index 0000000000000..ecda31ee7c0ec
--- /dev/null
+++ b/ext/opcache/tests/opt/match_002.phpt
@@ -0,0 +1,15 @@
+--TEST--
+Match 002: memory leak because of incorrect optimization
+--INI--
+opcache.enable=1
+opcache.enable_cli=1
+opcache.optimization_level=-1
+--FILE--
+
+--EXPECTF--
+Fatal error: Uncaught Error: Undefined constant "y" in %smatch_002.php:2
+Stack trace:
+#0 {main}
+ thrown in %smatch_002.php on line 2
diff --git a/ext/opcache/tests/preload_const_autoload.inc b/ext/opcache/tests/preload_const_autoload.inc
index b9634de49a56e..02c4ece92fefa 100644
--- a/ext/opcache/tests/preload_const_autoload.inc
+++ b/ext/opcache/tests/preload_const_autoload.inc
@@ -4,4 +4,4 @@ spl_autoload_register(function($class) {
var_dump($class);
new Abc;
});
-opcache_compile_file('preload_const_autoload_2.inc');
+opcache_compile_file(__DIR__ . '/preload_const_autoload_2.inc');
diff --git a/ext/opcache/tests/preloading_no_auto_globals_jit.inc b/ext/opcache/tests/preloading_no_auto_globals_jit.inc
new file mode 100644
index 0000000000000..ad80df7733d1d
--- /dev/null
+++ b/ext/opcache/tests/preloading_no_auto_globals_jit.inc
@@ -0,0 +1,6 @@
+
+--FILE--
+count_global_server());
+?>
+--EXPECTF--
+int(%d)
diff --git a/ext/pdo/pdo_dbh.c b/ext/pdo/pdo_dbh.c
index 0de21811f26e4..ed45c6d7ce40e 100644
--- a/ext/pdo/pdo_dbh.c
+++ b/ext/pdo/pdo_dbh.c
@@ -1164,7 +1164,7 @@ PHP_METHOD(PDO, query)
PHP_METHOD(PDO, quote)
{
pdo_dbh_t *dbh = Z_PDO_DBH_P(ZEND_THIS);
- zend_string *str;
+ zend_string *str, *quoted;
zend_long paramtype = PDO_PARAM_STR;
ZEND_PARSE_PARAMETERS_START(1, 2)
@@ -1180,8 +1180,14 @@ PHP_METHOD(PDO, quote)
pdo_raise_impl_error(dbh, NULL, "IM001", "driver does not support quoting");
RETURN_FALSE;
}
+ quoted = dbh->methods->quoter(dbh, str, paramtype);
- RETURN_STR(dbh->methods->quoter(dbh, str, paramtype));
+ if (quoted == NULL) {
+ PDO_HANDLE_DBH_ERR();
+ RETURN_FALSE;
+ }
+
+ RETURN_STR(quoted);
}
/* }}} */
diff --git a/ext/pdo/pdo_sql_parser.re b/ext/pdo/pdo_sql_parser.re
index 4c20700379731..6bb0837fb31f9 100644
--- a/ext/pdo/pdo_sql_parser.re
+++ b/ext/pdo/pdo_sql_parser.re
@@ -242,6 +242,13 @@ safe:
if (buf) {
zend_string_release_ex(buf, 0);
}
+ if (plc->quoted == NULL) {
+ /* bork */
+ ret = -1;
+ strncpy(stmt->error_code, stmt->dbh->error_code, 6);
+ goto clean_up;
+ }
+
} else {
pdo_raise_impl_error(stmt->dbh, stmt, "HY105", "Expected a stream resource");
ret = -1;
diff --git a/ext/pdo_sqlite/sqlite_driver.c b/ext/pdo_sqlite/sqlite_driver.c
index 6a41e2162a934..de5170a35a96b 100644
--- a/ext/pdo_sqlite/sqlite_driver.c
+++ b/ext/pdo_sqlite/sqlite_driver.c
@@ -226,7 +226,11 @@ static zend_string *pdo_sqlite_last_insert_id(pdo_dbh_t *dbh, const zend_string
/* NB: doesn't handle binary strings... use prepared stmts for that */
static zend_string* sqlite_handle_quoter(pdo_dbh_t *dbh, const zend_string *unquoted, enum pdo_param_type paramtype)
{
- char *quoted = safe_emalloc(2, ZSTR_LEN(unquoted), 3);
+ char *quoted;
+ if (ZSTR_LEN(unquoted) > (INT_MAX - 3) / 2) {
+ return NULL;
+ }
+ quoted = safe_emalloc(2, ZSTR_LEN(unquoted), 3);
/* TODO use %Q format? */
sqlite3_snprintf(2*ZSTR_LEN(unquoted) + 3, quoted, "'%q'", ZSTR_VAL(unquoted));
zend_string *quoted_str = zend_string_init(quoted, strlen(quoted), 0);
diff --git a/ext/pdo_sqlite/tests/bug81740.phpt b/ext/pdo_sqlite/tests/bug81740.phpt
new file mode 100644
index 0000000000000..2b8b9447f0fed
--- /dev/null
+++ b/ext/pdo_sqlite/tests/bug81740.phpt
@@ -0,0 +1,20 @@
+--TEST--
+Bug #81740 (PDO::quote() may return unquoted string)
+--EXTENSIONS--
+pdo
+pdo_sqlite
+--SKIPIF--
+
+--INI--
+memory_limit=-1
+--FILE--
+quote($string));
+?>
+--EXPECT--
+bool(false)
diff --git a/ext/phar/phar_object.c b/ext/phar/phar_object.c
index 5421b12198e8b..0e9f492b61f49 100644
--- a/ext/phar/phar_object.c
+++ b/ext/phar/phar_object.c
@@ -3314,7 +3314,7 @@ PHP_METHOD(Phar, compressFiles)
}
if (!pharobj_cancompress(&phar_obj->archive->manifest)) {
- if (flags == PHAR_FILE_COMPRESSED_GZ) {
+ if (flags == PHAR_ENT_COMPRESSED_GZ) {
zend_throw_exception_ex(spl_ce_BadMethodCallException, 0,
"Cannot compress all files as Gzip, some are compressed as bzip2 and cannot be decompressed");
} else {
diff --git a/ext/posix/posix.c b/ext/posix/posix.c
index ed7a93c540996..788fca0b2879d 100644
--- a/ext/posix/posix.c
+++ b/ext/posix/posix.c
@@ -474,15 +474,15 @@ PHP_FUNCTION(posix_ttyname)
efree(p);
RETURN_FALSE;
}
- RETURN_STRING(p);
+ RETVAL_STRING(p);
efree(p);
#else
if (NULL == (p = ttyname(fd))) {
POSIX_G(last_error) = errno;
RETURN_FALSE;
}
-#endif
RETURN_STRING(p);
+#endif
}
/* }}} */
diff --git a/ext/posix/tests/posix_getgrgid_basic.phpt b/ext/posix/tests/posix_getgrgid_basic.phpt
index 6b0daa5e940fc..392e81d491d61 100644
--- a/ext/posix/tests/posix_getgrgid_basic.phpt
+++ b/ext/posix/tests/posix_getgrgid_basic.phpt
@@ -17,7 +17,7 @@ Basic test of POSIX getgid and getgrid functions
Array
(
[name] => %s
- [passwd] => %a
+ [passwd] => %A
[members] => Array
%a
diff --git a/ext/posix/tests/posix_getgrnam_basic.phpt b/ext/posix/tests/posix_getgrnam_basic.phpt
index 5203d1ea0f056..acf8f4473e5d8 100644
--- a/ext/posix/tests/posix_getgrnam_basic.phpt
+++ b/ext/posix/tests/posix_getgrnam_basic.phpt
@@ -20,7 +20,7 @@ array(4) {
["name"]=>
string(%d) "%s"
["passwd"]=>
- string(1) "%s"
+ string(%d) "%S"
["members"]=>
%a
["gid"]=>
diff --git a/ext/random/random.c b/ext/random/random.c
index 161eb8e685203..64e30e5087186 100644
--- a/ext/random/random.c
+++ b/ext/random/random.c
@@ -828,7 +828,7 @@ static PHP_GINIT_FUNCTION(random)
/* {{{ PHP_GSHUTDOWN_FUNCTION */
static PHP_GSHUTDOWN_FUNCTION(random)
{
- if (random_globals->random_fd > 0) {
+ if (random_globals->random_fd >= 0) {
close(random_globals->random_fd);
random_globals->random_fd = -1;
}
diff --git a/ext/simplexml/tests/gh10200.phpt b/ext/simplexml/tests/gh10200.phpt
new file mode 100644
index 0000000000000..ad02d45bfe63c
--- /dev/null
+++ b/ext/simplexml/tests/gh10200.phpt
@@ -0,0 +1,22 @@
+--TEST--
+GH-10200 (zif_get_object_vars: Assertion `!(((__ht)->u.flags & (1<<2)) != 0)' failed.)
+--EXTENSIONS--
+simplexml
+--FILE--
+
+https://github.com/php/php-src/issues/10200 not encountered
+EOF;
+
+$xml = simplexml_load_string($xmlData);
+$output = get_object_vars($xml);
+var_dump($output);
+
+?>
+--EXPECT--
+array(1) {
+ [0]=>
+ string(59) "https://github.com/php/php-src/issues/10200%20not%20encountered"
+}
diff --git a/ext/spl/tests/bug73029.phpt b/ext/spl/tests/bug73029.phpt
index 5c3cd8420da8d..9e002f1182939 100644
--- a/ext/spl/tests/bug73029.phpt
+++ b/ext/spl/tests/bug73029.phpt
@@ -3,6 +3,13 @@ Bug #73029: Missing type check when unserializing SplArray
--FILE--
getMessage() . "\n";
+}
+try {
$a = 'C:11:"ArrayObject":19:0x:i:0;r:2;;m:a:0:{}}';
$m = unserialize($a);
$x = $m[2];
@@ -11,6 +18,10 @@ $x = $m[2];
}
?>
DONE
---EXPECT--
+--EXPECTF--
Error at offset 10 of 19 bytes
+
+Notice: unserialize(): Error at offset 22 of 43 bytes in %s on line %d
+
+Warning: Trying to access array offset on value of type bool in %s on line %d
DONE
diff --git a/ext/standard/string.c b/ext/standard/string.c
index a47687667a470..192a8b1602aad 100644
--- a/ext/standard/string.c
+++ b/ext/standard/string.c
@@ -2365,7 +2365,7 @@ PHP_FUNCTION(substr_replace)
if (HT_IS_PACKED(repl_ht)) {
while (repl_idx < repl_ht->nNumUsed) {
tmp_repl = &repl_ht->arPacked[repl_idx];
- if (repl_ht != IS_UNDEF) {
+ if (Z_TYPE_P(tmp_repl) != IS_UNDEF) {
break;
}
repl_idx++;
@@ -2373,7 +2373,7 @@ PHP_FUNCTION(substr_replace)
} else {
while (repl_idx < repl_ht->nNumUsed) {
tmp_repl = &repl_ht->arData[repl_idx].val;
- if (repl_ht != IS_UNDEF) {
+ if (Z_TYPE_P(tmp_repl) != IS_UNDEF) {
break;
}
repl_idx++;
@@ -3990,19 +3990,23 @@ static zend_always_inline char *php_stripslashes_impl(const char *str, char *out
quad_word q;
vst1q_u8(q.mem, vceqq_u8(x, vdupq_n_u8('\\')));
if (q.dw[0] | q.dw[1]) {
- int i = 0;
- for (; i < 16; i++) {
+ unsigned int i = 0;
+ while (i < 16) {
if (q.mem[i] == 0) {
*out++ = str[i];
+ i++;
continue;
}
i++; /* skip the slash */
- char s = str[i];
- if (s == '0')
- *out++ = '\0';
- else
- *out++ = s; /* preserve the next character */
+ if (i < len) {
+ char s = str[i];
+ if (s == '0')
+ *out++ = '\0';
+ else
+ *out++ = s; /* preserve the next character */
+ i++;
+ }
}
str += i;
len -= i;
diff --git a/ext/standard/tests/serialize/bug73341.phpt b/ext/standard/tests/serialize/bug73341.phpt
index 2f38949c1ae8f..3bdad06440be0 100644
--- a/ext/standard/tests/serialize/bug73341.phpt
+++ b/ext/standard/tests/serialize/bug73341.phpt
@@ -2,6 +2,13 @@
Bug #73144 (Use-afte-free in ArrayObject Deserialization)
--FILE--
getMessage()."\n";
+}
+
try {
$token = 'a:2:{i:0;O:1:"0":2:0s:1:"0";i:0;s:1:"0";a:1:{i:0;C:11:"ArrayObject":7:{x:i:0;r}';
$obj = unserialize($token);
@@ -20,5 +27,7 @@ unserialize($exploit);
--EXPECTF--
Error at offset 6 of 7 bytes
+Notice: unserialize(): Error at offset 19 of 79 bytes in %s on line %d
+
Notice: ArrayObject::unserialize(): Unexpected end of serialized data in %sbug73341.php on line %d
Error at offset 24 of 34 bytes
diff --git a/ext/standard/tests/serialize/bug74111.phpt b/ext/standard/tests/serialize/bug74111.phpt
index 62922bea55ae0..2062687aa271e 100644
--- a/ext/standard/tests/serialize/bug74111.phpt
+++ b/ext/standard/tests/serialize/bug74111.phpt
@@ -6,5 +6,5 @@ $s = 'O:8:"stdClass":00000000';
var_dump(unserialize($s));
?>
--EXPECTF--
-Notice: unserialize(): Error at offset 25 of 23 bytes in %s on line %d
+Notice: unserialize(): Error at offset 23 of 23 bytes in %s on line %d
bool(false)
diff --git a/ext/standard/tests/serialize/serialization_objects_017.phpt b/ext/standard/tests/serialize/serialization_objects_017.phpt
new file mode 100644
index 0000000000000..901d73f2b24aa
--- /dev/null
+++ b/ext/standard/tests/serialize/serialization_objects_017.phpt
@@ -0,0 +1,17 @@
+--TEST--
+Object serialization / unserialization: Strict format
+--FILE--
+
+--EXPECTF--
+Notice: unserialize(): Error at offset 9 of 22 bytes in %s on line %d
+bool(false)
+
+Notice: unserialize(): Error at offset 10 of 22 bytes in %s on line %d
+bool(false)
diff --git a/ext/standard/tests/serialize/serialization_objects_018.phpt b/ext/standard/tests/serialize/serialization_objects_018.phpt
new file mode 100644
index 0000000000000..e05ff5ca04608
--- /dev/null
+++ b/ext/standard/tests/serialize/serialization_objects_018.phpt
@@ -0,0 +1,33 @@
+--TEST--
+Object serialization / unserialization: Strict format (2)
+--FILE--
+
+--EXPECTF--
+Notice: unserialize(): Error at offset 9 of 15 bytes in %s on line %d
+bool(false)
+
+Notice: unserialize(): Error at offset 10 of 15 bytes in %s on line %d
+bool(false)
+
+Notice: unserialize(): Error at offset 14 of 15 bytes in %s on line %d
+bool(false)
+
+Notice: unserialize(): Error at offset 8 of 8 bytes in %s on line %d
+bool(false)
diff --git a/ext/standard/tests/strings/gh10187.phpt b/ext/standard/tests/strings/gh10187.phpt
new file mode 100644
index 0000000000000..b42c95e591ed0
--- /dev/null
+++ b/ext/standard/tests/strings/gh10187.phpt
@@ -0,0 +1,8 @@
+--TEST--
+GH-10187 (Segfault in stripslashes() with arm64)
+--FILE--
+
+--EXPECT--
+string(15) "1234567890abcde"
diff --git a/ext/standard/tests/strings/substr_replace_array_unset.phpt b/ext/standard/tests/strings/substr_replace_array_unset.phpt
new file mode 100644
index 0000000000000..ff253d3984476
--- /dev/null
+++ b/ext/standard/tests/strings/substr_replace_array_unset.phpt
@@ -0,0 +1,27 @@
+--TEST--
+substr_replace() function - array with unset
+--FILE--
+ 'bar', 'baz'];
+unset($replacement[42]);
+$newarr = substr_replace(['1 string', '2 string'], $replacement, 0);
+print_r($newarr);
+
+?>
+--EXPECT--
+Array
+(
+ [0] => A
+ [1] => B
+)
+Array
+(
+ [0] => foo
+ [1] => baz
+)
diff --git a/ext/standard/var_unserializer.re b/ext/standard/var_unserializer.re
index bcf19002fc0a2..313d7fadeb394 100644
--- a/ext/standard/var_unserializer.re
+++ b/ext/standard/var_unserializer.re
@@ -743,6 +743,19 @@ static inline int object_custom(UNSERIALIZE_PARAMETER, zend_class_entry *ce)
datalen = parse_iv2((*p) + 2, p);
+ if (max - (*p) < 2) {
+ return 0;
+ }
+
+ if ((*p)[0] != ':') {
+ return 0;
+ }
+
+ if ((*p)[1] != '{') {
+ (*p) += 1;
+ return 0;
+ }
+
(*p) += 2;
if (datalen < 0 || (max - (*p)) <= datalen) {
@@ -754,6 +767,7 @@ static inline int object_custom(UNSERIALIZE_PARAMETER, zend_class_entry *ce)
* with unserialize reading past the end of the passed buffer if the string is not
* appropriately terminated (usually NUL terminated, but '}' is also sufficient.) */
if ((*p)[datalen] != '}') {
+ (*p) += datalen;
return 0;
}
@@ -1293,6 +1307,16 @@ object ":" uiv ":" ["] {
return 0;
}
+ YYCURSOR = *p;
+
+ if (*(YYCURSOR) != ':') {
+ return 0;
+ }
+ if (*(YYCURSOR+1) != '{') {
+ *p = YYCURSOR+1;
+ return 0;
+ }
+
*p += 2;
has_unserialize = !incomplete_class && ce->__unserialize;
diff --git a/ext/xmlwriter/php_xmlwriter.c b/ext/xmlwriter/php_xmlwriter.c
index 8d4abb3d26b06..68961429dea59 100644
--- a/ext/xmlwriter/php_xmlwriter.c
+++ b/ext/xmlwriter/php_xmlwriter.c
@@ -449,7 +449,7 @@ PHP_FUNCTION(xmlwriter_write_element)
if (retval == -1) {
RETURN_FALSE;
}
- xmlTextWriterEndElement(ptr);
+ retval = xmlTextWriterEndElement(ptr);
if (retval == -1) {
RETURN_FALSE;
}
diff --git a/main/php_version.h b/main/php_version.h
index 4cc596766d9c1..05099d8764096 100644
--- a/main/php_version.h
+++ b/main/php_version.h
@@ -2,7 +2,7 @@
/* edit configure.ac to change version number */
#define PHP_MAJOR_VERSION 8
#define PHP_MINOR_VERSION 2
-#define PHP_RELEASE_VERSION 1
-#define PHP_EXTRA_VERSION "-dev"
-#define PHP_VERSION "8.2.1-dev"
-#define PHP_VERSION_ID 80201
+#define PHP_RELEASE_VERSION 2
+#define PHP_EXTRA_VERSION ""
+#define PHP_VERSION "8.2.2"
+#define PHP_VERSION_ID 80202
diff --git a/main/safe_bcmp.c b/main/safe_bcmp.c
index 27a1756d79b46..3e806de4ab6e3 100644
--- a/main/safe_bcmp.c
+++ b/main/safe_bcmp.c
@@ -19,7 +19,7 @@
#include
/*
- * Returns 0 if both inputs match, 1 if they don't.
+ * Returns 0 if both inputs match, non-zero if they don't.
* Returns -1 early if inputs do not have the same lengths.
*
*/
@@ -34,6 +34,7 @@ PHPAPI int php_safe_bcmp(const zend_string *a, const zend_string *b)
return -1;
}
+ /* This is security sensitive code. Do not optimize this for speed. */
while (i < ZSTR_LEN(a)) {
r |= ua[i] ^ ub[i];
++i;
diff --git a/run-tests.php b/run-tests.php
index 81306bb90efdd..e7cc6a7f28c52 100755
--- a/run-tests.php
+++ b/run-tests.php
@@ -1504,6 +1504,10 @@ function run_all_tests_parallel(array $test_files, array $env, $redir_tested): v
kill_children($workerProcs);
error("Could not find worker stdout in array of worker stdouts, THIS SHOULD NOT HAPPEN.");
}
+ if (feof($workerSock)) {
+ kill_children($workerProcs);
+ error("Worker $i died unexpectedly");
+ }
while (false !== ($rawMessage = fgets($workerSock))) {
// work around fgets truncating things
if (($rawMessageBuffers[$i] ?? '') !== '') {
diff --git a/sapi/fpm/fpm/fpm_conf.c b/sapi/fpm/fpm/fpm_conf.c
index f2d9d3c088a75..ef9b35a9994aa 100644
--- a/sapi/fpm/fpm/fpm_conf.c
+++ b/sapi/fpm/fpm/fpm_conf.c
@@ -34,6 +34,7 @@
#include "fpm_status.h"
#include "fpm_log.h"
#include "fpm_events.h"
+#include "fpm_unix.h"
#include "zlog.h"
#ifdef HAVE_SYSTEMD
#include "fpm_systemd.h"
@@ -1854,6 +1855,12 @@ int fpm_conf_init_main(int test_conf, int force_daemon) /* {{{ */
}
if (test_conf) {
+ for (struct fpm_worker_pool_s *wp = fpm_worker_all_pools; wp; wp = wp->next) {
+ if (!fpm_unix_test_config(wp)) {
+ return -1;
+ }
+ }
+
if (test_conf > 1) {
fpm_conf_dump();
}
diff --git a/sapi/fpm/fpm/fpm_main.c b/sapi/fpm/fpm/fpm_main.c
index a8d77ef3bed21..0a61abacd70df 100644
--- a/sapi/fpm/fpm/fpm_main.c
+++ b/sapi/fpm/fpm/fpm_main.c
@@ -149,6 +149,7 @@ typedef struct _php_cgi_globals_struct {
bool force_redirect;
bool discard_path;
bool fcgi_logging;
+ bool fcgi_logging_request_started;
char *redirect_status_env;
HashTable user_config_cache;
char *error_header;
@@ -598,11 +599,16 @@ void sapi_cgi_log_fastcgi(int level, char *message, size_t len)
* - logging is enabled (fastcgi.logging in php.ini)
* - we are currently dealing with a request
* - the message is not empty
- * - the fcgi_write did not fail
*/
- if (CGIG(fcgi_logging) && request && message && len > 0
- && fcgi_write(request, FCGI_STDERR, message, len) < 0) {
- php_handle_aborted_connection();
+ if (CGIG(fcgi_logging) && request && message && len > 0) {
+ if (CGIG(fcgi_logging_request_started)) {
+ fcgi_write(request, FCGI_STDERR, "; ", 2);
+ } else {
+ CGIG(fcgi_logging_request_started) = true;
+ }
+ if (fcgi_write(request, FCGI_STDERR, message, len) < 0) {
+ php_handle_aborted_connection();
+ }
}
}
/* }}} */
@@ -1403,6 +1409,7 @@ static void php_cgi_globals_ctor(php_cgi_globals_struct *php_cgi_globals)
php_cgi_globals->fix_pathinfo = 1;
php_cgi_globals->discard_path = 0;
php_cgi_globals->fcgi_logging = 1;
+ php_cgi_globals->fcgi_logging_request_started = false;
zend_hash_init(&php_cgi_globals->user_config_cache, 0, NULL, user_config_cache_entry_dtor, 1);
php_cgi_globals->error_header = NULL;
php_cgi_globals->fpm_config = NULL;
@@ -1791,7 +1798,8 @@ consult the installation file that came with this distribution, or visit \n\
zend_quiet_write(fpm_globals.send_config_pipe[1], &writeval, sizeof(writeval));
close(fpm_globals.send_config_pipe[1]);
}
- return FPM_EXIT_CONFIG;
+ exit_status = FPM_EXIT_CONFIG;
+ goto out;
}
if (fpm_globals.send_config_pipe[1]) {
@@ -1820,6 +1828,7 @@ consult the installation file that came with this distribution, or visit \n\
char *primary_script = NULL;
request_body_fd = -1;
SG(server_context) = (void *) request;
+ CGIG(fcgi_logging_request_started) = false;
init_request_info();
fpm_request_info();
@@ -1888,6 +1897,9 @@ consult the installation file that came with this distribution, or visit \n\
fpm_request_executing();
+ /* Reset exit status from the previous execution */
+ EG(exit_status) = 0;
+
php_execute_script(&file_handle);
fastcgi_request_done:
diff --git a/sapi/fpm/fpm/fpm_unix.c b/sapi/fpm/fpm/fpm_unix.c
index b1127b35949d3..d10a6f3254b24 100644
--- a/sapi/fpm/fpm/fpm_unix.c
+++ b/sapi/fpm/fpm/fpm_unix.c
@@ -46,6 +46,55 @@
size_t fpm_pagesize;
+
+static inline bool fpm_unix_is_id(const char* name)
+{
+ return strlen(name) == strspn(name, "0123456789");
+}
+
+static struct passwd *fpm_unix_get_passwd(struct fpm_worker_pool_s *wp, const char *name, int flags)
+{
+ struct passwd *pwd = getpwnam(name);
+ if (!pwd) {
+ zlog(flags, "[pool %s] cannot get uid for user '%s'", wp->config->name, name);
+ return NULL;
+ }
+
+ return pwd;
+}
+
+static inline bool fpm_unix_check_passwd(struct fpm_worker_pool_s *wp, const char *name, int flags)
+{
+ return !name || fpm_unix_is_id(name) || fpm_unix_get_passwd(wp, name, flags);
+}
+
+static struct group *fpm_unix_get_group(struct fpm_worker_pool_s *wp, const char *name, int flags)
+{
+ struct group *group = getgrnam(name);
+ if (!group) {
+ zlog(flags, "[pool %s] cannot get gid for group '%s'", wp->config->name, name);
+ return NULL;
+ }
+
+ return group;
+}
+
+static inline bool fpm_unix_check_group(struct fpm_worker_pool_s *wp, const char *name, int flags)
+{
+ return !name || fpm_unix_is_id(name) || fpm_unix_get_group(wp, name, flags);
+}
+
+bool fpm_unix_test_config(struct fpm_worker_pool_s *wp)
+{
+ struct fpm_worker_pool_config_s *config = wp->config;
+ return (
+ fpm_unix_check_passwd(wp, config->user, ZLOG_ERROR) &&
+ fpm_unix_check_group(wp, config->group, ZLOG_ERROR) &&
+ fpm_unix_check_passwd(wp, config->listen_owner, ZLOG_SYSERROR) &&
+ fpm_unix_check_group(wp, config->listen_group, ZLOG_SYSERROR)
+ );
+}
+
int fpm_unix_resolve_socket_permissions(struct fpm_worker_pool_s *wp) /* {{{ */
{
struct fpm_worker_pool_config_s *c = wp->config;
@@ -105,11 +154,10 @@ int fpm_unix_resolve_socket_permissions(struct fpm_worker_pool_s *wp) /* {{{ */
if ((end = strchr(p, ','))) {
*end++ = 0;
}
- pwd = getpwnam(p);
+ pwd = fpm_unix_get_passwd(wp, p, ZLOG_SYSERROR);
if (pwd) {
zlog(ZLOG_DEBUG, "[pool %s] user '%s' have uid=%d", wp->config->name, p, pwd->pw_uid);
} else {
- zlog(ZLOG_SYSERROR, "[pool %s] cannot get uid for user '%s'", wp->config->name, p);
acl_free(acl);
efree(tmp);
return -1;
@@ -138,11 +186,10 @@ int fpm_unix_resolve_socket_permissions(struct fpm_worker_pool_s *wp) /* {{{ */
if ((end = strchr(p, ','))) {
*end++ = 0;
}
- grp = getgrnam(p);
+ grp = fpm_unix_get_group(wp, p, ZLOG_SYSERROR);
if (grp) {
zlog(ZLOG_DEBUG, "[pool %s] group '%s' have gid=%d", wp->config->name, p, grp->gr_gid);
} else {
- zlog(ZLOG_SYSERROR, "[pool %s] cannot get gid for group '%s'", wp->config->name, p);
acl_free(acl);
efree(tmp);
return -1;
@@ -175,14 +222,13 @@ int fpm_unix_resolve_socket_permissions(struct fpm_worker_pool_s *wp) /* {{{ */
#endif
if (c->listen_owner && *c->listen_owner) {
- if (strlen(c->listen_owner) == strspn(c->listen_owner, "0123456789")) {
+ if (fpm_unix_is_id(c->listen_owner)) {
wp->socket_uid = strtoul(c->listen_owner, 0, 10);
} else {
struct passwd *pwd;
- pwd = getpwnam(c->listen_owner);
+ pwd = fpm_unix_get_passwd(wp, c->listen_owner, ZLOG_SYSERROR);
if (!pwd) {
- zlog(ZLOG_SYSERROR, "[pool %s] cannot get uid for user '%s'", wp->config->name, c->listen_owner);
return -1;
}
@@ -192,14 +238,13 @@ int fpm_unix_resolve_socket_permissions(struct fpm_worker_pool_s *wp) /* {{{ */
}
if (c->listen_group && *c->listen_group) {
- if (strlen(c->listen_group) == strspn(c->listen_group, "0123456789")) {
+ if (fpm_unix_is_id(c->listen_group)) {
wp->socket_gid = strtoul(c->listen_group, 0, 10);
} else {
struct group *grp;
- grp = getgrnam(c->listen_group);
+ grp = fpm_unix_get_group(wp, c->listen_group, ZLOG_SYSERROR);
if (!grp) {
- zlog(ZLOG_SYSERROR, "[pool %s] cannot get gid for group '%s'", wp->config->name, c->listen_group);
return -1;
}
wp->socket_gid = grp->gr_gid;
@@ -279,7 +324,7 @@ static int fpm_unix_conf_wp(struct fpm_worker_pool_s *wp) /* {{{ */
if (is_root) {
if (wp->config->user && *wp->config->user) {
- if (strlen(wp->config->user) == strspn(wp->config->user, "0123456789")) {
+ if (fpm_unix_is_id(wp->config->user)) {
wp->set_uid = strtoul(wp->config->user, 0, 10);
pwd = getpwuid(wp->set_uid);
if (pwd) {
@@ -289,9 +334,8 @@ static int fpm_unix_conf_wp(struct fpm_worker_pool_s *wp) /* {{{ */
} else {
struct passwd *pwd;
- pwd = getpwnam(wp->config->user);
+ pwd = fpm_unix_get_passwd(wp, wp->config->user, ZLOG_ERROR);
if (!pwd) {
- zlog(ZLOG_ERROR, "[pool %s] cannot get uid for user '%s'", wp->config->name, wp->config->user);
return -1;
}
@@ -304,14 +348,13 @@ static int fpm_unix_conf_wp(struct fpm_worker_pool_s *wp) /* {{{ */
}
if (wp->config->group && *wp->config->group) {
- if (strlen(wp->config->group) == strspn(wp->config->group, "0123456789")) {
+ if (fpm_unix_is_id(wp->config->group)) {
wp->set_gid = strtoul(wp->config->group, 0, 10);
} else {
struct group *grp;
- grp = getgrnam(wp->config->group);
+ grp = fpm_unix_get_group(wp, wp->config->group, ZLOG_ERROR);
if (!grp) {
- zlog(ZLOG_ERROR, "[pool %s] cannot get gid for group '%s'", wp->config->name, wp->config->group);
return -1;
}
wp->set_gid = grp->gr_gid;
diff --git a/sapi/fpm/fpm/fpm_unix.h b/sapi/fpm/fpm/fpm_unix.h
index 0bb22687b02f9..6fc9e5e8450dc 100644
--- a/sapi/fpm/fpm/fpm_unix.h
+++ b/sapi/fpm/fpm/fpm_unix.h
@@ -5,6 +5,8 @@
#include "fpm_worker_pool.h"
+bool fpm_unix_test_config(struct fpm_worker_pool_s *wp);
+
int fpm_unix_resolve_socket_permissions(struct fpm_worker_pool_s *wp);
int fpm_unix_set_socket_permissions(struct fpm_worker_pool_s *wp, const char *path);
int fpm_unix_free_socket_permissions(struct fpm_worker_pool_s *wp);
diff --git a/sapi/fpm/tests/bug68591-conf-test-group.phpt b/sapi/fpm/tests/bug68591-conf-test-group.phpt
new file mode 100644
index 0000000000000..d496d0549e87f
--- /dev/null
+++ b/sapi/fpm/tests/bug68591-conf-test-group.phpt
@@ -0,0 +1,39 @@
+--TEST--
+FPM: bug68591 - config test group existence
+--SKIPIF--
+
+--FILE--
+testConfig();
+
+?>
+Done
+--EXPECT--
+ERROR: [pool unconfined] cannot get gid for group 'aaaaaa'
+ERROR: FPM initialization failed
+Done
+--CLEAN--
+
diff --git a/sapi/fpm/tests/bug68591-conf-test-listen-group.phpt b/sapi/fpm/tests/bug68591-conf-test-listen-group.phpt
new file mode 100644
index 0000000000000..71ca71adf8316
--- /dev/null
+++ b/sapi/fpm/tests/bug68591-conf-test-listen-group.phpt
@@ -0,0 +1,39 @@
+--TEST--
+FPM: bug68591 - config test listen group existence
+--SKIPIF--
+
+--FILE--
+testConfig();
+
+?>
+Done
+--EXPECTF--
+ERROR: [pool unconfined] cannot get gid for group 'aaaaaa': %s
+ERROR: FPM initialization failed
+Done
+--CLEAN--
+
diff --git a/sapi/fpm/tests/bug68591-conf-test-listen-owner.phpt b/sapi/fpm/tests/bug68591-conf-test-listen-owner.phpt
new file mode 100644
index 0000000000000..c6108bcddeb68
--- /dev/null
+++ b/sapi/fpm/tests/bug68591-conf-test-listen-owner.phpt
@@ -0,0 +1,39 @@
+--TEST--
+FPM: bug68591 - config test listen owner existence
+--SKIPIF--
+
+--FILE--
+testConfig();
+
+?>
+Done
+--EXPECTF--
+ERROR: [pool unconfined] cannot get uid for user 'aaaaaa': %s
+ERROR: FPM initialization failed
+Done
+--CLEAN--
+
diff --git a/sapi/fpm/tests/bug68591-conf-test-user.phpt b/sapi/fpm/tests/bug68591-conf-test-user.phpt
new file mode 100644
index 0000000000000..5627151f3f13e
--- /dev/null
+++ b/sapi/fpm/tests/bug68591-conf-test-user.phpt
@@ -0,0 +1,38 @@
+--TEST--
+FPM: bug68591 - config test user existence
+--SKIPIF--
+
+--FILE--
+testConfig();
+
+?>
+Done
+--EXPECT--
+ERROR: [pool unconfined] cannot get uid for user 'aaaaaa'
+ERROR: FPM initialization failed
+Done
+--CLEAN--
+
diff --git a/sapi/fpm/tests/bug77106-fcgi-missing-nl.phpt b/sapi/fpm/tests/bug77106-fcgi-missing-nl.phpt
new file mode 100644
index 0000000000000..c4c7adf382545
--- /dev/null
+++ b/sapi/fpm/tests/bug77106-fcgi-missing-nl.phpt
@@ -0,0 +1,46 @@
+--TEST--
+FPM: bug77106 - Missing new lines in FCGI error stream
+--SKIPIF--
+
+--FILE--
+start();
+$tester->expectLogStartNotices();
+$tester->request()->expectErrorPattern(
+ '/Undefined variable \$a in .+ on line \d+; PHP message: PHP Warning:/'
+);
+$tester->terminate();
+$tester->close();
+
+?>
+Done
+--EXPECT--
+Done
+--CLEAN--
+
diff --git a/sapi/fpm/tests/gh9981-fastcgi-error-header-reset.phpt b/sapi/fpm/tests/gh9981-fastcgi-error-header-reset.phpt
new file mode 100644
index 0000000000000..5a1d65f1cb0bd
--- /dev/null
+++ b/sapi/fpm/tests/gh9981-fastcgi-error-header-reset.phpt
@@ -0,0 +1,51 @@
+--TEST--
+FPM: gh9981 - fastcgi.error_header is not reset
+--SKIPIF--
+
+--FILE--
+start(iniEntries: [
+ 'fastcgi.error_header' => '"HTTP/1.1 500 PHP Error"',
+ 'output_buffering' => 4096,
+]);
+$tester->expectLogStartNotices();
+$tester->request()->expectStatus('500 PHP Error');
+$tester->request('q=1')->expectNoStatus();
+$tester->terminate();
+$tester->expectLogTerminatingNotices();
+$tester->expectNoLogPattern('/Cannot modify header information/');
+$tester->close();
+
+?>
+Done
+--EXPECT--
+Done
+--CLEAN--
+
diff --git a/sapi/fpm/tests/response.inc b/sapi/fpm/tests/response.inc
index dd74e42990b08..97e5c3263e290 100644
--- a/sapi/fpm/tests/response.inc
+++ b/sapi/fpm/tests/response.inc
@@ -41,6 +41,11 @@ class Response
*/
private $expectInvalid;
+ /**
+ * @var bool
+ */
+ private bool $debugOutputted = false;
+
/**
* @param string|array|null $data
* @param bool $expectInvalid
@@ -71,9 +76,9 @@ class Response
$body = implode("\n", $body);
}
- if (!$this->checkIfValid()) {
+ if ( ! $this->checkIfValid()) {
$this->error('Response is invalid');
- } elseif (!$this->checkDefaultHeaders($contentType)) {
+ } elseif ( ! $this->checkDefaultHeaders($contentType)) {
$this->error('Response default headers not found');
} elseif ($body !== $this->rawBody) {
if ($multiLine) {
@@ -134,6 +139,57 @@ class Response
return $this;
}
+ /**
+ * Expect error pattern in the response.
+ *
+ * @param string $errorMessagePattern Expected error message RegExp patter.
+ *
+ * @return Response
+ */
+ public function expectErrorPattern(string $errorMessagePattern): Response
+ {
+ $errorData = $this->getErrorData();
+ if (preg_match($errorMessagePattern, $errorData) === 0) {
+ $this->error(
+ "The expected error pattern $errorMessagePattern does not match the returned error '$errorData'"
+ );
+ $this->debugOutput();
+ }
+
+ return $this;
+ }
+
+ /**
+ * Expect response status.
+ *
+ * @param string|null $status Expected status.
+ *
+ * @return Response
+ */
+ public function expectStatus(string|null $status): Response {
+ $headers = $this->getHeaders();
+ if (is_null($status) && !isset($headers['status'])) {
+ return $this;
+ }
+ if (!is_null($status) && !isset($headers['status'])) {
+ $this->error('Status is expected but not supplied');
+ } elseif ($status !== $headers['status']) {
+ $statusMessage = $status === null ? "expected not to be set": "expected to be $status";
+ $this->error("Status is $statusMessage but the actual value is {$headers['status']}");
+ }
+
+ return $this;
+ }
+
+ /**
+ * Expect response status not to be set.
+ *
+ * @return Response
+ */
+ public function expectNoStatus(): Response {
+ return $this->expectStatus(null);
+ }
+
/**
* Expect no error in the response.
*
@@ -187,6 +243,8 @@ class Response
echo "----------------- ERR -----------------\n";
echo $this->data['err_response'] . "\n";
echo "---------------------------------------\n\n";
+
+ $this->debugOutputted = true;
}
/**
@@ -331,8 +389,11 @@ class Response
*
* @return bool
*/
- private function error($message): bool
+ private function error(string $message): bool
{
+ if ( ! $this->debugOutputted) {
+ $this->debugOutput();
+ }
echo "ERROR: $message\n";
return false;
diff --git a/sapi/fpm/tests/tester.inc b/sapi/fpm/tests/tester.inc
index 50de960aa8dde..db8f0c04f4a61 100644
--- a/sapi/fpm/tests/tester.inc
+++ b/sapi/fpm/tests/tester.inc
@@ -269,10 +269,11 @@ class Tester
static public function skipIfConfigFails(string $configTemplate)
{
$tester = new self($configTemplate, '', [], self::getCallerFileName());
- $testResult = $tester->testConfig();
+ $testResult = $tester->testConfig(true);
if ($testResult !== null) {
self::clean(2);
- die("skip $testResult");
+ $message = $testResult[0] ?? 'Config failed';
+ die("skip $message");
}
}
@@ -378,17 +379,26 @@ class Tester
/**
* Test configuration file.
*
- * @return null|string
+ * @return null|array
* @throws \Exception
*/
- public function testConfig()
+ public function testConfig($silent = false)
{
$configFile = $this->createConfig();
$cmd = self::findExecutable() . ' -tt -y ' . $configFile . ' 2>&1';
$this->trace('Testing config using command', $cmd, true);
exec($cmd, $output, $code);
if ($code) {
- return preg_replace("/\[.+?\]/", "", $output[0]);
+ $messages = [];
+ foreach ($output as $outputLine) {
+ $message = preg_replace("/\[.+?\]/", "", $outputLine, 1);
+ $messages[] = $message;
+ if ( ! $silent) {
+ $this->error($message, null, false);
+ }
+ }
+
+ return $messages;
}
return null;
@@ -1259,14 +1269,15 @@ class Tester
/**
* Display error.
*
- * @param string $msg
- * @param \Exception|null $exception
+ * @param string $msg Error message.
+ * @param \Exception|null $exception If there is an exception, log its message
+ * @param bool $prefix Whether to prefix the error message
*
* @return false
*/
- private function error($msg, \Exception $exception = null): bool
+ private function error(string $msg, \Exception $exception = null, bool $prefix = true): bool
{
- $this->error = 'ERROR: ' . $msg;
+ $this->error = $prefix ? 'ERROR: ' . $msg : ltrim($msg);
if ($exception) {
$this->error .= '; EXCEPTION: ' . $exception->getMessage();
}
diff --git a/sapi/fpm/www.conf.in b/sapi/fpm/www.conf.in
index dfb41b680e42c..37ac8a194f7f8 100644
--- a/sapi/fpm/www.conf.in
+++ b/sapi/fpm/www.conf.in
@@ -17,9 +17,14 @@
; Default Value: none
;prefix = /path/to/pools/$pool
-; Unix user/group of processes
-; Note: The user is mandatory. If the group is not set, the default user's group
-; will be used.
+; Unix user/group of the child processes. This can be used only if the master
+; process running user is root. It is set after the child process is created.
+; The user and group can be specified either by their name or by their numeric
+; IDs.
+; Note: If the user is root, the executable needs to be started with
+; --allow-to-run-as-root option to work.
+; Default Values: The user is set to master process running user by default.
+; If the group is not set, the user's group is used.
user = @php_fpm_user@
group = @php_fpm_group@
@@ -43,11 +48,12 @@ listen = 127.0.0.1:9000
; permissions must be set in order to allow connections from a web server. Many
; BSD-derived systems allow connections regardless of permissions. The owner
; and group can be specified either by name or by their numeric IDs.
-; Default Values: user and group are set as the running user
-; mode is set to 0660
+; Default Values: Owner is set to the master process running user. If the group
+; is not set, the owner's group is used. Mode is set to 0660.
;listen.owner = @php_fpm_user@
;listen.group = @php_fpm_group@
;listen.mode = 0660
+
; When POSIX Access Control Lists are supported you can set them using
; these options, value is a comma separated list of user/group names.
; When set, listen.owner and listen.group are ignored
diff --git a/sapi/phpdbg/phpdbg.c b/sapi/phpdbg/phpdbg.c
index 0540676e0f5f2..d4ffcdf473df8 100644
--- a/sapi/phpdbg/phpdbg.c
+++ b/sapi/phpdbg/phpdbg.c
@@ -1378,6 +1378,8 @@ int main(int argc, char **argv) /* {{{ */
get_zend_version()
);
}
+ PHPDBG_G(flags) |= PHPDBG_IS_QUITTING;
+ php_module_shutdown();
sapi_deactivate();
sapi_shutdown();
php_ini_builder_deinit(&ini_builder);
diff --git a/sapi/phpdbg/phpdbg_bp.c b/sapi/phpdbg/phpdbg_bp.c
index a20b4bd65db92..07a1fc7a0be5d 100644
--- a/sapi/phpdbg/phpdbg_bp.c
+++ b/sapi/phpdbg/phpdbg_bp.c
@@ -829,19 +829,21 @@ static inline void phpdbg_create_conditional_break(phpdbg_breakcond_t *brake, co
uint32_t cops = CG(compiler_options);
zend_string *bp_code;
- switch (param->type) {
- case STR_PARAM:
- case NUMERIC_FUNCTION_PARAM:
- case METHOD_PARAM:
- case NUMERIC_METHOD_PARAM:
- case FILE_PARAM:
- case ADDR_PARAM:
- /* do nothing */
- break;
+ if (param) {
+ switch (param->type) {
+ case STR_PARAM:
+ case NUMERIC_FUNCTION_PARAM:
+ case METHOD_PARAM:
+ case NUMERIC_METHOD_PARAM:
+ case FILE_PARAM:
+ case ADDR_PARAM:
+ /* do nothing */
+ break;
- default:
- phpdbg_error("Invalid parameter type for conditional breakpoint");
- return;
+ default:
+ phpdbg_error("Invalid parameter type for conditional breakpoint");
+ return;
+ }
}
PHPDBG_BREAK_INIT(new_break, PHPDBG_BREAK_COND);
diff --git a/sapi/phpdbg/phpdbg_prompt.c b/sapi/phpdbg/phpdbg_prompt.c
index a70034737273a..2464e39e5683c 100644
--- a/sapi/phpdbg/phpdbg_prompt.c
+++ b/sapi/phpdbg/phpdbg_prompt.c
@@ -1321,7 +1321,7 @@ PHPDBG_API const char *phpdbg_load_module_or_extension(char **path, const char *
module_entry->handle = handle;
if ((module_entry = zend_register_module_ex(module_entry)) == NULL) {
- phpdbg_error("Unable to register module %s", module_entry->name);
+ phpdbg_error("Unable to register module %s", *name);
goto quit;
}
diff --git a/sapi/phpdbg/phpdbg_utils.c b/sapi/phpdbg/phpdbg_utils.c
index 344b9c73e476d..f638d608905ba 100644
--- a/sapi/phpdbg/phpdbg_utils.c
+++ b/sapi/phpdbg/phpdbg_utils.c
@@ -466,6 +466,9 @@ PHPDBG_API int phpdbg_parse_variable_with_arg(char *input, size_t len, HashTable
case ']':
break;
case '>':
+ if (!last_index) {
+ goto error;
+ }
if (last_index[index_len - 1] == '-') {
new_index = 1;
index_len--;
diff --git a/sapi/phpdbg/tests/watch_007.phpt b/sapi/phpdbg/tests/watch_007.phpt
new file mode 100644
index 0000000000000..f1980d60dd9de
--- /dev/null
+++ b/sapi/phpdbg/tests/watch_007.phpt
@@ -0,0 +1,25 @@
+--TEST--
+Test malformed watchpoint name
+--INI--
+opcache.optimization_level=0
+--PHPDBG--
+b test
+r
+w $>
+q
+--EXPECTF--
+[Successful compilation of %s]
+prompt> [Breakpoint #0 added at test]
+prompt> [Breakpoint #0 in test() at %s:%d, hits: 1]
+>00004: }
+ 00005: test();
+ 00006: $a = 2;
+prompt> [Malformed input]
+prompt>
+--FILE--
+