Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
0f2ddec
PHP-8.5 is now for PHP 8.5.6-dev
edorian Mar 24, 2026
f8e4f64
[skip ci] Regenerate ext/fileinfo/libmagic.patch (GH-21509)
orlitzky Mar 24, 2026
00c4295
Merge branch 'PHP-8.5'
edorian Mar 24, 2026
9caa0a7
Fix bc_str2num accepting strings with embedded null bytes
jorgsowa Mar 22, 2026
ce1761e
ext/dom: add a const qualifier in dom_objects_set_class_ex()(#21511)
Girgias Mar 24, 2026
118fa2c
ext/mysqli: add a const qualifier in mysqli_objects_new() (#21510)
Girgias Mar 24, 2026
674473d
ext/xsl: add const qualifiers when iteration over a CE's parents (#21…
Girgias Mar 24, 2026
a8cc04d
Optimize JSON pretty print indentation performance (GH-21474)
LamentXU123 Mar 24, 2026
8af4e80
ext/opcache: formalize return type of accel_restart_is_active() to bool
Girgias Mar 9, 2026
046b44e
ext/opcache: formalize return type of zend_get_stream_timestamp() to …
Girgias Mar 9, 2026
c136801
ext/opcache: formalize return type of do_validate_timestamps() to zen…
Girgias Mar 9, 2026
b431c36
ext/opcache: formalize return type of check_persistent_script_access(…
Girgias Mar 9, 2026
e7ffec3
ext/opcache: reduce scope of variable
Girgias Mar 9, 2026
d50b91f
ext/opcache: add const qualifiers
Girgias Mar 9, 2026
bcdadd5
ext/opcache: use type size_t instead of type int
Girgias Mar 23, 2026
022fc4c
zend_builtin_functions.c: Use RETURN_THROW() for clarity
Girgias Nov 7, 2025
5b5b17a
zend_builtin_functions.c: use type uint32_t instead of int
Girgias Nov 7, 2025
82866cc
zend_builtin_functions.c: use known string when possible
Girgias Nov 7, 2025
4852e7f
zend_builtin_functions.c: add const qualifiers
Girgias Nov 7, 2025
db8b4b5
zend_builtin_functions.c: remove some variable shadowing
Girgias Nov 7, 2025
afd04af
zend_builtin_functions.c: refactor is_a_impl() to use early returns
Girgias Nov 7, 2025
ee7d02d
zend_builtin_functions.c: use RET{URN|VAL}_BOOL() when possible
Girgias Nov 7, 2025
deb5997
zend_objects_API.h: add some const qualifiers
Girgias Mar 24, 2026
66e4ba1
zend_objects_API.c: reduce scope of variable
Girgias Mar 24, 2026
e4dcd2e
zend_objects_API.c: use uint32_t type instead of int type
Girgias Mar 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ PHP NEWS
ReflectionProperty::skipLazyInitialization after failed LazyProxy
initialization). (Arnaud)

- BCMath:
. Added NUL-byte validation to BCMath functions. (jorgsowa)

- Date:
. Update timelib to 2022.16. (Derick)

Expand Down
2 changes: 2 additions & 0 deletions UPGRADING
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,8 @@ PHP 8.6 UPGRADE NOTES

- JSON:
. Improve performance of encoding arrays and objects.
. Improved performance of indentation generation in json_encode()
when using PHP_JSON_PRETTY_PRINT.

- Standard:
. Improved performance of array_fill_keys().
Expand Down
86 changes: 36 additions & 50 deletions Zend/zend_builtin_functions.c
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ ZEND_FUNCTION(func_num_args)
}

if (zend_forbid_dynamic_call() == FAILURE) {
RETURN_LONG(-1);
RETURN_THROWS();
}

RETURN_LONG(ZEND_CALL_NUM_ARGS(ex));
Expand Down Expand Up @@ -503,7 +503,7 @@ ZEND_FUNCTION(error_reporting)
}
/* }}} */

static bool validate_constant_array_argument(HashTable *ht, int argument_number) /* {{{ */
static bool validate_constant_array_argument(HashTable *ht, uint32_t argument_number) /* {{{ */
{
bool ret = true;
zval *val;
Expand Down Expand Up @@ -590,11 +590,7 @@ ZEND_FUNCTION(define)
/* non persistent */
ZEND_CONSTANT_SET_FLAGS(&c, 0, PHP_USER_CONSTANT);
c.name = zend_string_copy(name);
if (zend_register_constant(&c) != NULL) {
RETURN_TRUE;
} else {
RETURN_FALSE;
}
RETURN_BOOL(zend_register_constant(&c) != NULL);
}
/* }}} */

Expand All @@ -608,11 +604,7 @@ ZEND_FUNCTION(defined)
Z_PARAM_STR(name)
ZEND_PARSE_PARAMETERS_END();

if (zend_get_constant_ex(name, zend_get_executed_scope(), ZEND_FETCH_CLASS_SILENT)) {
RETURN_TRUE;
} else {
RETURN_FALSE;
}
RETURN_BOOL(zend_get_constant_ex(name, zend_get_executed_scope(), ZEND_FETCH_CLASS_SILENT));
}
/* }}} */

Expand All @@ -626,7 +618,7 @@ ZEND_FUNCTION(get_class)
}

if (!obj) {
zend_class_entry *scope = zend_get_executed_scope();
const zend_class_entry *scope = zend_get_executed_scope();

if (scope) {
zend_error(E_DEPRECATED, "Calling get_class() without arguments is deprecated");
Expand Down Expand Up @@ -691,7 +683,6 @@ static void is_a_impl(INTERNAL_FUNCTION_PARAMETERS, bool only_subclass) /* {{{ *
zend_string *class_name;
const zend_class_entry *instance_ce;
bool allow_string = only_subclass;
bool retval;

ZEND_PARSE_PARAMETERS_START(2, 3)
Z_PARAM_ZVAL(obj)
Expand All @@ -718,21 +709,19 @@ static void is_a_impl(INTERNAL_FUNCTION_PARAMETERS, bool only_subclass) /* {{{ *
}

if (!only_subclass && EXPECTED(zend_string_equals(instance_ce->name, class_name))) {
retval = 1;
} else {
const zend_class_entry *ce = zend_lookup_class_ex(class_name, NULL, ZEND_FETCH_CLASS_NO_AUTOLOAD);
if (!ce) {
retval = 0;
} else {
if (only_subclass && instance_ce == ce) {
retval = 0;
} else {
retval = instanceof_function(instance_ce, ce);
}
}
RETURN_TRUE;
}

const zend_class_entry *ce = zend_lookup_class_ex(class_name, NULL, ZEND_FETCH_CLASS_NO_AUTOLOAD);
if (!ce) {
RETURN_FALSE;
}

if (only_subclass && instance_ce == ce) {
RETURN_FALSE;
}

RETURN_BOOL(retval);
RETURN_BOOL(instanceof_function(instance_ce, ce));
}
/* }}} */

Expand Down Expand Up @@ -1026,7 +1015,6 @@ ZEND_FUNCTION(method_exists)
static void _property_exists(zval *return_value, const zval *object, zend_string *property)
{
zend_class_entry *ce;
zend_property_info *property_info;

if (Z_TYPE_P(object) == IS_STRING) {
ce = zend_lookup_class(Z_STR_P(object));
Expand All @@ -1040,18 +1028,17 @@ static void _property_exists(zval *return_value, const zval *object, zend_string
RETURN_THROWS();
}

property_info = zend_hash_find_ptr(&ce->properties_info, property);
const zend_property_info *property_info = zend_hash_find_ptr(&ce->properties_info, property);
if (property_info != NULL
&& (!(property_info->flags & ZEND_ACC_PRIVATE)
|| property_info->ce == ce)) {
RETURN_TRUE;
}

if (Z_TYPE_P(object) == IS_OBJECT &&
Z_OBJ_HANDLER_P(object, has_property)(Z_OBJ_P(object), property, ZEND_PROPERTY_EXISTS, NULL)) {
RETURN_TRUE;
}
RETURN_FALSE;
RETURN_BOOL(
Z_TYPE_P(object) == IS_OBJECT &&
Z_OBJ_HANDLER_P(object, has_property)(Z_OBJ_P(object), property, ZEND_PROPERTY_EXISTS, NULL)
);
}

/* {{{ Checks if the object or class has a property */
Expand Down Expand Up @@ -1111,11 +1098,7 @@ static zend_always_inline void _class_exists_impl(zval *return_value, zend_strin
ce = zend_lookup_class(name);
}

if (ce) {
RETURN_BOOL(((ce->ce_flags & flags) == flags) && !(ce->ce_flags & skip_flags));
} else {
RETURN_FALSE;
}
RETURN_BOOL(ce && ((ce->ce_flags & flags) == flags) && !(ce->ce_flags & skip_flags));
}
/* {{{ */

Expand Down Expand Up @@ -1421,15 +1404,14 @@ static inline void get_declared_class_impl(INTERNAL_FUNCTION_PARAMETERS, int fla
{
zend_string *key;
zval *zv;
zend_class_entry *ce;

ZEND_PARSE_PARAMETERS_NONE();

array_init(return_value);
zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(EG(class_table), key, zv) {
ce = Z_PTR_P(zv);
const zend_class_entry *ce = Z_PTR_P(zv);
if ((ce->ce_flags & (ZEND_ACC_LINKED|ZEND_ACC_INTERFACE|ZEND_ACC_TRAIT)) == flags
&& key
&& ZSTR_VAL(key)[0] != 0) {
Expand Down Expand Up @@ -1547,7 +1529,7 @@ ZEND_FUNCTION(get_resource_type)
if (resource_type) {
RETURN_STRING(resource_type);
} else {
RETURN_STRING("Unknown");
RETURN_INTERNED_STR(ZSTR_KNOWN(ZEND_STR_UNKNOWN_CAPITALIZED));
}
}
/* }}} */
Expand Down Expand Up @@ -1887,7 +1869,7 @@ ZEND_API void zend_fetch_debug_backtrace(zval *return_value, int skip_last, int
zend_execute_data *call, *last_call = NULL;
zend_object *object;
bool fake_frame = false;
int lineno, frameno = 0;
int frameno = 0;
zend_function *func;
zend_string *filename;
zend_string *include_filename = NULL;
Expand All @@ -1908,12 +1890,12 @@ ZEND_API void zend_fetch_debug_backtrace(zval *return_value, int skip_last, int
EG(filename_override) = NULL;
EG(lineno_override) = -1;

zend_string *filename = zend_get_executed_filename_ex();
zend_long lineno = zend_get_executed_lineno();
if (filename && (!zend_string_equals(filename, filename_override) || lineno != lineno_override)) {
zend_string *executed_filename = zend_get_executed_filename_ex();
uint32_t lineno = zend_get_executed_lineno();
if (executed_filename && (!zend_string_equals(executed_filename, filename_override) || lineno != lineno_override)) {
stack_frame = zend_new_array(8);
zend_hash_real_init_mixed(stack_frame);
ZVAL_STR_COPY(&tmp, filename);
ZVAL_STR_COPY(&tmp, executed_filename);
_zend_hash_append_ex(stack_frame, ZSTR_KNOWN(ZEND_STR_FILE), &tmp, 1);
ZVAL_LONG(&tmp, lineno);
_zend_hash_append_ex(stack_frame, ZSTR_KNOWN(ZEND_STR_LINE), &tmp, 1);
Expand Down Expand Up @@ -1976,19 +1958,21 @@ ZEND_API void zend_fetch_debug_backtrace(zval *return_value, int skip_last, int
zval *arg = zend_get_zval_ptr(op_data, op_data->op1_type, &op_data->op1, call);
if (Z_TYPE_P(arg) == IS_UNDEF) goto not_frameless_call;
}
zend_function *func = ZEND_FLF_FUNC(opline);
zend_function *frameless_func = ZEND_FLF_FUNC(opline);
/* Assume frameless functions are not recursive with themselves.
* This condition may be true when observers are enabled:
* Observers will put a call frame on top of the frameless opcode. */
if (last_call && last_call->func == func) {
if (last_call && last_call->func == frameless_func) {
goto not_frameless_call;
}
stack_frame = zend_new_array(8);
zend_hash_real_init_mixed(stack_frame);
ZVAL_STR_COPY(&tmp, func->common.function_name);
ZVAL_STR_COPY(&tmp, frameless_func->common.function_name);
_zend_hash_append_ex(stack_frame, ZSTR_KNOWN(ZEND_STR_FUNCTION), &tmp, 1);
/* Steal file and line from the previous frame. */
if (call->func && ZEND_USER_CODE(call->func->common.type)) {
uint32_t lineno;

filename = call->func->op_array.filename;
if (call->opline->opcode == ZEND_HANDLE_EXCEPTION) {
if (EG(opline_before_exception)) {
Expand Down Expand Up @@ -2040,6 +2024,8 @@ ZEND_API void zend_fetch_debug_backtrace(zval *return_value, int skip_last, int
zend_hash_real_init_mixed(stack_frame);

if (prev && prev->func && ZEND_USER_CODE(prev->func->common.type)) {
uint32_t lineno;

filename = prev->func->op_array.filename;
if (prev->opline->opcode == ZEND_HANDLE_EXCEPTION) {
if (EG(opline_before_exception)) {
Expand Down
8 changes: 3 additions & 5 deletions Zend/zend_objects_API.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,7 @@ ZEND_API void ZEND_FASTCALL zend_objects_store_call_destructors(zend_objects_sto
{
EG(flags) |= EG_FLAGS_OBJECT_STORE_NO_REUSE;
if (objects->top > 1) {
uint32_t i;
for (i = 1; i < objects->top; i++) {
for (uint32_t i = 1; i < objects->top; i++) {
zend_object *obj = objects->object_buckets[i];
if (IS_OBJ_VALID(obj)) {
if (!(OBJ_FLAGS(obj) & IS_OBJ_DESTRUCTOR_CALLED)) {
Expand Down Expand Up @@ -128,20 +127,19 @@ ZEND_API void ZEND_FASTCALL zend_objects_store_free_object_storage(zend_objects_
/* Store objects API */
static ZEND_COLD zend_never_inline void ZEND_FASTCALL zend_objects_store_put_cold(zend_object *object)
{
int handle;
uint32_t new_size = 2 * EG(objects_store).size;

EG(objects_store).object_buckets = (zend_object **) erealloc(EG(objects_store).object_buckets, new_size * sizeof(zend_object*));
/* Assign size after realloc, in case it fails */
EG(objects_store).size = new_size;
handle = EG(objects_store).top++;
uint32_t handle = EG(objects_store).top++;
object->handle = handle;
EG(objects_store).object_buckets[handle] = object;
}

ZEND_API void ZEND_FASTCALL zend_objects_store_put(zend_object *object)
{
int handle;
uint32_t handle;

/* When in shutdown sequence - do not reuse previously freed handles, to make sure
* the dtors for newly created objects are called in zend_objects_store_call_destructors() loop
Expand Down
4 changes: 2 additions & 2 deletions Zend/zend_objects_API.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ static zend_always_inline void zend_object_release(zend_object *obj)
}
}

static zend_always_inline size_t zend_object_properties_size(zend_class_entry *ce)
static zend_always_inline size_t zend_object_properties_size(const zend_class_entry *ce)
{
return sizeof(zval) *
(ce->default_properties_count -
Expand All @@ -90,7 +90,7 @@ static zend_always_inline size_t zend_object_properties_size(zend_class_entry *c
/* Allocates object type and zeros it, but not the standard zend_object and properties.
* Standard object MUST be initialized using zend_object_std_init().
* Properties MUST be initialized using object_properties_init(). */
static zend_always_inline void *zend_object_alloc(size_t obj_size, zend_class_entry *ce) {
static zend_always_inline void *zend_object_alloc(size_t obj_size, const zend_class_entry *ce) {
void *obj = emalloc(obj_size + zend_object_properties_size(ce));
memset(obj, 0, obj_size - sizeof(zend_object));
return obj;
Expand Down
6 changes: 3 additions & 3 deletions ext/bcmath/libbcmath/src/str2num.c
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ bool bc_str2num(bc_num *num, const char *str, const char *end, size_t scale, siz
const char *decimal_point = (*ptr == '.') ? ptr : NULL;

/* If a non-digit and non-decimal-point indicator is in the string, i.e. an invalid character */
if (UNEXPECTED(!decimal_point && *ptr != '\0')) {
if (UNEXPECTED(!decimal_point && ptr != end)) {
goto fail;
}

Expand All @@ -140,7 +140,7 @@ bool bc_str2num(bc_num *num, const char *str, const char *end, size_t scale, siz
/* search */
fractional_ptr = fractional_end = decimal_point + 1;
/* For strings that end with a decimal point, such as "012." */
if (UNEXPECTED(*fractional_ptr == '\0')) {
if (UNEXPECTED(fractional_ptr == end)) {
if (full_scale) {
*full_scale = 0;
}
Expand All @@ -149,7 +149,7 @@ bool bc_str2num(bc_num *num, const char *str, const char *end, size_t scale, siz

/* validate */
fractional_end = bc_count_digits(fractional_ptr, end);
if (UNEXPECTED(*fractional_end != '\0')) {
if (UNEXPECTED(fractional_end != end)) {
/* invalid num */
goto fail;
}
Expand Down
59 changes: 59 additions & 0 deletions ext/bcmath/tests/sec_embedded_null_truncation.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
--TEST--
bcmath strings with embedded null bytes must be rejected as not well-formed
--EXTENSIONS--
bcmath
--FILE--
<?php
$cases = [
"100\x005",
"1.2\x003",
"\x00123",
];

$calls = [
fn($s) => bcadd($s, "0"),
fn($s) => bcsub($s, "0"),
fn($s) => bcmul($s, "1"),
fn($s) => bcdiv($s, "1"),
fn($s) => bcmod($s, "7"),
fn($s) => bcpow($s, "1"),
fn($s) => bccomp($s, "0"),
fn($s) => bcsqrt($s),
];

foreach ($cases as $s) {
foreach ($calls as $fn) {
try {
$fn($s);
echo "FAIL: accepted\n";
} catch (\ValueError $e) {
echo $e->getMessage() . "\n";
}
}
}
?>
--EXPECT--
bcadd(): Argument #1 ($num1) is not well-formed
bcsub(): Argument #1 ($num1) is not well-formed
bcmul(): Argument #1 ($num1) is not well-formed
bcdiv(): Argument #1 ($num1) is not well-formed
bcmod(): Argument #1 ($num1) is not well-formed
bcpow(): Argument #1 ($num) is not well-formed
bccomp(): Argument #1 ($num1) is not well-formed
bcsqrt(): Argument #1 ($num) is not well-formed
bcadd(): Argument #1 ($num1) is not well-formed
bcsub(): Argument #1 ($num1) is not well-formed
bcmul(): Argument #1 ($num1) is not well-formed
bcdiv(): Argument #1 ($num1) is not well-formed
bcmod(): Argument #1 ($num1) is not well-formed
bcpow(): Argument #1 ($num) is not well-formed
bccomp(): Argument #1 ($num1) is not well-formed
bcsqrt(): Argument #1 ($num) is not well-formed
bcadd(): Argument #1 ($num1) is not well-formed
bcsub(): Argument #1 ($num1) is not well-formed
bcmul(): Argument #1 ($num1) is not well-formed
bcdiv(): Argument #1 ($num1) is not well-formed
bcmod(): Argument #1 ($num1) is not well-formed
bcpow(): Argument #1 ($num) is not well-formed
bccomp(): Argument #1 ($num1) is not well-formed
bcsqrt(): Argument #1 ($num) is not well-formed
2 changes: 1 addition & 1 deletion ext/dom/php_dom.c
Original file line number Diff line number Diff line change
Expand Up @@ -1496,7 +1496,7 @@ void dom_objects_free_storage(zend_object *object)

static void dom_objects_set_class_ex(zend_class_entry *class_type, dom_object *intern)
{
zend_class_entry *base_class = class_type;
const zend_class_entry *base_class = class_type;
while ((base_class->type != ZEND_INTERNAL_CLASS || base_class->info.internal.module->module_number != dom_module_entry.module_number) && base_class->parent != NULL) {
base_class = base_class->parent;
}
Expand Down
Loading