From d66e87b02ead158df288569487446e7c22206138 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Sun, 1 Jun 2025 17:58:46 +0200 Subject: [PATCH 001/682] Fix line assignment in zend_ast_create_va() The intent here was to assign the first found line. Instead this always fell back to CG(zend_lineno). Not sure if this line matters for anything in php-src, but the issue was observed in https://github.com/nikic/php-ast/issues/247. --- Zend/zend_ast.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index 09cc89684f877..f3f03940c62a6 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -279,7 +279,7 @@ ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_va( ast->attr = attr; for (uint32_t i = 0; i < children; i++) { ast->child[i] = va_arg(*va, zend_ast *); - if (lineno != (uint32_t)-1 && ast->child[i]) { + if (lineno == (uint32_t)-1 && ast->child[i]) { lineno = zend_ast_get_lineno(ast->child[i]); } } From 81593cfc6a08e56df836c6382e9873191b9cd6c1 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Mon, 2 Jun 2025 09:23:39 +0300 Subject: [PATCH 002/682] Update IR IR commit: e4343be0082897510c40a1b57baff427c6858878 --- ext/opcache/jit/ir/ir.c | 16 ++++++- ext/opcache/jit/ir/ir.h | 38 ++++++++++------ ext/opcache/jit/ir/ir_cfg.c | 50 ++++++++++++++++++++- ext/opcache/jit/ir/ir_emit.c | 4 +- ext/opcache/jit/ir/ir_fold.h | 78 ++++++++++++++++++++++++++++++++- ext/opcache/jit/ir/ir_private.h | 6 +-- ext/opcache/jit/ir/ir_sccp.c | 23 +++++++--- ext/opcache/jit/ir/ir_x86.dasc | 4 +- 8 files changed, 189 insertions(+), 30 deletions(-) diff --git a/ext/opcache/jit/ir/ir.c b/ext/opcache/jit/ir/ir.c index d9f7e3d0f7836..a9f55cc0e466c 100644 --- a/ext/opcache/jit/ir/ir.c +++ b/ext/opcache/jit/ir/ir.c @@ -918,7 +918,7 @@ static ir_ref _ir_fold_cse(ir_ctx *ctx, uint32_t opt, ir_ref op1, ir_ref op2, ir #define IR_FOLD_EMIT goto ir_fold_emit #define IR_FOLD_NEXT break -#include "ir_fold_hash.h" +#include #define IR_FOLD_RULE(x) ((x) >> 21) #define IR_FOLD_KEY(x) ((x) & 0x1fffff) @@ -1485,6 +1485,18 @@ void ir_update_op(ir_ctx *ctx, ir_ref ref, uint32_t idx, ir_ref new_val) void ir_array_grow(ir_array *a, uint32_t size) { IR_ASSERT(size > a->size); + if (size >= 256) { + size = IR_ALIGNED_SIZE(size, 256); + } else { + /* Use big enough power of 2 */ + size -= 1; + size |= (size >> 1); + size |= (size >> 2); + size |= (size >> 4); +// size |= (size >> 8); +// size |= (size >> 16); + size += 1; + } a->refs = ir_mem_realloc(a->refs, size * sizeof(ir_ref)); a->size = size; } @@ -1820,7 +1832,7 @@ int ir_mem_flush(void *ptr, size_t size) #else void *ir_mem_mmap(size_t size) { - int prot_flags = PROT_EXEC; + int prot_flags = PROT_EXEC; #if defined(__NetBSD__) prot_flags |= PROT_MPROTECT(PROT_READ|PROT_WRITE); #endif diff --git a/ext/opcache/jit/ir/ir.h b/ext/opcache/jit/ir/ir.h index 60c501d0bd470..ec5e57129c9ab 100644 --- a/ext/opcache/jit/ir/ir.h +++ b/ext/opcache/jit/ir/ir.h @@ -154,19 +154,27 @@ typedef enum _ir_type { } ir_type; #ifdef IR_64 -# define IR_SIZE_T IR_U64 -# define IR_SSIZE_T IR_I64 -# define IR_UINTPTR_T IR_U64 -# define IR_INTPTR_T IR_I64 -# define IR_C_UINTPTR IR_U64 -# define IR_C_INTPTR IR_I64 +# define IR_SIZE_T IR_U64 +# define IR_SSIZE_T IR_I64 +# define IR_UINTPTR_T IR_U64 +# define IR_INTPTR_T IR_I64 +# define IR_C_UINTPTR IR_U64 +# define IR_C_INTPTR IR_I64 +# define ir_const_size_t ir_const_u64 +# define ir_const_ssize_t ir_const_i64 +# define ir_const_uintptr_t ir_const_u64 +# define ir_const_intptr_t ir_const_i64 #else -# define IR_SIZE_T IR_U32 -# define IR_SSIZE_T IR_I32 -# define IR_UINTPTR_T IR_U32 -# define IR_INTPTR_T IR_I32 -# define IR_C_UINTPTR IR_U32 -# define IR_C_INTPTR IR_I32 +# define IR_SIZE_T IR_U32 +# define IR_SSIZE_T IR_I32 +# define IR_UINTPTR_T IR_U32 +# define IR_INTPTR_T IR_I32 +# define IR_C_UINTPTR IR_U32 +# define IR_C_INTPTR IR_I32 +# define ir_const_size_t ir_const_u32 +# define ir_const_ssize_t ir_const_i32 +# define ir_const_uintptr_t ir_const_u32 +# define ir_const_intptr_t ir_const_i32 #endif /* List of IR opcodes @@ -401,8 +409,10 @@ typedef int32_t ir_ref; #define IR_CONSTS_LIMIT_MIN (-(IR_TRUE - 1)) #define IR_INSNS_LIMIT_MIN (IR_UNUSED + 1) +/* ADDR_MEMBER is neccessary to workaround MSVC C preprocessor bug */ #ifndef IR_64 -# define ADDR_MEMBER uintptr_t addr; +# define ADDR_MEMBER uintptr_t addr; \ + void *ptr; #else # define ADDR_MEMBER #endif @@ -412,6 +422,7 @@ typedef union _ir_val { int64_t i64; #ifdef IR_64 uintptr_t addr; + void *ptr; #endif IR_STRUCT_LOHI( union { @@ -466,6 +477,7 @@ typedef struct _ir_insn { }, union { ir_ref op1; + ir_ref ref; ir_ref prev_const; } ); diff --git a/ext/opcache/jit/ir/ir_cfg.c b/ext/opcache/jit/ir/ir_cfg.c index 16facae51b1f6..01532c8ea3e30 100644 --- a/ext/opcache/jit/ir/ir_cfg.c +++ b/ext/opcache/jit/ir/ir_cfg.c @@ -77,6 +77,51 @@ void ir_reset_cfg(ir_ctx *ctx) } } +static uint32_t IR_NEVER_INLINE ir_cfg_remove_dead_inputs(ir_ctx *ctx, uint32_t *_blocks, ir_block *blocks, uint32_t bb_count) +{ + uint32_t b, count = 0; + ir_block *bb = blocks + 1; + ir_insn *insn; + ir_ref i, j, n, *ops, input; + + for (b = 1; b <= bb_count; b++, bb++) { + bb->successors = count; + count += ctx->use_lists[bb->end].count; + bb->successors_count = 0; + bb->predecessors = count; + insn = &ctx->ir_base[bb->start]; + if (insn->op == IR_MERGE || insn->op == IR_LOOP_BEGIN) { + n = insn->inputs_count; + ops = insn->ops; + for (i = 1, j = 1; i <= n; i++) { + input = ops[i]; + if (_blocks[input]) { + if (i != j) { + ops[j] = ops[i]; + } + j++; + } else if (input > 0) { + ir_use_list_remove_one(ctx, input, bb->start); + } + } + j--; + if (j != n) { + if (j == 1) { + insn->op = IR_BEGIN; + } + insn->inputs_count = j; + bb->predecessors_count = j; + j++; + for (;j <= n; j++) { + ops[j] = IR_UNUSED; + } + } + } + count += bb->predecessors_count; + } + return count; +} + int ir_build_cfg(ir_ctx *ctx) { ir_ref n, *p, ref, start, end; @@ -239,7 +284,10 @@ int ir_build_cfg(ir_ctx *ctx) bb++; } IR_BITSET_FOREACH_END(); bb_count = b - 1; - IR_ASSERT(count == edges_count * 2); + if (UNEXPECTED(count != edges_count * 2)) { + count = ir_cfg_remove_dead_inputs(ctx, _blocks, blocks, bb_count); + IR_ASSERT(count != edges_count * 2); + } ir_mem_free(bb_starts); /* Create an array of successor/predecessors control edges */ diff --git a/ext/opcache/jit/ir/ir_emit.c b/ext/opcache/jit/ir/ir_emit.c index 5cf44a51d0f48..c82655daf48db 100644 --- a/ext/opcache/jit/ir/ir_emit.c +++ b/ext/opcache/jit/ir/ir_emit.c @@ -415,9 +415,9 @@ static int ir_const_label(ir_ctx *ctx, ir_ref ref) } #if defined(IR_TARGET_X86) || defined(IR_TARGET_X64) -# include "ir_emit_x86.h" +# include #elif defined(IR_TARGET_AARCH64) -# include "ir_emit_aarch64.h" +# include #else # error "Unknown IR target" #endif diff --git a/ext/opcache/jit/ir/ir_fold.h b/ext/opcache/jit/ir/ir_fold.h index 78f3ca0c01e5f..88539e52ab085 100644 --- a/ext/opcache/jit/ir/ir_fold.h +++ b/ext/opcache/jit/ir/ir_fold.h @@ -486,10 +486,36 @@ IR_FOLD(MUL(C_FLOAT, C_FLOAT)) } IR_FOLD(DIV(C_U8, C_U8)) +{ + IR_ASSERT(IR_OPT_TYPE(opt) == op1_insn->type); + if (op2_insn->val.u64 == 0) { + /* division by zero */ + IR_FOLD_EMIT; + } + IR_FOLD_CONST_U(op1_insn->val.u8 / op2_insn->val.u8); +} + IR_FOLD(DIV(C_U16, C_U16)) +{ + IR_ASSERT(IR_OPT_TYPE(opt) == op1_insn->type); + if (op2_insn->val.u64 == 0) { + /* division by zero */ + IR_FOLD_EMIT; + } + IR_FOLD_CONST_U(op1_insn->val.u16 / op2_insn->val.u16); +} + IR_FOLD(DIV(C_U32, C_U32)) +{ + IR_ASSERT(IR_OPT_TYPE(opt) == op1_insn->type); + if (op2_insn->val.u64 == 0) { + /* division by zero */ + IR_FOLD_EMIT; + } + IR_FOLD_CONST_U(op1_insn->val.u32 / op2_insn->val.u32); +} + IR_FOLD(DIV(C_U64, C_U64)) -IR_FOLD(DIV(C_ADDR, C_ADDR)) { IR_ASSERT(IR_OPT_TYPE(opt) == op1_insn->type); if (op2_insn->val.u64 == 0) { @@ -499,9 +525,46 @@ IR_FOLD(DIV(C_ADDR, C_ADDR)) IR_FOLD_CONST_U(op1_insn->val.u64 / op2_insn->val.u64); } +IR_FOLD(DIV(C_ADDR, C_ADDR)) +{ + IR_ASSERT(IR_OPT_TYPE(opt) == op1_insn->type); + if (op2_insn->val.u64 == 0) { + /* division by zero */ + IR_FOLD_EMIT; + } + IR_FOLD_CONST_U(op1_insn->val.addr / op2_insn->val.addr); +} + IR_FOLD(DIV(C_I8, C_I8)) +{ + IR_ASSERT(IR_OPT_TYPE(opt) == op1_insn->type); + if (op2_insn->val.i64 == 0) { + /* division by zero */ + IR_FOLD_EMIT; + } + IR_FOLD_CONST_I(op1_insn->val.i8 / op2_insn->val.i8); +} + IR_FOLD(DIV(C_I16, C_I16)) +{ + IR_ASSERT(IR_OPT_TYPE(opt) == op1_insn->type); + if (op2_insn->val.i64 == 0) { + /* division by zero */ + IR_FOLD_EMIT; + } + IR_FOLD_CONST_I(op1_insn->val.i16 / op2_insn->val.i16); +} + IR_FOLD(DIV(C_I32, C_I32)) +{ + IR_ASSERT(IR_OPT_TYPE(opt) == op1_insn->type); + if (op2_insn->val.i64 == 0) { + /* division by zero */ + IR_FOLD_EMIT; + } + IR_FOLD_CONST_I(op1_insn->val.i32 / op2_insn->val.i32); +} + IR_FOLD(DIV(C_I64, C_I64)) { IR_ASSERT(IR_OPT_TYPE(opt) == op1_insn->type); @@ -1135,6 +1198,7 @@ IR_FOLD(MAX(C_FLOAT, C_FLOAT)) IR_FOLD(SEXT(C_I8)) IR_FOLD(SEXT(C_U8)) IR_FOLD(SEXT(C_BOOL)) +IR_FOLD(SEXT(C_CHAR)) { IR_ASSERT(IR_IS_TYPE_INT(IR_OPT_TYPE(opt))); IR_ASSERT(ir_type_size[IR_OPT_TYPE(opt)] > ir_type_size[op1_insn->type]); @@ -1160,6 +1224,7 @@ IR_FOLD(SEXT(C_U32)) IR_FOLD(ZEXT(C_I8)) IR_FOLD(ZEXT(C_U8)) IR_FOLD(ZEXT(C_BOOL)) +IR_FOLD(ZEXT(C_CHAR)) { IR_ASSERT(IR_IS_TYPE_INT(IR_OPT_TYPE(opt))); IR_ASSERT(ir_type_size[IR_OPT_TYPE(opt)] > ir_type_size[op1_insn->type]); @@ -1195,12 +1260,14 @@ IR_FOLD(TRUNC(C_U64)) default: IR_ASSERT(0); case IR_I8: + case IR_CHAR: IR_FOLD_CONST_I(op1_insn->val.i8); case IR_I16: IR_FOLD_CONST_I(op1_insn->val.i16); case IR_I32: IR_FOLD_CONST_I(op1_insn->val.i32); case IR_U8: + case IR_BOOL: IR_FOLD_CONST_U(op1_insn->val.u8); case IR_U16: IR_FOLD_CONST_U(op1_insn->val.u16); @@ -1474,6 +1541,10 @@ IR_FOLD(EQ(SEXT, C_ADDR)) } else { ir_type type = ctx->ir_base[op1_insn->op1].type; + if (op1_insn->op == IR_ZEXT + && (op2_insn->val.u64 >> (ir_type_size[type] * 8)) != 0) { + IR_FOLD_NEXT; + } if (IR_IS_TYPE_SIGNED(type)) { switch (ir_type_size[type]) { case 1: val.i64 = op2_insn->val.i8; break; @@ -1493,6 +1564,7 @@ IR_FOLD(EQ(SEXT, C_ADDR)) op2 = ir_const(ctx, val, type); IR_FOLD_RESTART; } + IR_FOLD_NEXT; } @@ -1518,6 +1590,10 @@ IR_FOLD(NE(SEXT, C_ADDR)) } else { ir_type type = ctx->ir_base[op1_insn->op1].type; + if (op1_insn->op == IR_ZEXT + && (op2_insn->val.u64 >> (ir_type_size[type] * 8)) != 0) { + IR_FOLD_NEXT; + } if (IR_IS_TYPE_SIGNED(type)) { switch (ir_type_size[type]) { case 1: val.i64 = op2_insn->val.i8; break; diff --git a/ext/opcache/jit/ir/ir_private.h b/ext/opcache/jit/ir/ir_private.h index 9c69d6074defe..69a0101d24ee2 100644 --- a/ext/opcache/jit/ir/ir_private.h +++ b/ext/opcache/jit/ir/ir_private.h @@ -255,9 +255,7 @@ IR_ALWAYS_INLINE void ir_arena_free(ir_arena *arena) IR_ALWAYS_INLINE void* ir_arena_alloc(ir_arena **arena_ptr, size_t size) { ir_arena *arena = *arena_ptr; - char *ptr = arena->ptr; - - size = IR_ALIGNED_SIZE(size, 8); + char *ptr = (char*)IR_ALIGNED_SIZE((uintptr_t)arena->ptr, 8); if (EXPECTED(size <= (size_t)(arena->end - ptr))) { arena->ptr = ptr + size; @@ -283,7 +281,7 @@ IR_ALWAYS_INLINE void* ir_arena_checkpoint(ir_arena *arena) return arena->ptr; } -IR_ALWAYS_INLINE void ir_release(ir_arena **arena_ptr, void *checkpoint) +IR_ALWAYS_INLINE void ir_arena_release(ir_arena **arena_ptr, void *checkpoint) { ir_arena *arena = *arena_ptr; diff --git a/ext/opcache/jit/ir/ir_sccp.c b/ext/opcache/jit/ir/ir_sccp.c index 8480861f91fe7..2e006516df818 100644 --- a/ext/opcache/jit/ir/ir_sccp.c +++ b/ext/opcache/jit/ir/ir_sccp.c @@ -1994,10 +1994,16 @@ static bool ir_try_promote_induction_var_ext(ir_ctx *ctx, ir_ref ext_ref, ir_ref if (use_insn->op >= IR_EQ && use_insn->op <= IR_UGT) { if (use_insn->op1 == phi_ref) { + if (IR_IS_TYPE_SIGNED(type) != IR_IS_TYPE_SIGNED(ctx->ir_base[use_insn->op2].type)) { + return 0; + } if (ir_is_cheaper_ext(ctx, use_insn->op2, ctx->ir_base[phi_ref].op1, ext_ref, op)) { continue; } } else if (use_insn->op2 == phi_ref) { + if (IR_IS_TYPE_SIGNED(type) != IR_IS_TYPE_SIGNED(ctx->ir_base[use_insn->op1].type)) { + return 0; + } if (ir_is_cheaper_ext(ctx, use_insn->op1, ctx->ir_base[phi_ref].op1, ext_ref, op)) { continue; } @@ -2027,10 +2033,16 @@ static bool ir_try_promote_induction_var_ext(ir_ctx *ctx, ir_ref ext_ref, ir_ref if (use_insn->op >= IR_EQ && use_insn->op <= IR_UGT) { if (use_insn->op1 == phi_ref) { + if (IR_IS_TYPE_SIGNED(type) != IR_IS_TYPE_SIGNED(ctx->ir_base[use_insn->op2].type)) { + return 0; + } if (ir_is_cheaper_ext(ctx, use_insn->op2, ctx->ir_base[phi_ref].op1, ext_ref, op)) { continue; } } else if (use_insn->op2 == phi_ref) { + if (IR_IS_TYPE_SIGNED(type) != IR_IS_TYPE_SIGNED(ctx->ir_base[use_insn->op1].type)) { + return 0; + } if (ir_is_cheaper_ext(ctx, use_insn->op1, ctx->ir_base[phi_ref].op1, ext_ref, op)) { continue; } @@ -3570,11 +3582,12 @@ void ir_iter_opt(ir_ctx *ctx, ir_bitqueue *worklist) if (val_insn->type == insn->type) { ir_iter_replace_insn(ctx, i, val, worklist); } else { - IR_ASSERT(!IR_IS_CONST_REF(insn->op2)); - ir_use_list_remove_one(ctx, insn->op2, i); - if (ir_is_dead(ctx, insn->op2)) { - /* schedule DCE */ - ir_bitqueue_add(worklist, insn->op2); + if (!IR_IS_CONST_REF(insn->op2)) { + ir_use_list_remove_one(ctx, insn->op2, i); + if (ir_is_dead(ctx, insn->op2)) { + /* schedule DCE */ + ir_bitqueue_add(worklist, insn->op2); + } } if (!IR_IS_CONST_REF(val)) { ir_use_list_add(ctx, val, i); diff --git a/ext/opcache/jit/ir/ir_x86.dasc b/ext/opcache/jit/ir/ir_x86.dasc index d01a8c41359aa..76602c2b4bcf5 100644 --- a/ext/opcache/jit/ir/ir_x86.dasc +++ b/ext/opcache/jit/ir/ir_x86.dasc @@ -3236,7 +3236,7 @@ static void ir_emit_store_mem_int_const(ir_ctx *ctx, ir_type type, ir_mem mem, i val = (int64_t)(intptr_t)ir_sym_val(ctx, val_insn); } - if (sizeof(void*) == 4 || IR_IS_SIGNED_32BIT(val)) { + if (ir_type_size[val_insn->type] <= 4 || IR_IS_SIGNED_32BIT(val)) { if (is_arg && ir_type_size[type] < 4) { type = IR_U32; } @@ -3664,7 +3664,7 @@ static int32_t ir_fuse_imm(ir_ctx *ctx, ir_ref ref) IR_ASSERT(IR_IS_SIGNED_32BIT((intptr_t)addr)); return (int32_t)(intptr_t)addr; } else { - IR_ASSERT(IR_IS_SIGNED_32BIT(val_insn->val.i32)); + IR_ASSERT(ir_type_size[val_insn->type] == 4 || IR_IS_SIGNED_32BIT(val_insn->val.i64)); return val_insn->val.i32; } } From f64e3d5d9ebd72f5d812136ac612fae435c620a0 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc <365207+arnaud-lb@users.noreply.github.com> Date: Mon, 2 Jun 2025 08:49:25 +0200 Subject: [PATCH 003/682] Fix build (#18716) --- ext/uri/config.m4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/uri/config.m4 b/ext/uri/config.m4 index 6cb0e6a27bcb4..f29bbe58bd32e 100644 --- a/ext/uri/config.m4 +++ b/ext/uri/config.m4 @@ -15,6 +15,6 @@ $URIPARSER_DIR/src/UriMemory.c $URIPARSER_DIR/src/UriNormalize.c $URIPARSER_DIR/ $URIPARSER_DIR/src/UriParse.c $URIPARSER_DIR/src/UriParseBase.c $URIPARSER_DIR/src/UriQuery.c \ $URIPARSER_DIR/src/UriRecompose.c $URIPARSER_DIR/src/UriResolve.c $URIPARSER_DIR/src/UriShorten.c" -PHP_NEW_EXTENSION(uri, [php_uri.c $URIPARSER_SOURCES], [no],,[-I$ext_builddir/$URIPARSER_DIR/include -DURI_STATIC_BUILD -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1]) +PHP_NEW_EXTENSION(uri, [php_uri.c $URIPARSER_SOURCES], [no],,[-I$ext_srcdir/$URIPARSER_DIR/include -DURI_STATIC_BUILD -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1]) PHP_ADD_EXTENSION_DEP(uri, lexbor) PHP_ADD_BUILD_DIR($ext_builddir/$URIPARSER_DIR/src $ext_builddir/$URIPARSER_DIR/include) From b871261c10fcf5ffef3851ae31ac12a0170044d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=9A=D0=BE=D0=B2?= =?UTF-8?q?=D0=B0=D0=BB=D1=91=D0=B2?= <115738313+andr3y-k0v4l3v@users.noreply.github.com> Date: Mon, 2 Jun 2025 12:55:25 +0300 Subject: [PATCH 004/682] ext/mysqlnd/mysqlnd_auth.c: Add error handling for invalid public key size (#18663) Reported-by: Pavel Nekrasov Signed-off-by: Andrey Kovalev Co-authored-by: Andrey Kovalev --- ext/mysqlnd/mysqlnd_auth.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/ext/mysqlnd/mysqlnd_auth.c b/ext/mysqlnd/mysqlnd_auth.c index b8a23f87c663e..691375b1a6959 100644 --- a/ext/mysqlnd/mysqlnd_auth.c +++ b/ext/mysqlnd/mysqlnd_auth.c @@ -1005,9 +1005,19 @@ void php_mysqlnd_scramble_sha2(zend_uchar * const buffer, const zend_uchar * con static size_t mysqlnd_caching_sha2_public_encrypt(MYSQLND_CONN_DATA * conn, mysqlnd_rsa_t server_public_key, size_t passwd_len, unsigned char **crypted, char *xor_str) { - size_t server_public_key_len = (size_t) EVP_PKEY_size(server_public_key); - DBG_ENTER("mysqlnd_caching_sha2_public_encrypt"); + + int pkey_size = EVP_PKEY_size(server_public_key); + + if (pkey_size <= 0) { + EVP_PKEY_free(server_public_key); + SET_CLIENT_ERROR(conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, "invalid public key size"); + DBG_ERR("invalid public key size"); + DBG_RETURN(0); + } + + size_t server_public_key_len = (size_t) pkey_size; + /* Because RSA_PKCS1_OAEP_PADDING is used there is a restriction on the passwd_len. RSA_PKCS1_OAEP_PADDING is recommended for new applications. See more here: From 88f546b166bc9845fb398ec112de526a8cce0b7f Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sun, 1 Jun 2025 18:45:04 +0200 Subject: [PATCH 005/682] Fix missing empty string checks in ext/enchant The library requires the tags to be non-empty, and also requires the ordering to be non-empty. For the tags, otherwise, assertion failures can be observed. Closes GH-18733. --- NEWS | 1 + ext/enchant/enchant.c | 15 ++++++++++++ .../tests/broker_dict_exists_empty.phpt | 17 ++++++++++++++ .../tests/broker_set_ordering_empty.phpt | 23 +++++++++++++++++++ 4 files changed, 56 insertions(+) create mode 100644 ext/enchant/tests/broker_dict_exists_empty.phpt create mode 100644 ext/enchant/tests/broker_set_ordering_empty.phpt diff --git a/NEWS b/NEWS index baf838481682c..a2ce7e7ad2d1e 100644 --- a/NEWS +++ b/NEWS @@ -71,6 +71,7 @@ PHP NEWS - Enchant: . Added enchant_dict_remove_from_session(). (nielsdos) . Added enchant_dict_remove(). (nielsdos) + . Fix missing empty string checks. (nielsdos) - EXIF: . Add OffsetTime* Exif tags. (acc987) diff --git a/ext/enchant/enchant.c b/ext/enchant/enchant.c index d1d9ee60c440a..eedb49b69e8f9 100644 --- a/ext/enchant/enchant.c +++ b/ext/enchant/enchant.c @@ -529,6 +529,11 @@ PHP_FUNCTION(enchant_broker_dict_exists) PHP_ENCHANT_GET_BROKER; + if (taglen == 0) { + zend_argument_must_not_be_empty_error(2); + RETURN_THROWS(); + } + RETURN_BOOL(enchant_broker_dict_exists(pbroker->pbroker, tag)); } /* }}} */ @@ -554,6 +559,16 @@ PHP_FUNCTION(enchant_broker_set_ordering) PHP_ENCHANT_GET_BROKER; + if (ptaglen == 0) { + zend_argument_must_not_be_empty_error(2); + RETURN_THROWS(); + } + + if (porderinglen == 0) { + zend_argument_must_not_be_empty_error(3); + RETURN_THROWS(); + } + enchant_broker_set_ordering(pbroker->pbroker, ptag, pordering); RETURN_TRUE; } diff --git a/ext/enchant/tests/broker_dict_exists_empty.phpt b/ext/enchant/tests/broker_dict_exists_empty.phpt new file mode 100644 index 0000000000000..ce4a2ae436251 --- /dev/null +++ b/ext/enchant/tests/broker_dict_exists_empty.phpt @@ -0,0 +1,17 @@ +--TEST-- +enchant_broker_dict_exists() function - empty tag +--EXTENSIONS-- +enchant +--FILE-- +getMessage(), "\n"; +} +echo "Done\n"; +?> +--EXPECT-- +enchant_broker_dict_exists(): Argument #2 ($tag) must not be empty +Done diff --git a/ext/enchant/tests/broker_set_ordering_empty.phpt b/ext/enchant/tests/broker_set_ordering_empty.phpt new file mode 100644 index 0000000000000..f0df14dd74d67 --- /dev/null +++ b/ext/enchant/tests/broker_set_ordering_empty.phpt @@ -0,0 +1,23 @@ +--TEST-- +enchant_broker_set_ordering() function - empty tag +--EXTENSIONS-- +enchant +--FILE-- +getMessage(), "\n"; +} +try { + enchant_broker_set_ordering($broker, '*', ''); +} catch (ValueError $e) { + echo $e->getMessage(), "\n"; +} +echo "Done\n"; +?> +--EXPECT-- +enchant_broker_set_ordering(): Argument #2 ($tag) must not be empty +enchant_broker_set_ordering(): Argument #3 ($ordering) must not be empty +Done From 36891a677593c6b2cfcac29adae7cd26f21b754e Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Mon, 2 Jun 2025 23:35:42 +0200 Subject: [PATCH 006/682] Move VM exception checks (#18730) Checking the non-exception path without arguments first, this avoids a redundant check in the case without arguments. The exception path may become more expensive, but we don't optimize for exception flow, rather we optimize for the happy flow. The other paths are unaffected. --- Zend/zend_vm_def.h | 8 ++++---- Zend/zend_vm_execute.h | 24 ++++++++++++------------ 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 95d9a44f80dd7..617e114dd05db 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -5962,16 +5962,16 @@ ZEND_VM_HANDLER(68, ZEND_NEW, UNUSED|CLASS_FETCH|CONST|VAR, UNUSED|CACHE_SLOT, N constructor = Z_OBJ_HT_P(result)->get_constructor(Z_OBJ_P(result)); if (constructor == NULL) { - if (UNEXPECTED(EG(exception))) { - HANDLE_EXCEPTION(); - } - /* If there are no arguments, skip over the DO_FCALL opcode. We check if the next * opcode is DO_FCALL in case EXT instructions are used. */ if (EXPECTED(opline->extended_value == 0 && (opline+1)->opcode == ZEND_DO_FCALL)) { ZEND_VM_NEXT_OPCODE_EX(1, 2); } + if (UNEXPECTED(EG(exception))) { + HANDLE_EXCEPTION(); + } + /* Perform a dummy function call */ call = zend_vm_stack_push_call_frame( ZEND_CALL_FUNCTION, (zend_function *) &zend_pass_function, diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index aa1c8e7e175eb..791e4b4e88437 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -11046,16 +11046,16 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_NEW_SPEC_CONST_UNUSED_HANDLER( constructor = Z_OBJ_HT_P(result)->get_constructor(Z_OBJ_P(result)); if (constructor == NULL) { - if (UNEXPECTED(EG(exception))) { - HANDLE_EXCEPTION(); - } - /* If there are no arguments, skip over the DO_FCALL opcode. We check if the next * opcode is DO_FCALL in case EXT instructions are used. */ if (EXPECTED(opline->extended_value == 0 && (opline+1)->opcode == ZEND_DO_FCALL)) { ZEND_VM_NEXT_OPCODE_EX(1, 2); } + if (UNEXPECTED(EG(exception))) { + HANDLE_EXCEPTION(); + } + /* Perform a dummy function call */ call = zend_vm_stack_push_call_frame( ZEND_CALL_FUNCTION, (zend_function *) &zend_pass_function, @@ -30594,16 +30594,16 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_NEW_SPEC_VAR_UNUSED_HANDLER(ZE constructor = Z_OBJ_HT_P(result)->get_constructor(Z_OBJ_P(result)); if (constructor == NULL) { - if (UNEXPECTED(EG(exception))) { - HANDLE_EXCEPTION(); - } - /* If there are no arguments, skip over the DO_FCALL opcode. We check if the next * opcode is DO_FCALL in case EXT instructions are used. */ if (EXPECTED(opline->extended_value == 0 && (opline+1)->opcode == ZEND_DO_FCALL)) { ZEND_VM_NEXT_OPCODE_EX(1, 2); } + if (UNEXPECTED(EG(exception))) { + HANDLE_EXCEPTION(); + } + /* Perform a dummy function call */ call = zend_vm_stack_push_call_frame( ZEND_CALL_FUNCTION, (zend_function *) &zend_pass_function, @@ -38059,16 +38059,16 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_NEW_SPEC_UNUSED_UNUSED_HANDLER constructor = Z_OBJ_HT_P(result)->get_constructor(Z_OBJ_P(result)); if (constructor == NULL) { - if (UNEXPECTED(EG(exception))) { - HANDLE_EXCEPTION(); - } - /* If there are no arguments, skip over the DO_FCALL opcode. We check if the next * opcode is DO_FCALL in case EXT instructions are used. */ if (EXPECTED(opline->extended_value == 0 && (opline+1)->opcode == ZEND_DO_FCALL)) { ZEND_VM_NEXT_OPCODE_EX(1, 2); } + if (UNEXPECTED(EG(exception))) { + HANDLE_EXCEPTION(); + } + /* Perform a dummy function call */ call = zend_vm_stack_push_call_frame( ZEND_CALL_FUNCTION, (zend_function *) &zend_pass_function, From 0a8961a0e26ea37c2821c6726bfc8957d0eaf29b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Taavi=20Eom=C3=A4e?= Date: Mon, 2 Jun 2025 17:37:40 +0300 Subject: [PATCH 007/682] Log a warning when opcache lock file permissions could not be changed Closes GH-18742. --- NEWS | 2 ++ ext/opcache/zend_shared_alloc.c | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index a2ce7e7ad2d1e..8427273c9d9a5 100644 --- a/NEWS +++ b/NEWS @@ -121,6 +121,8 @@ PHP NEWS . Fixed ZTS OPcache build on Cygwin. (cmb) . Added opcache.file_cache_read_only. (Samuel Melrose) . Updated default value of opcache.jit_hot_loop. (Arnaud) + . Log a warning when opcache lock file permissions could not be changed. + (Taavi Eomäe) - Output: . Fixed calculation of aligned buffer size. (cmb) diff --git a/ext/opcache/zend_shared_alloc.c b/ext/opcache/zend_shared_alloc.c index 8516493dd8708..37dbe59f3d55c 100644 --- a/ext/opcache/zend_shared_alloc.c +++ b/ext/opcache/zend_shared_alloc.c @@ -115,7 +115,9 @@ void zend_shared_alloc_create_lock(char *lockfile_path) zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Unable to create opcache lock file in %s: %s (%d)", lockfile_path, strerror(errno), errno); } - fchmod(lock_file, 0666); + if (fchmod(lock_file, 0666) == -1) { + zend_accel_error(ACCEL_LOG_WARNING, "Unable to change opcache lock file permissions in %s: %s (%d)", lockfile_path, strerror(errno), errno); + } val = fcntl(lock_file, F_GETFD, 0); val |= FD_CLOEXEC; From 4c5a6b0e8d8fb379b04a4396259c614fd87c983d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Tue, 3 Jun 2025 20:08:47 +0200 Subject: [PATCH 008/682] tree-wide: Remove stacktraces from tests testing throwing clones (#18748) This is in preparation for the possible future transformation of `clone` into a function call, but also meaningful on its own, since the purpose of the tests is not to test the stack trace generation, but rather that an exception was thrown. It also cleans up some unreachable code in the tests. --- Zend/tests/generators/clone.phpt | 16 +++++++------- ext/dom/tests/modern/token_list/clone.phpt | 13 ++++++------ ext/gd/tests/gdimage_prevent_cloning.phpt | 11 +++++----- .../tests/mysqli_driver_unclonable.phpt | 14 +++++++------ .../tests/mysqli_result_unclonable.phpt | 14 ++++++------- ext/mysqli/tests/mysqli_stmt_unclonable.phpt | 15 +++++++------ ext/mysqli/tests/mysqli_unclonable.phpt | 15 +++++++------ ext/pdo/tests/bug_77849.phpt | 13 ++++++------ ext/pdo/tests/bug_77849_2.phpt | 17 ++++++++------- .../ReflectionClass_CannotClone_basic.phpt | 13 ++++++------ .../ReflectionClass_isCloneable_001.phpt | 14 ++++++------- ext/xml/tests/bug78563.phpt | 16 +++++++------- ext/xmlreader/tests/bug51936.phpt | 21 +++++++++---------- tests/classes/factory_and_singleton_007.phpt | 17 +++++++-------- tests/classes/factory_and_singleton_008.phpt | 17 +++++++-------- 15 files changed, 115 insertions(+), 111 deletions(-) diff --git a/Zend/tests/generators/clone.phpt b/Zend/tests/generators/clone.phpt index 1e8da25136a13..748b826365cc0 100644 --- a/Zend/tests/generators/clone.phpt +++ b/Zend/tests/generators/clone.phpt @@ -7,12 +7,14 @@ function gen() { yield; } -$gen = gen(); -clone $gen; + +try { + $gen = gen(); + clone $gen; +} catch (Throwable $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} ?> ---EXPECTF-- -Fatal error: Uncaught Error: Trying to clone an uncloneable object of class Generator in %s:%d -Stack trace: -#0 {main} - thrown in %s on line %d +--EXPECT-- +Error: Trying to clone an uncloneable object of class Generator diff --git a/ext/dom/tests/modern/token_list/clone.phpt b/ext/dom/tests/modern/token_list/clone.phpt index 039551f2d43d8..e0c71e9fd7910 100644 --- a/ext/dom/tests/modern/token_list/clone.phpt +++ b/ext/dom/tests/modern/token_list/clone.phpt @@ -7,11 +7,12 @@ dom $dom = DOM\XMLDocument::createFromString(''); $element = $dom->documentElement; -clone $element->classList; +try { + clone $element->classList; +} catch (Throwable $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} ?> ---EXPECTF-- -Fatal error: Uncaught Error: Trying to clone an uncloneable object of class Dom\TokenList in %s:%d -Stack trace: -#0 {main} - thrown in %s on line %d +--EXPECT-- +Error: Trying to clone an uncloneable object of class Dom\TokenList diff --git a/ext/gd/tests/gdimage_prevent_cloning.phpt b/ext/gd/tests/gdimage_prevent_cloning.phpt index 426f7d9c48f6c..609e6f99bbfa9 100644 --- a/ext/gd/tests/gdimage_prevent_cloning.phpt +++ b/ext/gd/tests/gdimage_prevent_cloning.phpt @@ -5,12 +5,13 @@ gd --FILE-- getMessage(), PHP_EOL; +} ?> ---EXPECTF-- -Fatal error: Uncaught Error: Trying to clone an uncloneable object of class GdImage in %s:%d -Stack trace: -#0 {main} - thrown in %s on line %d +--EXPECT-- +Error: Trying to clone an uncloneable object of class GdImage diff --git a/ext/mysqli/tests/mysqli_driver_unclonable.phpt b/ext/mysqli/tests/mysqli_driver_unclonable.phpt index 54b97ef36c931..7041a024f67cb 100644 --- a/ext/mysqli/tests/mysqli_driver_unclonable.phpt +++ b/ext/mysqli/tests/mysqli_driver_unclonable.phpt @@ -4,12 +4,14 @@ Trying to clone mysqli_driver object mysqli --FILE-- getMessage(), PHP_EOL; +} + ?> ---EXPECTF-- -Fatal error: Uncaught Error: Trying to clone an uncloneable object of class mysqli_driver in %s:%d -Stack trace: -#0 {main} - thrown in %s on line %d +--EXPECT-- +Error: Trying to clone an uncloneable object of class mysqli_driver diff --git a/ext/mysqli/tests/mysqli_result_unclonable.phpt b/ext/mysqli/tests/mysqli_result_unclonable.phpt index b54c39f7a170f..98770406bb713 100644 --- a/ext/mysqli/tests/mysqli_result_unclonable.phpt +++ b/ext/mysqli/tests/mysqli_result_unclonable.phpt @@ -17,11 +17,11 @@ require_once 'skipifconnectfailure.inc'; if (!($res = mysqli_query($link, "SELECT 'good' AS morning"))) printf("[002] [%d] %s\n", mysqli_errno($link), mysqli_error($link)); - $res_clone = clone $res; - print "done!"; + try { + $res_clone = clone $res; + } catch (Throwable $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; + } ?> ---EXPECTF-- -Fatal error: Uncaught Error: Trying to clone an uncloneable object of class mysqli_result in %s:%d -Stack trace: -#0 {main} - thrown in %s on line %d +--EXPECT-- +Error: Trying to clone an uncloneable object of class mysqli_result diff --git a/ext/mysqli/tests/mysqli_stmt_unclonable.phpt b/ext/mysqli/tests/mysqli_stmt_unclonable.phpt index 29658bb25c669..c1e01d37e36c0 100644 --- a/ext/mysqli/tests/mysqli_stmt_unclonable.phpt +++ b/ext/mysqli/tests/mysqli_stmt_unclonable.phpt @@ -17,12 +17,11 @@ require_once 'skipifconnectfailure.inc'; if (!$stmt = mysqli_stmt_init($link)) printf("[002] [%d] %s\n", mysqli_errno($link), mysqli_error($link)); - /* no, still bails out */ - $stmt_clone = clone $stmt; - print "done!"; + try { + $stmt_clone = clone $stmt; + } catch (Throwable $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; + } ?> ---EXPECTF-- -Fatal error: Uncaught Error: Trying to clone an uncloneable object of class mysqli_stmt in %s:%d -Stack trace: -#0 {main} - thrown in %s on line %d +--EXPECT-- +Error: Trying to clone an uncloneable object of class mysqli_stmt diff --git a/ext/mysqli/tests/mysqli_unclonable.phpt b/ext/mysqli/tests/mysqli_unclonable.phpt index 0772854ed96a1..6e42171606c24 100644 --- a/ext/mysqli/tests/mysqli_unclonable.phpt +++ b/ext/mysqli/tests/mysqli_unclonable.phpt @@ -14,13 +14,12 @@ require_once 'skipifconnectfailure.inc'; printf("[001] Cannot connect to the server using host=%s, user=%s, passwd=***, dbname=%s, port=%s, socket=%s\n", $host, $user, $db, $port, $socket); - $link_clone = clone $link; - mysqli_close($link); + try { + $link_clone = clone $link; + } catch (Throwable $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; + } - print "done!"; ?> ---EXPECTF-- -Fatal error: Uncaught Error: Trying to clone an uncloneable object of class mysqli in %s:%d -Stack trace: -#0 {main} - thrown in %s on line %d +--EXPECT-- +Error: Trying to clone an uncloneable object of class mysqli diff --git a/ext/pdo/tests/bug_77849.phpt b/ext/pdo/tests/bug_77849.phpt index 6fbf56e869b70..bbb0f3e8595e2 100644 --- a/ext/pdo/tests/bug_77849.phpt +++ b/ext/pdo/tests/bug_77849.phpt @@ -15,10 +15,11 @@ if (getenv('REDIR_TEST_DIR') === false) putenv('REDIR_TEST_DIR='.dirname(__FILE_ require_once getenv('REDIR_TEST_DIR') . 'pdo_test.inc'; $db = PDOTest::factory(); -$db2 = clone $db; +try { + $db2 = clone $db; +} catch (Throwable $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} ?> ---EXPECTF-- -Fatal error: Uncaught Error: Trying to clone an uncloneable object of class PDO in %s -Stack trace: -#0 {main} - thrown in %s on line %d +--EXPECT-- +Error: Trying to clone an uncloneable object of class PDO diff --git a/ext/pdo/tests/bug_77849_2.phpt b/ext/pdo/tests/bug_77849_2.phpt index 6453a79312e2a..3e481eedb1e1a 100644 --- a/ext/pdo/tests/bug_77849_2.phpt +++ b/ext/pdo/tests/bug_77849_2.phpt @@ -4,13 +4,14 @@ PDO Common: Bug #77849 (inconsistent state of cloned statament object) pdo --FILE-- ---EXPECTF-- -Fatal error: Uncaught Error: Trying to clone an uncloneable object of class PDOStatement in %s:4 -Stack trace: -#0 {main} - thrown in %s on line 4 +try { + $stmt = new PDOStatement(); + clone $stmt; +} catch (Throwable $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} +?> +--EXPECT-- +Error: Trying to clone an uncloneable object of class PDOStatement diff --git a/ext/reflection/tests/ReflectionClass_CannotClone_basic.phpt b/ext/reflection/tests/ReflectionClass_CannotClone_basic.phpt index 58ce9c65dce0f..64d884d6ae02c 100644 --- a/ext/reflection/tests/ReflectionClass_CannotClone_basic.phpt +++ b/ext/reflection/tests/ReflectionClass_CannotClone_basic.phpt @@ -6,10 +6,11 @@ TestFest PHP|Tek --FILE-- getMessage(), PHP_EOL; +} ?> ---EXPECTF-- -Fatal error: Uncaught Error: Trying to clone an uncloneable object of class ReflectionClass in %s:%d -Stack trace: -#0 {main} - thrown in %s on line %d +--EXPECT-- +Error: Trying to clone an uncloneable object of class ReflectionClass diff --git a/ext/reflection/tests/ReflectionClass_isCloneable_001.phpt b/ext/reflection/tests/ReflectionClass_isCloneable_001.phpt index 0b3701f1bb610..bde3a60a1e7f9 100644 --- a/ext/reflection/tests/ReflectionClass_isCloneable_001.phpt +++ b/ext/reflection/tests/ReflectionClass_isCloneable_001.phpt @@ -49,10 +49,14 @@ $obj = new ReflectionClass('xmlwriter'); var_dump($obj->isCloneable()); $obj = new ReflectionObject(new XMLWriter); var_dump($obj->isCloneable()); -$h = clone new xmlwriter; +try { + $h = clone new xmlwriter; +} catch (Throwable $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} ?> ---EXPECTF-- +--EXPECT-- User class bool(true) bool(true) @@ -68,8 +72,4 @@ bool(true) Internal class - XMLWriter bool(false) bool(false) - -Fatal error: Uncaught Error: Trying to clone an uncloneable object of class XMLWriter in %s:%d -Stack trace: -#0 {main} - thrown in %s on line %d +Error: Trying to clone an uncloneable object of class XMLWriter diff --git a/ext/xml/tests/bug78563.phpt b/ext/xml/tests/bug78563.phpt index 4e1bb5d63bf57..dc7d5fe02dc26 100644 --- a/ext/xml/tests/bug78563.phpt +++ b/ext/xml/tests/bug78563.phpt @@ -5,13 +5,13 @@ xml --FILE-- getMessage(), PHP_EOL; +} ?> -===DONE=== ---EXPECTF-- -Fatal error: Uncaught Error: Trying to clone an uncloneable object of class XMLParser in %s:%d -Stack trace: -#0 {main} - thrown in %s on line %d +--EXPECT-- +Error: Trying to clone an uncloneable object of class XMLParser diff --git a/ext/xmlreader/tests/bug51936.phpt b/ext/xmlreader/tests/bug51936.phpt index 00a8134d1a45f..6014c5550a629 100644 --- a/ext/xmlreader/tests/bug51936.phpt +++ b/ext/xmlreader/tests/bug51936.phpt @@ -4,20 +4,19 @@ Bug #51936 (Crash with clone XMLReader) xmlreader --FILE-- xml(""); $xmlreader->next(); -$xmlreader2 = clone $xmlreader; -$xmlreader2->next(); -?> -Done ---EXPECTF-- -Test -Fatal error: Uncaught Error: Trying to clone an uncloneable object of class XMLReader in %s:%d -Stack trace: -#0 {main} - thrown in %s on line %d +try { + $xmlreader2 = clone $xmlreader; + $xmlreader2->next(); +} catch (Throwable $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +Error: Trying to clone an uncloneable object of class XMLReader diff --git a/tests/classes/factory_and_singleton_007.phpt b/tests/classes/factory_and_singleton_007.phpt index 2c35090eed5e0..fc232bdb8655a 100644 --- a/tests/classes/factory_and_singleton_007.phpt +++ b/tests/classes/factory_and_singleton_007.phpt @@ -8,14 +8,13 @@ class test { } } -$obj = new test; -$clone = clone $obj; -$obj = NULL; +try { + $obj = new test; + $clone = clone $obj; +} catch (Throwable $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} -echo "Done\n"; ?> ---EXPECTF-- -Fatal error: Uncaught Error: Call to protected test::__clone() from global scope in %s:%d -Stack trace: -#0 {main} - thrown in %s on line %d +--EXPECT-- +Error: Call to protected test::__clone() from global scope diff --git a/tests/classes/factory_and_singleton_008.phpt b/tests/classes/factory_and_singleton_008.phpt index 2b2c0721c75e5..672c083270730 100644 --- a/tests/classes/factory_and_singleton_008.phpt +++ b/tests/classes/factory_and_singleton_008.phpt @@ -8,14 +8,13 @@ class test { } } -$obj = new test; -$clone = clone $obj; -$obj = NULL; +try { + $obj = new test; + $clone = clone $obj; +} catch (Throwable $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} -echo "Done\n"; ?> ---EXPECTF-- -Fatal error: Uncaught Error: Call to private test::__clone() from global scope in %s:%d -Stack trace: -#0 {main} - thrown in %s on line %d +--EXPECT-- +Error: Call to private test::__clone() from global scope From 08a95798833963bfb1a33bbacd19045b84d491b9 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Tue, 3 Jun 2025 21:19:15 +0200 Subject: [PATCH 009/682] Fix memory leak of X509_STORE in php_openssl_setup_verify() on failure Closes GH-18750. --- NEWS | 4 ++++ ext/openssl/openssl.c | 1 + ext/openssl/tests/memory_leak_x509_store.phpt | 22 +++++++++++++++++++ 3 files changed, 27 insertions(+) create mode 100644 ext/openssl/tests/memory_leak_x509_store.phpt diff --git a/NEWS b/NEWS index 40806102246ab..d6f38f9adceb9 100644 --- a/NEWS +++ b/NEWS @@ -19,6 +19,10 @@ PHP NEWS . Fix memory leak in intl_datetime_decompose() on failure. (nielsdos) . Fix memory leak in locale lookup on failure. (nielsdos) +- OpenSSL: + . Fix memory leak of X509_STORE in php_openssl_setup_verify() on failure. + (nielsdos) + - Phar: . Add missing filter cleanups on phar failure. (nielsdos) . Fixed bug GH-18642 (Signed integer overflow in ext/phar fseek). (nielsdos) diff --git a/ext/openssl/openssl.c b/ext/openssl/openssl.c index c978859b7ec00..718f946ad176d 100644 --- a/ext/openssl/openssl.c +++ b/ext/openssl/openssl.c @@ -2421,6 +2421,7 @@ static X509_STORE *php_openssl_setup_verify(zval *calist, uint32_t arg_num) ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(calist), item) { zend_string *str = zval_try_get_string(item); if (UNEXPECTED(!str)) { + X509_STORE_free(store); return NULL; } diff --git a/ext/openssl/tests/memory_leak_x509_store.phpt b/ext/openssl/tests/memory_leak_x509_store.phpt new file mode 100644 index 0000000000000..bc9b113602a33 --- /dev/null +++ b/ext/openssl/tests/memory_leak_x509_store.phpt @@ -0,0 +1,22 @@ +--TEST-- +Memory leak of X509_STORE in php_openssl_setup_verify() on failure +--EXTENSIONS-- +openssl +--FILE-- +getMessage(), "\n"; +} + +?> +--EXPECT-- +stop From 6eae466e32635a0df6258e3694c465f1c6134fbd Mon Sep 17 00:00:00 2001 From: NickSdot <32384907+NickSdot@users.noreply.github.com> Date: Wed, 4 Jun 2025 12:36:03 +0800 Subject: [PATCH 010/682] Zend/tests/property_hooks: fix typo in file name (#18754) [skip ci] --- ...delegated_read_wirte.phpt => backed_delegated_read_write.phpt} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Zend/tests/property_hooks/{backed_delegated_read_wirte.phpt => backed_delegated_read_write.phpt} (100%) diff --git a/Zend/tests/property_hooks/backed_delegated_read_wirte.phpt b/Zend/tests/property_hooks/backed_delegated_read_write.phpt similarity index 100% rename from Zend/tests/property_hooks/backed_delegated_read_wirte.phpt rename to Zend/tests/property_hooks/backed_delegated_read_write.phpt From 3a14ce19a5363f51300315e786ec32258bab6449 Mon Sep 17 00:00:00 2001 From: Marc Bennewitz Date: Wed, 4 Jun 2025 07:42:05 +0200 Subject: [PATCH 011/682] Fix stubs of DateTimeZone->getTransitions (#17992) The default value of `timestamp_end` is INT32_MAX and not ZEND_LONG_MAX --- .../internal_declaration_error_const.phpt | 2 +- ext/date/php_date.stub.php | 4 ++-- ext/date/php_date_arginfo.h | 6 +++--- ...ctionParameter_getDefaultValueConstantName_Internal.phpt | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Zend/tests/parameter_default_values/internal_declaration_error_const.phpt b/Zend/tests/parameter_default_values/internal_declaration_error_const.phpt index 5b0a9bf05f0f0..407bad6077057 100644 --- a/Zend/tests/parameter_default_values/internal_declaration_error_const.phpt +++ b/Zend/tests/parameter_default_values/internal_declaration_error_const.phpt @@ -10,4 +10,4 @@ class MyDateTimeZone extends DateTimeZone } ?> --EXPECTF-- -Fatal error: Declaration of MyDateTimeZone::getTransitions(): array|false must be compatible with DateTimeZone::getTransitions(int $timestampBegin = PHP_INT_MIN, int $timestampEnd = PHP_INT_MAX): array|false in %s on line %d +Fatal error: Declaration of MyDateTimeZone::getTransitions(): array|false must be compatible with DateTimeZone::getTransitions(int $timestampBegin = PHP_INT_MIN, int $timestampEnd = 2147483647): array|false in %s on line %d diff --git a/ext/date/php_date.stub.php b/ext/date/php_date.stub.php index f375c60ff0a4c..7f60be40b1bc8 100644 --- a/ext/date/php_date.stub.php +++ b/ext/date/php_date.stub.php @@ -227,7 +227,7 @@ function timezone_offset_get(DateTimeZone $object, DateTimeInterface $datetime): * @refcount 1 */ function timezone_transitions_get( - DateTimeZone $object, int $timestampBegin = PHP_INT_MIN, int $timestampEnd = PHP_INT_MAX): array|false {} + DateTimeZone $object, int $timestampBegin = PHP_INT_MIN, int $timestampEnd = 2147483647): array|false {} /** * @return array|false @@ -615,7 +615,7 @@ public function getOffset(DateTimeInterface $datetime): int {} * @tentative-return-type * @alias timezone_transitions_get */ - public function getTransitions(int $timestampBegin = PHP_INT_MIN, int $timestampEnd = PHP_INT_MAX): array|false {} + public function getTransitions(int $timestampBegin = PHP_INT_MIN, int $timestampEnd = 2147483647): array|false {} /** * @return array|false diff --git a/ext/date/php_date_arginfo.h b/ext/date/php_date_arginfo.h index 82e9f0f1718ae..2a0fd6e5ac009 100644 --- a/ext/date/php_date_arginfo.h +++ b/ext/date/php_date_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 093743b4fe7a698d1262cc1a81b60a85064fdccb */ + * Stub hash: 4e61617ca7c877aa3811d674d47850f23157074b */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_strtotime, 0, 1, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, datetime, IS_STRING, 0) @@ -175,7 +175,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_timezone_transitions_get, 0, 1, MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_ARG_OBJ_INFO(0, object, DateTimeZone, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timestampBegin, IS_LONG, 0, "PHP_INT_MIN") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timestampEnd, IS_LONG, 0, "PHP_INT_MAX") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timestampEnd, IS_LONG, 0, "2147483647") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_timezone_location_get, 0, 1, MAY_BE_ARRAY|MAY_BE_FALSE) @@ -436,7 +436,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_MASK_EX(arginfo_class_DateTimeZone_getTransitions, 0, 0, MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timestampBegin, IS_LONG, 0, "PHP_INT_MIN") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timestampEnd, IS_LONG, 0, "PHP_INT_MAX") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timestampEnd, IS_LONG, 0, "2147483647") ZEND_END_ARG_INFO() #define arginfo_class_DateTimeZone_getLocation arginfo_class_DateTime_getLastErrors diff --git a/ext/reflection/tests/internal_parameter_default_value/ReflectionParameter_getDefaultValueConstantName_Internal.phpt b/ext/reflection/tests/internal_parameter_default_value/ReflectionParameter_getDefaultValueConstantName_Internal.phpt index b013c9db66725..b6473b0a3abb2 100644 --- a/ext/reflection/tests/internal_parameter_default_value/ReflectionParameter_getDefaultValueConstantName_Internal.phpt +++ b/ext/reflection/tests/internal_parameter_default_value/ReflectionParameter_getDefaultValueConstantName_Internal.phpt @@ -47,7 +47,7 @@ NULL NULL ---------- string(11) "PHP_INT_MIN" -string(11) "PHP_INT_MAX" +NULL ---------- string(17) "DateTimeZone::ALL" NULL From 6820a4de2333822892efee3e0c910108474cf331 Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Mon, 19 May 2025 11:01:10 +0200 Subject: [PATCH 012/682] Fix GH-18579: --libdir option default value Using PHP_LIBDIR for lib64 case --- UPGRADING.INTERNALS | 4 ++++ configure.ac | 8 ++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS index 82bd53db51872..574d5f0827ff7 100644 --- a/UPGRADING.INTERNALS +++ b/UPGRADING.INTERNALS @@ -46,6 +46,10 @@ PHP 8.5 INTERNALS UPGRADE NOTES without duplicate build rules. It is up to the SAPI maintainers to ensure that appropriate build rules are created. +- Linux build system changes + . libdir is properly set when --libdir (ex: /usr/lib64) and --with-libdir (ex lib64) + configure options are used to ${libdir}/php (ex: /usr/lib64/php) + ======================== 3. Module changes ======================== diff --git a/configure.ac b/configure.ac index 61d1c350a82be..e4bd8162a2ebc 100644 --- a/configure.ac +++ b/configure.ac @@ -1324,11 +1324,15 @@ AS_VAR_IF([program_prefix], [NONE], [program_prefix=]) AS_VAR_IF([program_suffix], [NONE], [program_suffix=]) orig_libdir=$libdir + +dnl First for unexpanded (default), second for expanded (from options) AS_CASE([$libdir], - ['${exec_prefix}/lib'], [libdir=$libdir/php]) + ['${exec_prefix}/lib'], [libdir=$libdir/php], + [${exec_prefix}/${PHP_LIBDIR}], [libdir=$libdir/php]) AS_CASE([$(eval echo $datadir)], - ['${prefix}/share'], [datadir=$datadir/php]) + ['${prefix}/share'], [datadir=$datadir/php], + [${prefix}/share], [datadir=$datadir/php]) phptempdir=$(pwd)/libs From 690cde6903c4f16cbb598cad0e74b879b4b1cc6a Mon Sep 17 00:00:00 2001 From: Ayesh Karunaratne Date: Sun, 1 Jun 2025 12:08:31 +0530 Subject: [PATCH 013/682] UPGRADING: minor typo fixes Closes GH-18726 --- UPGRADING | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/UPGRADING b/UPGRADING index 46284f16ce3f4..8f8b7e7685e2a 100644 --- a/UPGRADING +++ b/UPGRADING @@ -42,8 +42,8 @@ PHP 8.5 UPGRADE NOTES have run and the output handlers have been cleaned up. This is a consequence of fixing GH-18033. . Traits are now bound before the parent class. This is a subtle behavioral - change, but should closer match user expectations, demonstrated by GH-15753 - and GH-16198. + change, but should more closely match user expectations, demonstrated by + GH-15753 and GH-16198. - FileInfo: . finfo_file() and finfo::file() now throws a ValueError instead of a @@ -91,7 +91,7 @@ PHP 8.5 UPGRADE NOTES argument when fetching into an object, will now throw an Error. . The value of the constants PDO::FETCH_GROUP, PDO::FETCH_UNIQUE, PDO::FETCH_CLASSTYPE, PDO::FETCH_PROPS_LATE, and PDO::FETCH_SERIALIZE - has changed. + have changed. . A ValueError is now thrown if PDO::FETCH_PROPS_LATE is used with a fetch mode different than PDO::FETCH_CLASS, consistent with other fetch flags. . A ValueError is now thrown if PDO::FETCH_INTO is used as a fetch mode in @@ -158,7 +158,7 @@ PHP 8.5 UPGRADE NOTES CURLINFO_USED_PROXY gets zero set if no proxy was used in the previous transfer or a non-zero value if a proxy was used. CURLINFO_HTTPAUTH_USED and CURLINFO_PROXYAUTH_USED get bitmasks - indicating the http and proxy authentication methods that were + indicating the HTTP and proxy authentication methods that were used in the previous request. See CURLAUTH_* constants for possible values. . Added CURLOPT_INFILESIZE_LARGE Curl option, which is a safe @@ -168,10 +168,10 @@ PHP 8.5 UPGRADE NOTES accepts the largest integer value the system can handle. . Added CURLFOLLOW_OBEYCODE, CURLFOLLOW_FIRSTONLY and CURLFOLLOW_ALL values for CURLOPT_FOLLOWLOCATION curl_easy_setopt option. - CURLFOLLOW_OBEYCODE to follow more strictly in regard of redirect + CURLFOLLOW_OBEYCODE to follow more strictly in regard to redirect if they are allowed. CURLFOLLOW_FIRSTONLY to follow only the - first redirect thus if there any follow up redirect, it won't go - any further. CURLFOLLOW_ALL is equivalent to set CURLOPT_FOLLOWLOCATION + first redirect thus if there is any follow up redirect, it won't go + any further. CURLFOLLOW_ALL is equivalent to setting CURLOPT_FOLLOWLOCATION to true. - DOM: @@ -187,7 +187,7 @@ PHP 8.5 UPGRADE NOTES number formats. . Added Locale::addLikelySubtags and Locale::minimizeSubtags to handle likely tags on a given locale. - . Added IntlListFormatter class to format, order, punctuates + . Added IntlListFormatter class to format, order, and punctuate a list of items with a given locale, IntlListFormatter::TYPE_AND, IntlListFormatter::TYPE_OR, IntlListFormatter::TYPE_UNITS operands and IntlListFormatter::WIDTH_WIDE, IntlListFormatter::WIDTH_SHORT and @@ -247,7 +247,7 @@ PHP 8.5 UPGRADE NOTES was actually never possible. - LDAP: - . ldap_get_option() now accept a NULL connection, as ldap_set_option(), + . ldap_get_option() now accepts a NULL connection, as ldap_set_option(), to allow retrieval of global options. - libxml: @@ -260,7 +260,7 @@ PHP 8.5 UPGRADE NOTES . PDO::pgsqlCopyFromArray also supports inputs as Iterable. . Pdo\Pgsql::setAttribute and Pdo\Pgsql::prepare supports PDO::ATTR_PREFETCH sets to 0 which set to lazy fetch mode. - In this mode, statements cannot be run parallely. + In this mode, statements cannot be run in parallel. - PDO_SQLITE: . SQLite PDO::quote() will now throw an exception or emit a warning, @@ -287,7 +287,7 @@ PHP 8.5 UPGRADE NOTES are enum cases rather than normal class constants. - Session: - . session_start is stricter in regard of the option argument. + . session_start is stricter in regard to the option argument. It throws a ValueError if the whole is not a hashmap or a TypeError if read_on_close value is not a valid type compatible with int. @@ -304,7 +304,7 @@ PHP 8.5 UPGRADE NOTES ValueError if the port is lower than 0 or greater than 65535, also if any of the hints array entry is indexes numerically. . socket_addrinfo_lookup throws a TypeError if any of the hints - values cannot be cast to a int and can throw a ValueError if + values cannot be cast to int and can throw a ValueError if any of these values overflow. . socket_set_option with MCAST_LEAVE_GROUP/MCAST_LEAVE_SOURCE_GROUP options will throw an exception if the value isn't a valid object From f46f42b2b2de3668d6e2b8d8efc32dd9285b9026 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Wed, 4 Jun 2025 17:46:57 +0200 Subject: [PATCH 014/682] Implement request #55503: Extend __getTypes to support enumerations (#18704) Co-authored-by: datibbaw --- NEWS | 2 ++ UPGRADING | 3 +++ ext/soap/soap.c | 16 ++++++++++++++++ ext/soap/tests/bugs/bug42359.phpt | 4 ++-- ext/soap/tests/req55503.phpt | 16 ++++++++++++++++ ext/soap/tests/req55503.wsdl | 30 ++++++++++++++++++++++++++++++ 6 files changed, 69 insertions(+), 2 deletions(-) create mode 100644 ext/soap/tests/req55503.phpt create mode 100644 ext/soap/tests/req55503.wsdl diff --git a/NEWS b/NEWS index 8427273c9d9a5..24d617d0c7933 100644 --- a/NEWS +++ b/NEWS @@ -190,6 +190,8 @@ PHP NEWS . Fix namespace handling of WSDL and XML schema in SOAP, fixing at least GH-16320 and bug #68576. (nielsdos) . Fixed bug #70951 (Segmentation fault on invalid WSDL cache). (nielsdos) + . Implement request #55503 (Extend __getTypes to support enumerations). + (nielsdos, datibbaw) - Sockets: . Added IPPROTO_ICMP/IPPROTO_ICMPV6 to create raw socket for ICMP usage. diff --git a/UPGRADING b/UPGRADING index 8f8b7e7685e2a..ef6739c3da933 100644 --- a/UPGRADING +++ b/UPGRADING @@ -194,6 +194,9 @@ PHP 8.5 UPGRADE NOTES IntlListFormatter::WIDTH_NARROW widths. It is supported from icu 67. +- SOAP: + . Enumeration cases are now dumped in __getTypes(). + - XSL: . The $namespace argument of XSLTProcessor::getParameter(), XSLTProcessor::setParameter() and XSLTProcessor::removeParameter() diff --git a/ext/soap/soap.c b/ext/soap/soap.c index 094730b88f412..e0577ac648cae 100644 --- a/ext/soap/soap.c +++ b/ext/soap/soap.c @@ -4404,6 +4404,22 @@ static void type_to_string(sdlTypePtr type, smart_str *buf, int level) /* {{{ */ smart_str_appendl(buf, "anyType ", sizeof("anyType ")-1); } smart_str_appendl(buf, type->name, strlen(type->name)); + + if (type->restrictions && type->restrictions->enumeration) { + zend_string *key; + bool first = true; + + smart_str_appends(buf, " {"); + ZEND_HASH_MAP_FOREACH_STR_KEY(type->restrictions->enumeration, key) { + if (first) { + first = false; + } else { + smart_str_appends(buf, ", "); + } + smart_str_append(buf, key); + } ZEND_HASH_FOREACH_END(); + smart_str_appendc(buf, '}'); + } break; case XSD_TYPEKIND_LIST: smart_str_appendl(buf, "list ", 5); diff --git a/ext/soap/tests/bugs/bug42359.phpt b/ext/soap/tests/bugs/bug42359.phpt index 0cb4f75c69be4..f717561e7caeb 100644 --- a/ext/soap/tests/bugs/bug42359.phpt +++ b/ext/soap/tests/bugs/bug42359.phpt @@ -13,7 +13,7 @@ print_r($soap->__getTypes()); Array ( [0] => list listItem {anonymous1} - [1] => string anonymous1 - [2] => string enumItem + [1] => string anonymous1 {test1, test2} + [2] => string enumItem {test1, test2} [3] => list listItem2 {enumItem} ) diff --git a/ext/soap/tests/req55503.phpt b/ext/soap/tests/req55503.phpt new file mode 100644 index 0000000000000..ac3c8627048c1 --- /dev/null +++ b/ext/soap/tests/req55503.phpt @@ -0,0 +1,16 @@ +--TEST-- +Request #55503 (Extend __getTypes to support enumerations) +--EXTENSIONS-- +soap +--INI-- +soap.wsdl_cache_enabled=0 +--FILE-- +__getTypes()); +?> +--EXPECT-- +array(1) { + [0]=> + string(102) "anyType PersonaMemberType {NEW, LIMITED, FREE, PAID_ACTIVE, TRIAL_ACTIVE, PAID_EXPIRED, TRIAL_EXPIRED}" +} diff --git a/ext/soap/tests/req55503.wsdl b/ext/soap/tests/req55503.wsdl new file mode 100644 index 0000000000000..91ac62c8bc001 --- /dev/null +++ b/ext/soap/tests/req55503.wsdl @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 2f5ef4d2b729227f5847dd36faba94ee430cd096 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Wed, 4 Jun 2025 17:46:00 +0100 Subject: [PATCH 015/682] Use custom OpenSSL libctx in md and cipher handling code (#18516) --- ext/openssl/openssl.c | 25 ++++-- ext/openssl/openssl_backend_common.c | 124 ++++++--------------------- ext/openssl/openssl_backend_v1.c | 108 +++++++++++++++++++++++ ext/openssl/openssl_backend_v3.c | 85 +++++++++++++++++- ext/openssl/php_openssl_backend.h | 24 ++++-- 5 files changed, 252 insertions(+), 114 deletions(-) diff --git a/ext/openssl/openssl.c b/ext/openssl/openssl.c index aab240dc8506a..6518a719314fd 100644 --- a/ext/openssl/openssl.c +++ b/ext/openssl/openssl.c @@ -583,7 +583,7 @@ PHP_FUNCTION(openssl_spki_new) zval *zpkey = NULL; EVP_PKEY *pkey = NULL; NETSCAPE_SPKI *spki=NULL; - const EVP_MD *mdtype; + const EVP_MD *mdtype = NULL; if (zend_parse_parameters(ZEND_NUM_ARGS(), "Os|l", &zpkey, php_openssl_pkey_ce, &challenge, &challenge_len, &algo) == FAILURE) { RETURN_THROWS(); @@ -647,6 +647,7 @@ PHP_FUNCTION(openssl_spki_new) goto cleanup; cleanup: + php_openssl_release_evp_md(mdtype); EVP_PKEY_free(pkey); if (spki != NULL) { NETSCAPE_SPKI_free(spki); @@ -1821,7 +1822,6 @@ PHP_FUNCTION(openssl_csr_sign) X509_free(new_cert); } - PHP_SSL_REQ_DISPOSE(&req); EVP_PKEY_free(priv_key); EVP_PKEY_free(key); if (csr_str) { @@ -1830,6 +1830,7 @@ PHP_FUNCTION(openssl_csr_sign) if (cert_str && cert && cert != new_cert) { X509_free(cert); } + PHP_SSL_REQ_DISPOSE(&req); } /* }}} */ @@ -2104,7 +2105,8 @@ PHP_FUNCTION(openssl_pkey_export_to_file) } if (!php_openssl_check_path(filename, filename_len, file_path, 2)) { - goto clean_exit_key; + EVP_PKEY_free(key); + return; } PHP_SSL_REQ_INIT(&req); @@ -2139,10 +2141,9 @@ PHP_FUNCTION(openssl_pkey_export_to_file) } clean_exit: - PHP_SSL_REQ_DISPOSE(&req); BIO_free(bio_out); -clean_exit_key: EVP_PKEY_free(key); + PHP_SSL_REQ_DISPOSE(&req); } /* }}} */ @@ -2204,9 +2205,9 @@ PHP_FUNCTION(openssl_pkey_export) php_openssl_store_errors(); } } - PHP_SSL_REQ_DISPOSE(&req); EVP_PKEY_free(key); BIO_free(bio_out); + PHP_SSL_REQ_DISPOSE(&req); } /* }}} */ @@ -2686,6 +2687,7 @@ PHP_FUNCTION(openssl_pkcs7_encrypt) if (recipcerts) { sk_X509_pop_free(recipcerts, X509_free); } + php_openssl_release_evp_cipher(cipher); } /* }}} */ @@ -3334,6 +3336,7 @@ PHP_FUNCTION(openssl_cms_encrypt) if (recipcerts) { sk_X509_pop_free(recipcerts, X509_free); } + php_openssl_release_evp_cipher(cipher); } /* }}} */ @@ -3969,7 +3972,7 @@ PHP_FUNCTION(openssl_sign) } if (method_str) { - mdtype = EVP_get_digestbyname(ZSTR_VAL(method_str)); + mdtype = php_openssl_get_evp_md_by_name(ZSTR_VAL(method_str)); } else { mdtype = php_openssl_get_evp_md_from_algo(method_long); } @@ -3996,6 +3999,7 @@ PHP_FUNCTION(openssl_sign) RETVAL_FALSE; } EVP_MD_CTX_destroy(md_ctx); + php_openssl_release_evp_md(mdtype); EVP_PKEY_free(pkey); } /* }}} */ @@ -4027,7 +4031,7 @@ PHP_FUNCTION(openssl_verify) PHP_OPENSSL_CHECK_SIZE_T_TO_UINT(signature_len, signature, 2); if (method_str) { - mdtype = EVP_get_digestbyname(ZSTR_VAL(method_str)); + mdtype = php_openssl_get_evp_md_by_name(ZSTR_VAL(method_str)); } else { mdtype = php_openssl_get_evp_md_from_algo(method_long); } @@ -4041,6 +4045,7 @@ PHP_FUNCTION(openssl_verify) if (!EG(exception)) { php_error_docref(NULL, E_WARNING, "Supplied key param cannot be coerced into a public key"); } + php_openssl_release_evp_md(mdtype); RETURN_FALSE; } @@ -4051,6 +4056,7 @@ PHP_FUNCTION(openssl_verify) php_openssl_store_errors(); } EVP_MD_CTX_destroy(md_ctx); + php_openssl_release_evp_md(mdtype); EVP_PKEY_free(pkey); RETURN_LONG(err); } @@ -4323,7 +4329,7 @@ PHP_FUNCTION(openssl_digest) if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss|b", &data, &data_len, &method, &method_len, &raw_output) == FAILURE) { RETURN_THROWS(); } - mdtype = EVP_get_digestbyname(method); + mdtype = php_openssl_get_evp_md_by_name(method); if (!mdtype) { php_error_docref(NULL, E_WARNING, "Unknown digest algorithm"); RETURN_FALSE; @@ -4356,6 +4362,7 @@ PHP_FUNCTION(openssl_digest) } EVP_MD_CTX_destroy(md_ctx); + php_openssl_release_evp_md(mdtype); } /* }}} */ diff --git a/ext/openssl/openssl_backend_common.c b/ext/openssl/openssl_backend_common.c index 02210cce8b1cb..42b70c72a9cd0 100644 --- a/ext/openssl/openssl_backend_common.c +++ b/ext/openssl/openssl_backend_common.c @@ -369,11 +369,11 @@ int php_openssl_parse_config(struct php_x509_request * req, zval * optional_args if (strcmp(req->digest_name, "null") == 0) { req->digest = req->md_alg = EVP_md_null(); } else { - req->digest = req->md_alg = EVP_get_digestbyname(req->digest_name); + req->digest = req->md_alg = php_openssl_get_evp_md_by_name(req->digest_name); } } if (req->md_alg == NULL) { - req->md_alg = req->digest = EVP_sha1(); + req->md_alg = req->digest = php_openssl_get_evp_md_by_name("sha1"); php_openssl_store_errors(); } @@ -417,6 +417,10 @@ void php_openssl_dispose_config(struct php_x509_request * req) NCONF_free(req->req_config); req->req_config = NULL; } + if (req->md_alg != NULL && req->md_alg != EVP_md_null()) { + php_openssl_release_evp_md(req->md_alg); + } + php_openssl_release_evp_cipher(req->priv_key_encrypt_cipher); } zend_result php_openssl_load_rand_file(const char * file, int *egdsocket, int *seeded) @@ -469,92 +473,6 @@ zend_result php_openssl_write_rand_file(const char * file, int egdsocket, int se return SUCCESS; } -EVP_MD * php_openssl_get_evp_md_from_algo(zend_long algo) { - EVP_MD *mdtype; - - switch (algo) { - case OPENSSL_ALGO_SHA1: - mdtype = (EVP_MD *) EVP_sha1(); - break; - case OPENSSL_ALGO_MD5: - mdtype = (EVP_MD *) EVP_md5(); - break; -#ifndef OPENSSL_NO_MD4 - case OPENSSL_ALGO_MD4: - mdtype = (EVP_MD *) EVP_md4(); - break; -#endif -#ifndef OPENSSL_NO_MD2 - case OPENSSL_ALGO_MD2: - mdtype = (EVP_MD *) EVP_md2(); - break; -#endif - case OPENSSL_ALGO_SHA224: - mdtype = (EVP_MD *) EVP_sha224(); - break; - case OPENSSL_ALGO_SHA256: - mdtype = (EVP_MD *) EVP_sha256(); - break; - case OPENSSL_ALGO_SHA384: - mdtype = (EVP_MD *) EVP_sha384(); - break; - case OPENSSL_ALGO_SHA512: - mdtype = (EVP_MD *) EVP_sha512(); - break; -#ifndef OPENSSL_NO_RMD160 - case OPENSSL_ALGO_RMD160: - mdtype = (EVP_MD *) EVP_ripemd160(); - break; -#endif - default: - return NULL; - break; - } - return mdtype; -} - -const EVP_CIPHER * php_openssl_get_evp_cipher_from_algo(zend_long algo) { - switch (algo) { -#ifndef OPENSSL_NO_RC2 - case PHP_OPENSSL_CIPHER_RC2_40: - return EVP_rc2_40_cbc(); - break; - case PHP_OPENSSL_CIPHER_RC2_64: - return EVP_rc2_64_cbc(); - break; - case PHP_OPENSSL_CIPHER_RC2_128: - return EVP_rc2_cbc(); - break; -#endif - -#ifndef OPENSSL_NO_DES - case PHP_OPENSSL_CIPHER_DES: - return EVP_des_cbc(); - break; - case PHP_OPENSSL_CIPHER_3DES: - return EVP_des_ede3_cbc(); - break; -#endif - -#ifndef OPENSSL_NO_AES - case PHP_OPENSSL_CIPHER_AES_128_CBC: - return EVP_aes_128_cbc(); - break; - case PHP_OPENSSL_CIPHER_AES_192_CBC: - return EVP_aes_192_cbc(); - break; - case PHP_OPENSSL_CIPHER_AES_256_CBC: - return EVP_aes_256_cbc(); - break; -#endif - - - default: - return NULL; - break; - } -} - void php_openssl_backend_init(void) { #ifdef LIBRESSL_VERSION_NUMBER @@ -1932,7 +1850,7 @@ PHP_OPENSSL_API zend_string* php_openssl_encrypt( PHP_OPENSSL_CHECK_LONG_TO_INT_NULL_RETURN(tag_len, tag_len); - cipher_type = EVP_get_cipherbyname(method); + cipher_type = php_openssl_get_evp_cipher_by_name(method); if (!cipher_type) { php_error_docref(NULL, E_WARNING, "Unknown cipher algorithm"); return NULL; @@ -1940,6 +1858,7 @@ PHP_OPENSSL_API zend_string* php_openssl_encrypt( cipher_ctx = EVP_CIPHER_CTX_new(); if (!cipher_ctx) { + php_openssl_release_evp_cipher(cipher_type); php_error_docref(NULL, E_WARNING, "Failed to create cipher context"); return NULL; } @@ -1998,6 +1917,7 @@ PHP_OPENSSL_API zend_string* php_openssl_encrypt( } EVP_CIPHER_CTX_reset(cipher_ctx); EVP_CIPHER_CTX_free(cipher_ctx); + php_openssl_release_evp_cipher(cipher_type); return outbuf; } @@ -2024,7 +1944,7 @@ PHP_OPENSSL_API zend_string* php_openssl_decrypt( PHP_OPENSSL_CHECK_SIZE_T_TO_INT_NULL_RETURN(tag_len, tag); - cipher_type = EVP_get_cipherbyname(method); + cipher_type = php_openssl_get_evp_cipher_by_name(method); if (!cipher_type) { php_error_docref(NULL, E_WARNING, "Unknown cipher algorithm"); return NULL; @@ -2032,6 +1952,7 @@ PHP_OPENSSL_API zend_string* php_openssl_decrypt( cipher_ctx = EVP_CIPHER_CTX_new(); if (!cipher_ctx) { + php_openssl_release_evp_cipher(cipher_type); php_error_docref(NULL, E_WARNING, "Failed to create cipher context"); return NULL; } @@ -2077,14 +1998,15 @@ PHP_OPENSSL_API zend_string* php_openssl_decrypt( } EVP_CIPHER_CTX_reset(cipher_ctx); EVP_CIPHER_CTX_free(cipher_ctx); + php_openssl_release_evp_cipher(cipher_type); return outbuf; } -const EVP_CIPHER *php_openssl_get_evp_cipher_by_name(const char *method) +const EVP_CIPHER *php_openssl_get_evp_cipher_by_name_with_warning(const char *method) { const EVP_CIPHER *cipher_type; - cipher_type = EVP_get_cipherbyname(method); + cipher_type = php_openssl_get_evp_cipher_by_name(method); if (!cipher_type) { php_error_docref(NULL, E_WARNING, "Unknown cipher algorithm"); return NULL; @@ -2096,16 +2018,26 @@ const EVP_CIPHER *php_openssl_get_evp_cipher_by_name(const char *method) PHP_OPENSSL_API zend_long php_openssl_cipher_iv_length(const char *method) { - const EVP_CIPHER *cipher_type = php_openssl_get_evp_cipher_by_name(method); + const EVP_CIPHER *cipher_type = php_openssl_get_evp_cipher_by_name_with_warning(method); + if (cipher_type == NULL) { + return -1; + } + int iv_length = EVP_CIPHER_iv_length(cipher_type); + php_openssl_release_evp_cipher(cipher_type); - return cipher_type == NULL ? -1 : EVP_CIPHER_iv_length(cipher_type); + return iv_length; } PHP_OPENSSL_API zend_long php_openssl_cipher_key_length(const char *method) { - const EVP_CIPHER *cipher_type = php_openssl_get_evp_cipher_by_name(method); + const EVP_CIPHER *cipher_type = php_openssl_get_evp_cipher_by_name_with_warning(method); + if (cipher_type == NULL) { + return -1; + } + int key_length = EVP_CIPHER_key_length(cipher_type); + php_openssl_release_evp_cipher(cipher_type); - return cipher_type == NULL ? -1 : EVP_CIPHER_key_length(cipher_type); + return key_length; } PHP_OPENSSL_API zend_string* php_openssl_random_pseudo_bytes(zend_long buffer_length) diff --git a/ext/openssl/openssl_backend_v1.c b/ext/openssl/openssl_backend_v1.c index eb94ee3fbe4bc..8b9ede38437d6 100644 --- a/ext/openssl/openssl_backend_v1.c +++ b/ext/openssl/openssl_backend_v1.c @@ -561,6 +561,114 @@ zend_string *php_openssl_dh_compute_key(EVP_PKEY *pkey, char *pub_str, size_t pu return data; } +const EVP_MD *php_openssl_get_evp_md_by_name(const char *name) +{ + return EVP_get_digestbyname(name); +} + +const EVP_MD *php_openssl_get_evp_md_from_algo(zend_long algo) +{ + EVP_MD *mdtype; + + switch (algo) { + case OPENSSL_ALGO_SHA1: + mdtype = (EVP_MD *) EVP_sha1(); + break; + case OPENSSL_ALGO_MD5: + mdtype = (EVP_MD *) EVP_md5(); + break; +#ifndef OPENSSL_NO_MD4 + case OPENSSL_ALGO_MD4: + mdtype = (EVP_MD *) EVP_md4(); + break; +#endif +#ifndef OPENSSL_NO_MD2 + case OPENSSL_ALGO_MD2: + mdtype = (EVP_MD *) EVP_md2(); + break; +#endif + case OPENSSL_ALGO_SHA224: + mdtype = (EVP_MD *) EVP_sha224(); + break; + case OPENSSL_ALGO_SHA256: + mdtype = (EVP_MD *) EVP_sha256(); + break; + case OPENSSL_ALGO_SHA384: + mdtype = (EVP_MD *) EVP_sha384(); + break; + case OPENSSL_ALGO_SHA512: + mdtype = (EVP_MD *) EVP_sha512(); + break; +#ifndef OPENSSL_NO_RMD160 + case OPENSSL_ALGO_RMD160: + mdtype = (EVP_MD *) EVP_ripemd160(); + break; +#endif + default: + return NULL; + break; + } + return mdtype; +} + +void php_openssl_release_evp_md(const EVP_MD *md) +{ + // Do nothing as MD is static +} + +const EVP_CIPHER *php_openssl_get_evp_cipher_by_name(const char *name) +{ + return EVP_get_cipherbyname(name); +} + +const EVP_CIPHER *php_openssl_get_evp_cipher_from_algo(zend_long algo) +{ + switch (algo) { +#ifndef OPENSSL_NO_RC2 + case PHP_OPENSSL_CIPHER_RC2_40: + return EVP_rc2_40_cbc(); + break; + case PHP_OPENSSL_CIPHER_RC2_64: + return EVP_rc2_64_cbc(); + break; + case PHP_OPENSSL_CIPHER_RC2_128: + return EVP_rc2_cbc(); + break; +#endif + +#ifndef OPENSSL_NO_DES + case PHP_OPENSSL_CIPHER_DES: + return EVP_des_cbc(); + break; + case PHP_OPENSSL_CIPHER_3DES: + return EVP_des_ede3_cbc(); + break; +#endif + +#ifndef OPENSSL_NO_AES + case PHP_OPENSSL_CIPHER_AES_128_CBC: + return EVP_aes_128_cbc(); + break; + case PHP_OPENSSL_CIPHER_AES_192_CBC: + return EVP_aes_192_cbc(); + break; + case PHP_OPENSSL_CIPHER_AES_256_CBC: + return EVP_aes_256_cbc(); + break; +#endif + + + default: + return NULL; + break; + } +} + +void php_openssl_release_evp_cipher(const EVP_CIPHER *cipher) +{ + // Do nothing as the cipher is static +} + void php_openssl_get_cipher_methods(zval *return_value, bool aliases) { array_init(return_value); diff --git a/ext/openssl/openssl_backend_v3.c b/ext/openssl/openssl_backend_v3.c index 0876690aee0ba..e3c80cf659ba1 100644 --- a/ext/openssl/openssl_backend_v3.c +++ b/ext/openssl/openssl_backend_v3.c @@ -696,6 +696,89 @@ zend_string *php_openssl_dh_compute_key(EVP_PKEY *pkey, char *pub_str, size_t pu return result; } +const EVP_MD *php_openssl_get_evp_md_by_name(const char *name) +{ + return EVP_MD_fetch(OPENSSL_G(libctx), name, OPENSSL_G(propq)); +} + +static const char *php_openssl_digest_names[] = { + [OPENSSL_ALGO_SHA1] = "SHA1", + [OPENSSL_ALGO_MD5] = "MD5", +#ifndef OPENSSL_NO_MD4 + [OPENSSL_ALGO_MD4] = "MD4", +#endif +#ifndef OPENSSL_NO_MD2 + [OPENSSL_ALGO_MD2] = "MD2", +#endif + [OPENSSL_ALGO_SHA224] = "SHA224", + [OPENSSL_ALGO_SHA256] = "SHA256", + [OPENSSL_ALGO_SHA384] = "SHA384", + [OPENSSL_ALGO_SHA512] = "SHA512", +#ifndef OPENSSL_NO_RMD160 + [OPENSSL_ALGO_RMD160] = "RIPEMD160", +#endif +}; + +const EVP_MD *php_openssl_get_evp_md_from_algo(zend_long algo) +{ + if (algo < 0 || algo >= (zend_long)(sizeof(php_openssl_digest_names) / sizeof(*php_openssl_digest_names))) { + return NULL; + } + + const char *name = php_openssl_digest_names[algo]; + if (!name) { + return NULL; + } + + return php_openssl_get_evp_md_by_name(name); +} + +void php_openssl_release_evp_md(const EVP_MD *md) +{ + if (md != NULL) { + // It is fine to remove const as the md is from EVP_MD_fetch + EVP_MD_free((EVP_MD *) md); + } +} + +static const char *php_openssl_cipher_names[] = { + [PHP_OPENSSL_CIPHER_RC2_40] = "RC2-40-CBC", + [PHP_OPENSSL_CIPHER_RC2_128] = "RC2-CBC", + [PHP_OPENSSL_CIPHER_RC2_64] = "RC2-64-CBC", + [PHP_OPENSSL_CIPHER_DES] = "DES-CBC", + [PHP_OPENSSL_CIPHER_3DES] = "DES-EDE3-CBC", + [PHP_OPENSSL_CIPHER_AES_128_CBC]= "AES-128-CBC", + [PHP_OPENSSL_CIPHER_AES_192_CBC]= "AES-192-CBC", + [PHP_OPENSSL_CIPHER_AES_256_CBC]= "AES-256-CBC", +}; + +const EVP_CIPHER *php_openssl_get_evp_cipher_by_name(const char *name) +{ + return EVP_CIPHER_fetch(OPENSSL_G(libctx), name, OPENSSL_G(propq)); +} + +const EVP_CIPHER *php_openssl_get_evp_cipher_from_algo(zend_long algo) +{ + if (algo < 0 || algo >= (zend_long)(sizeof(php_openssl_cipher_names) / sizeof(*php_openssl_cipher_names))) { + return NULL; + } + + const char *name = php_openssl_cipher_names[algo]; + if (!name) { + return NULL; + } + + return php_openssl_get_evp_cipher_by_name(name); +} + +void php_openssl_release_evp_cipher(const EVP_CIPHER *cipher) +{ + if (cipher != NULL) { + // It is fine to remove const as the cipher is from EVP_CIPHER_fetch + EVP_CIPHER_free((EVP_CIPHER *) cipher); + } +} + static void php_openssl_add_cipher_name(const char *name, void *arg) { size_t len = strlen(name); @@ -722,7 +805,7 @@ static int php_openssl_compare_func(Bucket *a, Bucket *b) void php_openssl_get_cipher_methods(zval *return_value, bool aliases) { array_init(return_value); - EVP_CIPHER_do_all_provided(NULL, + EVP_CIPHER_do_all_provided(OPENSSL_G(libctx), aliases ? php_openssl_add_cipher_or_alias : php_openssl_add_cipher, return_value); zend_hash_sort(Z_ARRVAL_P(return_value), php_openssl_compare_func, 1); diff --git a/ext/openssl/php_openssl_backend.h b/ext/openssl/php_openssl_backend.h index 5cfa73160b853..69317e3c7833b 100644 --- a/ext/openssl/php_openssl_backend.h +++ b/ext/openssl/php_openssl_backend.h @@ -75,14 +75,17 @@ enum php_openssl_key_type { OPENSSL_KEYTYPE_RSA, OPENSSL_KEYTYPE_DSA, OPENSSL_KEYTYPE_DH, + OPENSSL_KEYTYPE_EC, + OPENSSL_KEYTYPE_X25519, + OPENSSL_KEYTYPE_ED25519, + OPENSSL_KEYTYPE_X448, + OPENSSL_KEYTYPE_ED448, + OPENSSL_KEYTYPE_DEFAULT = OPENSSL_KEYTYPE_RSA, - OPENSSL_KEYTYPE_EC = OPENSSL_KEYTYPE_DH +1, - OPENSSL_KEYTYPE_X25519 = OPENSSL_KEYTYPE_DH +2, - OPENSSL_KEYTYPE_ED25519 = OPENSSL_KEYTYPE_DH +3, - OPENSSL_KEYTYPE_X448 = OPENSSL_KEYTYPE_DH +4, - OPENSSL_KEYTYPE_ED448 = OPENSSL_KEYTYPE_DH +5, }; +/* Cipher constants, do not forget to update php_openssl_cipher_names in + * openssl_backend_v3.c if new constant added. */ enum php_openssl_cipher_type { PHP_OPENSSL_CIPHER_RC2_40, PHP_OPENSSL_CIPHER_RC2_128, @@ -106,10 +109,10 @@ enum php_openssl_encoding { ENCODING_PEM, }; - #define MIN_KEY_LENGTH 384 -/* constants used in ext/phar/util.c, keep in sync */ +/* Constants used in ext/phar/util.c, keep in sync and do not forget to update + * php_openssl_digest_names in openssl_backend_v3.c if new constant added. */ #define OPENSSL_ALGO_SHA1 1 #define OPENSSL_ALGO_MD5 2 #ifndef OPENSSL_NO_MD4 @@ -126,6 +129,7 @@ enum php_openssl_encoding { #ifndef OPENSSL_NO_RMD160 #define OPENSSL_ALGO_RMD160 10 #endif + #define DEBUG_SMIME 0 #if !defined(OPENSSL_NO_EC) && defined(EVP_PKEY_EC) @@ -221,8 +225,12 @@ void php_openssl_dispose_config(struct php_x509_request * req); zend_result php_openssl_load_rand_file(const char * file, int *egdsocket, int *seeded); zend_result php_openssl_write_rand_file(const char * file, int egdsocket, int seeded); -EVP_MD * php_openssl_get_evp_md_from_algo(zend_long algo); +const EVP_MD *php_openssl_get_evp_md_by_name(const char *name); +const EVP_MD *php_openssl_get_evp_md_from_algo(zend_long algo); +void php_openssl_release_evp_md(const EVP_MD *md); +const EVP_CIPHER * php_openssl_get_evp_cipher_by_name(const char *name); const EVP_CIPHER * php_openssl_get_evp_cipher_from_algo(zend_long algo); +void php_openssl_release_evp_cipher(const EVP_CIPHER *cipher); void php_openssl_backend_init(void); void php_openssl_backend_init_common(void); From 111072a9f0353e885abb9fd1c3cb15e56fff9703 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Tue, 3 Jun 2025 19:08:20 +0200 Subject: [PATCH 016/682] Fix GH-18744: PHP 8.4 classList works not correctly if copy HTMLElement by clone keyword. The $classList property is special in the sense that it's a cached object instance per (HTML)Element instance. The reason for this design is because it has the [[SameObject]] IDL attribute. Cloning in PHP also clones the properties, so it also clones the cached instance. To solve this, we undo this by resetting the backing storage. Closes GH-18749. --- NEWS | 4 +++ ext/dom/element.c | 15 ++++++---- ext/dom/php_dom.c | 23 +++++++++++++-- ext/dom/php_dom.h | 1 + ext/dom/tests/modern/token_list/gh18744.phpt | 30 ++++++++++++++++++++ 5 files changed, 66 insertions(+), 7 deletions(-) create mode 100644 ext/dom/tests/modern/token_list/gh18744.phpt diff --git a/NEWS b/NEWS index 5d10703707a44..b577d8fd948b5 100644 --- a/NEWS +++ b/NEWS @@ -19,6 +19,10 @@ PHP NEWS - Date: . Fix leaks with multiple calls to DatePeriod iterator current(). (nielsdos) +- DOM: + . Fixed bug GH-18744 (classList works not correctly if copy HTMLElement by + clone keyword). (nielsdos) + - FPM: . Fixed GH-18662 (fpm_get_status segfault). (txuna) diff --git a/ext/dom/element.c b/ext/dom/element.c index 64c53e4a2d8a1..e25805df53eb6 100644 --- a/ext/dom/element.c +++ b/ext/dom/element.c @@ -177,10 +177,7 @@ zend_result dom_element_class_name_write(dom_object *obj, zval *newval) } /* }}} */ -/* {{{ classList TokenList -URL: https://dom.spec.whatwg.org/#dom-element-classlist -*/ -zend_result dom_element_class_list_read(dom_object *obj, zval *retval) +zval *dom_element_class_list_zval(dom_object *obj) { const uint32_t PROP_INDEX = 0; @@ -191,7 +188,15 @@ zend_result dom_element_class_list_read(dom_object *obj, zval *retval) ZEND_ASSERT(OBJ_PROP_TO_NUM(prop_info->offset) == PROP_INDEX); #endif - zval *cached_token_list = OBJ_PROP_NUM(&obj->std, PROP_INDEX); + return OBJ_PROP_NUM(&obj->std, PROP_INDEX); +} + +/* {{{ classList TokenList +URL: https://dom.spec.whatwg.org/#dom-element-classlist +*/ +zend_result dom_element_class_list_read(dom_object *obj, zval *retval) +{ + zval *cached_token_list = dom_element_class_list_zval(obj); if (Z_ISUNDEF_P(cached_token_list)) { object_init_ex(cached_token_list, dom_token_list_class_entry); dom_token_list_object *intern = php_dom_token_list_from_obj(Z_OBJ_P(cached_token_list)); diff --git a/ext/dom/php_dom.c b/ext/dom/php_dom.c index 3c8d6e15cea20..3242529d8842c 100644 --- a/ext/dom/php_dom.c +++ b/ext/dom/php_dom.c @@ -101,6 +101,7 @@ static zend_object_handlers dom_modern_nodelist_object_handlers; static zend_object_handlers dom_html_collection_object_handlers; static zend_object_handlers dom_object_namespace_node_handlers; static zend_object_handlers dom_modern_domimplementation_object_handlers; +static zend_object_handlers dom_modern_element_object_handlers; static zend_object_handlers dom_token_list_object_handlers; #ifdef LIBXML_XPATH_ENABLED zend_object_handlers dom_xpath_object_handlers; @@ -669,6 +670,21 @@ static zend_object *dom_objects_store_clone_obj(zend_object *zobject) /* {{{ */ } /* }}} */ +static zend_object *dom_modern_element_clone_obj(zend_object *zobject) +{ + zend_object *clone = dom_objects_store_clone_obj(zobject); + + /* The $classList property is unique per element, and cached due to its [[SameObject]] requirement. + * Remove it from the clone so the clone will get a fresh instance upon demand. */ + zval *class_list = dom_element_class_list_zval(php_dom_obj_from_obj(clone)); + if (!Z_ISUNDEF_P(class_list)) { + zval_ptr_dtor(class_list); + ZVAL_UNDEF(class_list); + } + + return clone; +} + static zend_object *dom_object_namespace_node_clone_obj(zend_object *zobject) { dom_object_namespace_node *intern = php_dom_namespace_node_obj_from_obj(zobject); @@ -778,6 +794,9 @@ PHP_MINIT_FUNCTION(dom) * one instance per parent object. */ dom_modern_domimplementation_object_handlers.clone_obj = NULL; + memcpy(&dom_modern_element_object_handlers, &dom_object_handlers, sizeof(zend_object_handlers)); + dom_modern_element_object_handlers.clone_obj = dom_modern_element_clone_obj; + memcpy(&dom_nnodemap_object_handlers, &dom_object_handlers, sizeof(zend_object_handlers)); dom_nnodemap_object_handlers.free_obj = dom_nnodemap_objects_free_storage; dom_nnodemap_object_handlers.read_dimension = dom_nodemap_read_dimension; @@ -1108,7 +1127,7 @@ PHP_MINIT_FUNCTION(dom) dom_modern_element_class_entry = register_class_Dom_Element(dom_modern_node_class_entry, dom_modern_parentnode_class_entry, dom_modern_childnode_class_entry); dom_modern_element_class_entry->create_object = dom_objects_new; - dom_modern_element_class_entry->default_object_handlers = &dom_object_handlers; + dom_modern_element_class_entry->default_object_handlers = &dom_modern_element_object_handlers; zend_hash_init(&dom_modern_element_prop_handlers, 0, NULL, NULL, true); DOM_REGISTER_PROP_HANDLER(&dom_modern_element_prop_handlers, "namespaceURI", dom_node_namespace_uri_read, NULL); @@ -1132,7 +1151,7 @@ PHP_MINIT_FUNCTION(dom) dom_html_element_class_entry = register_class_Dom_HTMLElement(dom_modern_element_class_entry); dom_html_element_class_entry->create_object = dom_objects_new; - dom_html_element_class_entry->default_object_handlers = &dom_object_handlers; + dom_html_element_class_entry->default_object_handlers = &dom_modern_element_object_handlers; zend_hash_add_new_ptr(&classes, dom_html_element_class_entry->name, &dom_modern_element_prop_handlers); dom_text_class_entry = register_class_DOMText(dom_characterdata_class_entry); diff --git a/ext/dom/php_dom.h b/ext/dom/php_dom.h index bb0d83676dca1..22c738b20e0f6 100644 --- a/ext/dom/php_dom.h +++ b/ext/dom/php_dom.h @@ -187,6 +187,7 @@ bool php_dom_create_nullable_object(xmlNodePtr obj, zval *return_value, dom_obje xmlNodePtr dom_clone_node(php_dom_libxml_ns_mapper *ns_mapper, xmlNodePtr node, xmlDocPtr doc, bool recursive); void dom_set_document_ref_pointers(xmlNodePtr node, php_libxml_ref_obj *document); void dom_set_document_ref_pointers_attr(xmlAttrPtr attr, php_libxml_ref_obj *document); +zval *dom_element_class_list_zval(dom_object *obj); typedef enum { DOM_LOAD_STRING = 0, diff --git a/ext/dom/tests/modern/token_list/gh18744.phpt b/ext/dom/tests/modern/token_list/gh18744.phpt new file mode 100644 index 0000000000000..a9109df789d8f --- /dev/null +++ b/ext/dom/tests/modern/token_list/gh18744.phpt @@ -0,0 +1,30 @@ +--TEST-- +GH-18744 (classList works not correctly if copy HTMLElement by clone keyword.) +--EXTENSIONS-- +dom +--FILE-- +createElement('div'); +$ele1->classList->add('foo'); +$ele2 = clone $ele1; +$ele2->classList->add('bar'); + +echo "Element1 class: " . $ele1->getAttribute('class'); +echo "\n"; +echo "Element2 class: " . $ele2->getAttribute('class'); +echo "\n"; + +var_dump($ele1->classList !== $ele2->classList); +// These comparisons are not pointless: they're getters and should not create new objects +var_dump($ele1->classList === $ele1->classList); +var_dump($ele2->classList === $ele2->classList); + +?> +--EXPECT-- +Element1 class: foo +Element2 class: foo bar +bool(true) +bool(true) +bool(true) From 2b383848a738eda02bc0f5bf116b021a6e42c24a Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Wed, 4 Jun 2025 19:52:21 +0200 Subject: [PATCH 017/682] Fix handling of references in zval_try_get_long() This API can't handle references, yet everyone keeps forgetting that it can't and that you should DEREF upfront. Fix every type of this issue once and for all by moving the reference handling to this Zend API. Closes GH-18761. --- NEWS | 1 + Zend/zend_operators.c | 9 +++++++++ ext/intl/dateformat/dateformat_parse.c | 8 +++----- ext/mbstring/mbstring.c | 2 +- .../tests/mb_encode_numericentity_references.phpt | 12 ++++++++++++ ext/pcntl/pcntl.c | 3 +-- 6 files changed, 27 insertions(+), 8 deletions(-) create mode 100644 ext/mbstring/tests/mb_encode_numericentity_references.phpt diff --git a/NEWS b/NEWS index b577d8fd948b5..6c30ac60aa88d 100644 --- a/NEWS +++ b/NEWS @@ -12,6 +12,7 @@ PHP NEWS released on bailout). (DanielEScherzer and ilutov) . Fixed GH-18695 (zend_ast_export() - float number is not preserved). (Oleg Efimov) + . Fix handling of references in zval_try_get_long(). (nielsdos) - Curl: . Fix memory leak when setting a list via curl_setopt fails. (nielsdos) diff --git a/Zend/zend_operators.c b/Zend/zend_operators.c index ae75e95a71c1d..252b8df1ea0fb 100644 --- a/Zend/zend_operators.c +++ b/Zend/zend_operators.c @@ -378,6 +378,7 @@ static zend_always_inline zend_result zendi_try_convert_scalar_to_number(zval *o static zend_never_inline zend_long ZEND_FASTCALL zendi_try_get_long(const zval *op, bool *failed) /* {{{ */ { *failed = 0; +try_again: switch (Z_TYPE_P(op)) { case IS_NULL: case IS_FALSE: @@ -448,6 +449,14 @@ static zend_never_inline zend_long ZEND_FASTCALL zendi_try_get_long(const zval * case IS_ARRAY: *failed = 1; return 0; + case IS_REFERENCE: + op = Z_REFVAL_P(op); + if (Z_TYPE_P(op) == IS_LONG) { + return Z_LVAL_P(op); + } else { + goto try_again; + } + break; EMPTY_SWITCH_DEFAULT_CASE() } } diff --git a/ext/intl/dateformat/dateformat_parse.c b/ext/intl/dateformat/dateformat_parse.c index 129e007107db7..2bdde08bcaced 100644 --- a/ext/intl/dateformat/dateformat_parse.c +++ b/ext/intl/dateformat/dateformat_parse.c @@ -185,12 +185,10 @@ PHP_METHOD(IntlDateFormatter, parseToCalendar) DATE_FORMAT_METHOD_FETCH_OBJECT; if (z_parse_pos) { - zval *z_parse_pos_tmp = z_parse_pos; - ZVAL_DEREF(z_parse_pos_tmp); - bool failed = false; - zend_long long_parse_pos = zval_try_get_long(z_parse_pos_tmp, &failed); + bool failed; + zend_long long_parse_pos = zval_try_get_long(z_parse_pos, &failed); if (failed) { - zend_argument_type_error(2, "must be of type int, %s given", zend_zval_value_name(z_parse_pos_tmp)); + zend_argument_type_error(2, "must be of type int, %s given", zend_zval_value_name(z_parse_pos)); RETURN_THROWS(); } if (ZEND_LONG_INT_OVFL(long_parse_pos)) { diff --git a/ext/mbstring/mbstring.c b/ext/mbstring/mbstring.c index e848f21615bca..ca5a8b8ec72e4 100644 --- a/ext/mbstring/mbstring.c +++ b/ext/mbstring/mbstring.c @@ -3930,7 +3930,7 @@ static uint32_t *make_conversion_map(HashTable *target_hash, size_t *conversion_ uint32_t *mapelm = convmap; ZEND_HASH_FOREACH_VAL(target_hash, hash_entry) { - bool failed = true; + bool failed; zend_long tmp = zval_try_get_long(hash_entry, &failed); if (failed) { efree(convmap); diff --git a/ext/mbstring/tests/mb_encode_numericentity_references.phpt b/ext/mbstring/tests/mb_encode_numericentity_references.phpt new file mode 100644 index 0000000000000..682be9b2cd1e2 --- /dev/null +++ b/ext/mbstring/tests/mb_encode_numericentity_references.phpt @@ -0,0 +1,12 @@ +--TEST-- +mb_encode_numericentity() reference handling +--EXTENSIONS-- +mbstring +--FILE-- + +--EXPECT-- +string(0) "" diff --git a/ext/pcntl/pcntl.c b/ext/pcntl/pcntl.c index 89631960d5677..25a4de9386d44 100644 --- a/ext/pcntl/pcntl.c +++ b/ext/pcntl/pcntl.c @@ -874,8 +874,7 @@ static bool php_pcntl_set_user_signal_infos( zval *user_signal_no; ZEND_HASH_FOREACH_VAL(user_signals, user_signal_no) { - bool failed = true; - ZVAL_DEREF(user_signal_no); + bool failed; zend_long tmp = zval_try_get_long(user_signal_no, &failed); if (failed) { From e13ba36abbce730b552768d13a575799937a6e2d Mon Sep 17 00:00:00 2001 From: David Carlier Date: Wed, 4 Jun 2025 19:22:06 +0100 Subject: [PATCH 018/682] ext/tidy: anticipate tidyOptIsReadOnly retirement. using tidyOptGetCategory when possible. related GH-18751 close GH-18763 --- NEWS | 1 + ext/tidy/config.m4 | 5 +++++ ext/tidy/tidy.c | 4 ++++ 3 files changed, 10 insertions(+) diff --git a/NEWS b/NEWS index d6f38f9adceb9..9b2c97db0b1cf 100644 --- a/NEWS +++ b/NEWS @@ -44,6 +44,7 @@ PHP NEWS - Tidy: . Fix memory leak in tidy output handler on error. (nielsdos) + . Fix tidyOptIsReadonly deprecation, using tidyOptGetCategory. (David Carlier) 05 Jun 2025, PHP 8.3.22 diff --git a/ext/tidy/config.m4 b/ext/tidy/config.m4 index bc0976a1dd9b3..569cb3672944b 100644 --- a/ext/tidy/config.m4 +++ b/ext/tidy/config.m4 @@ -62,6 +62,11 @@ if test "$PHP_TIDY" != "no"; then AC_DEFINE(HAVE_TIDYRELEASEDATE,1,[ ]) ], [], []) + PHP_CHECK_LIBRARY($TIDY_LIB_NAME,tidyOptGetCategory, + [ + AC_DEFINE(HAVE_TIDYOPTGETCATEGORY,1,[ ]) + ], [], []) + PHP_ADD_LIBRARY_WITH_PATH($TIDY_LIB_NAME, $TIDY_LIBDIR, TIDY_SHARED_LIBADD) PHP_ADD_INCLUDE($TIDY_INCDIR) diff --git a/ext/tidy/tidy.c b/ext/tidy/tidy.c index 46dd637f40e54..a42e2bc203770 100644 --- a/ext/tidy/tidy.c +++ b/ext/tidy/tidy.c @@ -232,7 +232,11 @@ static int _php_tidy_set_tidy_opt(TidyDoc doc, char *optname, zval *value) return FAILURE; } +#if defined(HAVE_TIDYOPTGETCATEGORY) + if (tidyOptGetCategory(opt) == TidyInternalCategory) { +#else if (tidyOptIsReadOnly(opt)) { +#endif php_error_docref(NULL, E_WARNING, "Attempting to set read-only option \"%s\"", optname); return FAILURE; } From 7f914620193079b42a91eb84451719e4b376702b Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Wed, 4 Jun 2025 23:18:48 +0200 Subject: [PATCH 019/682] Remove nonsensical code from odbc_execute() (#18767) strlen() operates on NUL-terminated strings, writing a NUL byte at the strlen offset accomplishes nothing but wasting cycles. --- ext/odbc/php_odbc.c | 1 - 1 file changed, 1 deletion(-) diff --git a/ext/odbc/php_odbc.c b/ext/odbc/php_odbc.c index 4017c72a35a67..2eb994cad3d5e 100644 --- a/ext/odbc/php_odbc.c +++ b/ext/odbc/php_odbc.c @@ -1068,7 +1068,6 @@ PHP_FUNCTION(odbc_execute) RETURN_FALSE; } filename = estrndup(&ZSTR_VAL(tmpstr)[1], ZSTR_LEN(tmpstr) - 2); - filename[strlen(filename)] = '\0'; /* Check the basedir */ if (php_check_open_basedir(filename)) { From 42f6c15186b289fa16d71bf0a33cbc5da6098c43 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Fri, 21 Feb 2025 15:23:24 +0100 Subject: [PATCH 020/682] Fix bug #74796: Requests through http proxy set peer name This issue happens because http wrapper sets peer_name but then does not remove so it stays in the context. The fix removes the peer name from the context after enabling crypto. In addition to bug #74796, this also fixes bug #76196. In addition it should be a final fix for those SOAP bugs: bug #69783 bug #52913 bug #61463 --- NEWS | 2 + ext/openssl/tests/bug74796.phpt | 175 ++++++++++++++++++++++++++++++ ext/standard/http_fopen_wrapper.c | 6 + main/streams/php_stream_context.h | 3 +- main/streams/streams.c | 14 +++ 5 files changed, 199 insertions(+), 1 deletion(-) create mode 100644 ext/openssl/tests/bug74796.phpt diff --git a/NEWS b/NEWS index 9b2c97db0b1cf..6867a3dbcaeb8 100644 --- a/NEWS +++ b/NEWS @@ -22,6 +22,8 @@ PHP NEWS - OpenSSL: . Fix memory leak of X509_STORE in php_openssl_setup_verify() on failure. (nielsdos) + . Fixed bug #74796 (Requests through http proxy set peer name). + (Jakub Zelenka) - Phar: . Add missing filter cleanups on phar failure. (nielsdos) diff --git a/ext/openssl/tests/bug74796.phpt b/ext/openssl/tests/bug74796.phpt new file mode 100644 index 0000000000000..a5c99e61697a8 --- /dev/null +++ b/ext/openssl/tests/bug74796.phpt @@ -0,0 +1,175 @@ +--TEST-- +Bug #74796: TLS encryption fails behind HTTP proxy +--EXTENSIONS-- +openssl +--SKIPIF-- + +--FILE-- + [ + 'SNI_server_certs' => [ + "cs.php.net" => __DIR__ . "/sni_server_cs.pem", + "uk.php.net" => __DIR__ . "/sni_server_uk.pem", + "us.php.net" => __DIR__ . "/sni_server_us.pem" + ] + ]]); + + $server = stream_socket_server('tls://127.0.0.1:0', $errno, $errstr, $serverFlags, $ctx); + phpt_notify_server_start($server); + + for ($i=0; $i < 3; $i++) { + $conn = stream_socket_accept($server, 3); + fwrite($conn, "HTTP/1.0 200 OK\r\n\r\nHello from server $i"); + fclose($conn); + } + + phpt_wait(); +CODE; + +$proxyCode = <<<'CODE' + function parse_sni_from_client_hello($data) { + $sni = null; + + if (strlen($data) < 5 || ord($data[0]) != 0x16) return null; + + $session_id_len = ord($data[43]); + $ptr = 44 + $session_id_len; + + // Cipher suites length + $cipher_suites_len = (ord($data[$ptr]) << 8) | ord($data[$ptr+1]); + $ptr += 2 + $cipher_suites_len; + + // Compression methods length + $compression_methods_len = ord($data[$ptr]); + $ptr += 1 + $compression_methods_len; + + // Extensions length + if ($ptr + 2 > strlen($data)) return null; + $extensions_len = (ord($data[$ptr]) << 8) | ord($data[$ptr+1]); + $ptr += 2; + + $extensions_end = $ptr + $extensions_len; + + while ($ptr + 4 <= $extensions_end) { + $ext_type = (ord($data[$ptr]) << 8) | ord($data[$ptr+1]); + $ext_len = (ord($data[$ptr+2]) << 8) | ord($data[$ptr+3]); + $ptr += 4; + + if ($ext_type === 0x00) { // SNI extension + if ($ptr + 2 > strlen($data)) break; + $name_list_len = (ord($data[$ptr]) << 8) | ord($data[$ptr+1]); + $ptr += 2; + + if ($ptr + 3 > strlen($data)) break; + $name_type = ord($data[$ptr]); + $name_len = (ord($data[$ptr+1]) << 8) | ord($data[$ptr+2]); + $ptr += 3; + + if ($name_type === 0) { // host_name type + $sni = substr($data, $ptr, $name_len); + break; + } + } + + $ptr += $ext_len; + } + + return $sni; + } + + $flags = STREAM_SERVER_BIND | STREAM_SERVER_LISTEN; + $server = stream_socket_server("tcp://127.0.0.1:0", $errornum, $errorstr, $flags); + phpt_notify_server_start($server); + + for ($i=0; $i < 3; $i++) { + $upstream = stream_socket_client("tcp://{{ ADDR }}", $errornum, $errorstr, 30, STREAM_CLIENT_CONNECT); + stream_set_blocking($upstream, false); + + $conn = stream_socket_accept($server); + stream_set_blocking($conn, true); + + // reading CONNECT request headers + while (($line = fgets($conn)) !== false) { + if (rtrim($line) === '') break; // empty line means end of headers + } + + // successful CONNECT response + fwrite($conn, "HTTP/1.0 200 Connection established\r\n\r\n"); + + // tunnel data + stream_set_blocking($conn, false); + $firstRead = true; + while (!feof($conn) && !feof($upstream)) { + $clientData = fread($conn, 8192); + if ($clientData !== false && $clientData !== '') { + if ($firstRead) { + $sni = parse_sni_from_client_hello($clientData); + if ($sni !== null) { + file_put_contents(__DIR__ . "/bug74796_proxy_sni.log", $sni . "\n", FILE_APPEND); + } + $firstRead = false; + } + fwrite($upstream, $clientData); + } + + $serverData = fread($upstream, 8192); + if ($serverData !== false && $serverData !== '') { + fwrite($conn, $serverData); + } + } + fclose($conn); + fclose($upstream); + phpt_wait(); + } +CODE; + +$clientCode = <<<'CODE' + $clientCtx = stream_context_create([ + 'ssl' => [ + 'cafile' => __DIR__ . '/sni_server_ca.pem', + 'verify_peer' => true, + 'verify_peer_name' => true, + ], + "http" => [ + "proxy" => "tcp://{{ ADDR }}" + ], + ]); + + // servers + $hosts = ["cs.php.net", "uk.php.net", "us.php.net"]; + foreach ($hosts as $host) { + var_dump(file_get_contents("https://$host/", false, $clientCtx)); + var_dump(stream_context_get_options($clientCtx)['ssl']['peer_name'] ?? null); + phpt_notify('proxy'); + } + + echo file_get_contents(__DIR__ . "/bug74796_proxy_sni.log"); + + phpt_notify('server'); +CODE; + +include 'ServerClientTestCase.inc'; +ServerClientTestCase::getInstance()->run($clientCode, [ + 'server' => $serverCode, + 'proxy' => $proxyCode, +]); +?> +--CLEAN-- + +--EXPECT-- +string(19) "Hello from server 0" +NULL +string(19) "Hello from server 1" +NULL +string(19) "Hello from server 2" +NULL +cs.php.net +uk.php.net +us.php.net diff --git a/ext/standard/http_fopen_wrapper.c b/ext/standard/http_fopen_wrapper.c index b4d065dd0b625..040ee4eabf78b 100644 --- a/ext/standard/http_fopen_wrapper.c +++ b/ext/standard/http_fopen_wrapper.c @@ -470,12 +470,14 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, if (stream && use_proxy && use_ssl) { smart_str header = {0}; + bool reset_ssl_peer_name = false; /* Set peer_name or name verification will try to use the proxy server name */ if (!context || (tmpzval = php_stream_context_get_option(context, "ssl", "peer_name")) == NULL) { ZVAL_STR_COPY(&ssl_proxy_peer_name, resource->host); php_stream_context_set_option(PHP_STREAM_CONTEXT(stream), "ssl", "peer_name", &ssl_proxy_peer_name); zval_ptr_dtor(&ssl_proxy_peer_name); + reset_ssl_peer_name = true; } smart_str_appendl(&header, "CONNECT ", sizeof("CONNECT ")-1); @@ -572,6 +574,10 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, stream = NULL; } } + + if (reset_ssl_peer_name) { + php_stream_context_unset_option(PHP_STREAM_CONTEXT(stream), "ssl", "peer_name"); + } } php_stream_http_response_header_info_init(&header_info); diff --git a/main/streams/php_stream_context.h b/main/streams/php_stream_context.h index d4ebe29bc162e..56a1f53747116 100644 --- a/main/streams/php_stream_context.h +++ b/main/streams/php_stream_context.h @@ -59,7 +59,8 @@ PHPAPI zval *php_stream_context_get_option(php_stream_context *context, const char *wrappername, const char *optionname); PHPAPI void php_stream_context_set_option(php_stream_context *context, const char *wrappername, const char *optionname, zval *optionvalue); - +void php_stream_context_unset_option(php_stream_context *context, + const char *wrappername, const char *optionname); PHPAPI php_stream_notifier *php_stream_notification_alloc(void); PHPAPI void php_stream_notification_free(php_stream_notifier *notifier); END_EXTERN_C() diff --git a/main/streams/streams.c b/main/streams/streams.c index 4e0aaa53b443e..4f9c88e4774c4 100644 --- a/main/streams/streams.c +++ b/main/streams/streams.c @@ -2432,6 +2432,20 @@ PHPAPI void php_stream_context_set_option(php_stream_context *context, SEPARATE_ARRAY(wrapperhash); zend_hash_str_update(Z_ARRVAL_P(wrapperhash), optionname, strlen(optionname), optionvalue); } + +void php_stream_context_unset_option(php_stream_context *context, + const char *wrappername, const char *optionname) +{ + zval *wrapperhash; + + wrapperhash = zend_hash_str_find(Z_ARRVAL(context->options), wrappername, strlen(wrappername)); + if (NULL == wrapperhash) { + return; + } + SEPARATE_ARRAY(&context->options); + SEPARATE_ARRAY(wrapperhash); + zend_hash_str_del(Z_ARRVAL_P(wrapperhash), optionname, strlen(optionname)); +} /* }}} */ /* {{{ php_stream_dirent_alphasort */ From 91becb304286942337ad65ec614cc0d1a3f79cd6 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Tue, 3 Jun 2025 20:49:29 +0100 Subject: [PATCH 021/682] ext/tidy: config options checks strengthening. close GH-18751 --- NEWS | 4 ++++ UPGRADING | 6 +++++ ext/tidy/tests/tidy_error1.phpt | 40 ++++++++++++++++++++++++++++----- ext/tidy/tidy.c | 38 +++++++++++++++++-------------- 4 files changed, 66 insertions(+), 22 deletions(-) diff --git a/NEWS b/NEWS index 24d617d0c7933..88ba1f1e5bcc2 100644 --- a/NEWS +++ b/NEWS @@ -244,6 +244,10 @@ PHP NEWS - Tests: . Allow to shuffle tests even in non-parallell mode. (dhuang00) +- Tidy: + . tidy::__construct/parseFile/parseString methods throw an exception if + the configuration argument is invalid. (David Carlier) + - Windows: . Fixed bug GH-10992 (Improper long path support for relative paths). (cmb, nielsdos) diff --git a/UPGRADING b/UPGRADING index ef6739c3da933..0a639508ef696 100644 --- a/UPGRADING +++ b/UPGRADING @@ -316,6 +316,12 @@ PHP 8.5 UPGRADE NOTES . socket_getsockname gets the interface index and its string representation with AF_PACKET socket. +- Tidy: + . tidy::__construct/parseFile/parseString now throws a ValueError + if the configuration contains an invalid or set a read-only + internal entry, a TypeError contains, at least, one element + when the key is not a string. + - Zlib: . The "use_include_path" argument for the gzfile, gzopen and readgzfile functions had been changed diff --git a/ext/tidy/tests/tidy_error1.phpt b/ext/tidy/tests/tidy_error1.phpt index e9ced390d7ea7..0660f37437e2b 100644 --- a/ext/tidy/tests/tidy_error1.phpt +++ b/ext/tidy/tests/tidy_error1.phpt @@ -7,12 +7,42 @@ tidy --FILE-- '; -$config = array('bogus' => 'willnotwork'); +$config = ['bogus' => 'willnotwork']; $tidy = new tidy(); -var_dump($tidy->parseString($buffer, $config)); + +try { + $tidy->parseString($buffer, $config); +} catch (\ValueError $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} + +$config = ['neither']; +try { + $tidy->parseString($buffer, $config); +} catch (\TypeError $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} + +$config = ['doctype-mode' => 'customtag']; + +try { + var_dump($tidy->parseString($buffer, $config)); +} catch (\ValueError $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} + +$config = ['doctype' => 'php', 0 => 'value2']; + +try { + var_dump($tidy->parseString($buffer, $config)); +} catch (\TypeError $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} ?> ---EXPECTF-- -Warning: tidy::parseString(): Unknown Tidy configuration option "bogus" in %s on line %d -bool(true) +--EXPECT-- +ValueError: tidy::parseString(): Argument #2 ($config) Unknown Tidy configuration option "bogus" +TypeError: tidy::parseString(): Argument #2 ($config) must be of type array with keys as string +ValueError: tidy::parseString(): Argument #2 ($config) Attempting to set read-only option "doctype-mode" +TypeError: tidy::parseString(): Argument #2 ($config) must be of type array with keys as string diff --git a/ext/tidy/tidy.c b/ext/tidy/tidy.c index f7dca175e6024..ec1b10078d48d 100644 --- a/ext/tidy/tidy.c +++ b/ext/tidy/tidy.c @@ -130,8 +130,8 @@ static void tidy_doc_update_properties(PHPTidyObj *); static void tidy_add_node_default_properties(PHPTidyObj *); static void *php_tidy_get_opt_val(PHPTidyDoc *, TidyOption, TidyOptionType *); static void php_tidy_create_node(INTERNAL_FUNCTION_PARAMETERS, tidy_base_nodetypes); -static int _php_tidy_set_tidy_opt(TidyDoc, const char *, zval *); -static int _php_tidy_apply_config_array(TidyDoc doc, const HashTable *ht_options); +static zend_result _php_tidy_set_tidy_opt(TidyDoc, const char *, zval *, uint32_t arg); +static zend_result _php_tidy_apply_config_array(TidyDoc doc, const HashTable *ht_options, uint32_t arg); static PHP_INI_MH(php_tidy_set_clean_output); static void php_tidy_clean_output_start(const char *name, size_t name_len); static php_output_handler *php_tidy_output_handler_init(const char *handler_name, size_t handler_name_len, size_t chunk_size, int flags); @@ -209,10 +209,10 @@ static void php_tidy_load_config(TidyDoc doc, const char *path) } } -static zend_result php_tidy_apply_config(TidyDoc doc, const zend_string *str_string, const HashTable *ht_options) +static zend_result php_tidy_apply_config(TidyDoc doc, const zend_string *str_string, const HashTable *ht_options, uint32_t arg) { if (ht_options) { - return _php_tidy_apply_config_array(doc, ht_options); + return _php_tidy_apply_config_array(doc, ht_options, arg); } else if (str_string) { if (php_check_open_basedir(ZSTR_VAL(str_string))) { return FAILURE; @@ -222,14 +222,14 @@ static zend_result php_tidy_apply_config(TidyDoc doc, const zend_string *str_str return SUCCESS; } -static int _php_tidy_set_tidy_opt(TidyDoc doc, const char *optname, zval *value) +static zend_result _php_tidy_set_tidy_opt(TidyDoc doc, const char *optname, zval *value, uint32_t arg) { TidyOption opt = tidyGetOptionByName(doc, optname); zend_string *str, *tmp_str; zend_long lval; if (!opt) { - php_error_docref(NULL, E_WARNING, "Unknown Tidy configuration option \"%s\"", optname); + zend_argument_value_error(arg, "Unknown Tidy configuration option \"%s\"", optname); return FAILURE; } @@ -238,7 +238,7 @@ static int _php_tidy_set_tidy_opt(TidyDoc doc, const char *optname, zval *value) #else if (tidyOptIsReadOnly(opt)) { #endif - php_error_docref(NULL, E_WARNING, "Attempting to set read-only option \"%s\"", optname); + zend_argument_value_error(arg, "Attempting to set read-only option \"%s\"", optname); return FAILURE; } @@ -345,7 +345,7 @@ static void php_tidy_quick_repair(INTERNAL_FUNCTION_PARAMETERS, bool is_file) TIDY_SET_DEFAULT_CONFIG(doc); - if (php_tidy_apply_config(doc, config_str, config_ht) != SUCCESS) { + if (php_tidy_apply_config(doc, config_str, config_ht, 2) != SUCCESS) { RETVAL_FALSE; } else if (enc_len) { if (tidySetCharEncoding(doc, enc) < 0) { @@ -783,7 +783,7 @@ static void php_tidy_create_node(INTERNAL_FUNCTION_PARAMETERS, tidy_base_nodetyp tidy_create_node_object(return_value, obj->ptdoc, node); } -static int _php_tidy_apply_config_array(TidyDoc doc, const HashTable *ht_options) +static zend_result _php_tidy_apply_config_array(TidyDoc doc, const HashTable *ht_options, uint32_t arg) { zval *opt_val; zend_string *opt_name; @@ -791,12 +791,16 @@ static int _php_tidy_apply_config_array(TidyDoc doc, const HashTable *ht_options if (!HT_IS_PACKED(ht_options)) { ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(ht_options, opt_name, opt_val) { if (opt_name == NULL) { - continue; + zend_argument_type_error(arg, "must be of type array with keys as string"); + return FAILURE; } - _php_tidy_set_tidy_opt(doc, ZSTR_VAL(opt_name), opt_val); + _php_tidy_set_tidy_opt(doc, ZSTR_VAL(opt_name), opt_val, arg); } ZEND_HASH_FOREACH_END(); + return SUCCESS; + } else { + zend_argument_type_error(arg, "must be of type array with keys as string"); + return FAILURE; } - return SUCCESS; } static int php_tidy_parse_string(PHPTidyObj *obj, const char *string, uint32_t len, const char *enc) @@ -1018,7 +1022,7 @@ PHP_FUNCTION(tidy_parse_string) object_init_ex(return_value, tidy_ce_doc); obj = Z_TIDY_P(return_value); - if (php_tidy_apply_config(obj->ptdoc->doc, options_str, options_ht) != SUCCESS + if (php_tidy_apply_config(obj->ptdoc->doc, options_str, options_ht, 2) != SUCCESS || php_tidy_parse_string(obj, ZSTR_VAL(input), (uint32_t)ZSTR_LEN(input), enc) != SUCCESS) { zval_ptr_dtor(return_value); RETURN_FALSE; @@ -1086,7 +1090,7 @@ PHP_FUNCTION(tidy_parse_file) object_init_ex(return_value, tidy_ce_doc); obj = Z_TIDY_P(return_value); - if (php_tidy_apply_config(obj->ptdoc->doc, options_str, options_ht) != SUCCESS + if (php_tidy_apply_config(obj->ptdoc->doc, options_str, options_ht, 2) != SUCCESS || php_tidy_parse_string(obj, ZSTR_VAL(contents), (uint32_t)ZSTR_LEN(contents), enc) != SUCCESS) { zval_ptr_dtor(return_value); RETVAL_FALSE; @@ -1381,7 +1385,7 @@ PHP_METHOD(tidy, __construct) zend_error_handling error_handling; zend_replace_error_handling(EH_THROW, NULL, &error_handling); - if (php_tidy_apply_config(obj->ptdoc->doc, options_str, options_ht) != SUCCESS) { + if (php_tidy_apply_config(obj->ptdoc->doc, options_str, options_ht, 2) != SUCCESS) { zend_restore_error_handling(&error_handling); zend_string_release_ex(contents, 0); RETURN_THROWS(); @@ -1425,7 +1429,7 @@ PHP_METHOD(tidy, parseFile) RETURN_THROWS(); } - RETVAL_BOOL(php_tidy_apply_config(obj->ptdoc->doc, options_str, options_ht) == SUCCESS + RETVAL_BOOL(php_tidy_apply_config(obj->ptdoc->doc, options_str, options_ht, 2) == SUCCESS && php_tidy_parse_string(obj, ZSTR_VAL(contents), (uint32_t)ZSTR_LEN(contents), enc) == SUCCESS); zend_string_release_ex(contents, 0); @@ -1454,7 +1458,7 @@ PHP_METHOD(tidy, parseString) TIDY_SET_CONTEXT; obj = Z_TIDY_P(object); - RETURN_BOOL(php_tidy_apply_config(obj->ptdoc->doc, options_str, options_ht) == SUCCESS + RETURN_BOOL(php_tidy_apply_config(obj->ptdoc->doc, options_str, options_ht, 2) == SUCCESS && php_tidy_parse_string(obj, ZSTR_VAL(input), (uint32_t)ZSTR_LEN(input), enc) == SUCCESS); } From 444cc78a3e94e90a9424e7200ff9dca410d3f28f Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Thu, 5 Jun 2025 16:18:06 +0200 Subject: [PATCH 022/682] Skip OpenSSL proxy test for bug #74796 on Windows --- ext/openssl/tests/bug74796.phpt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ext/openssl/tests/bug74796.phpt b/ext/openssl/tests/bug74796.phpt index a5c99e61697a8..b3f594d5e60f4 100644 --- a/ext/openssl/tests/bug74796.phpt +++ b/ext/openssl/tests/bug74796.phpt @@ -5,6 +5,9 @@ openssl --SKIPIF-- --FILE-- Date: Thu, 5 Jun 2025 17:46:56 +0200 Subject: [PATCH 023/682] Use cheaper functions to get member name in PDO (#18769) --- ext/pdo/pdo_stmt.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/ext/pdo/pdo_stmt.c b/ext/pdo/pdo_stmt.c index afd6b2a43da6a..697940d94260d 100644 --- a/ext/pdo/pdo_stmt.c +++ b/ext/pdo/pdo_stmt.c @@ -2255,12 +2255,13 @@ static zval *row_dim_read(zend_object *object, zval *offset, int type, zval *rv) } return rv; } else { - zend_string *member = zval_try_get_string(offset); + zend_string *tmp_member; + zend_string *member = zval_try_get_tmp_string(offset, &tmp_member); if (!member) { return NULL; } zval *result = row_prop_read(object, member, type, NULL, rv); - zend_string_release_ex(member, false); + zend_tmp_string_release(tmp_member); return result; } } @@ -2330,12 +2331,13 @@ static int row_dim_exists(zend_object *object, zval *offset, int check_empty) zval_ptr_dtor_nogc(retval); return res; } else { - zend_string *member = zval_try_get_string(offset); + zend_string *tmp_member; + zend_string *member = zval_try_get_tmp_string(offset, &tmp_member); if (!member) { return 0; } int result = row_prop_exists(object, member, check_empty, NULL); - zend_string_release_ex(member, false); + zend_tmp_string_release(tmp_member); return result; } } From d40e3200a7ff5d6ebb331993502b46964db82158 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Thu, 5 Jun 2025 12:46:49 +0100 Subject: [PATCH 024/682] ext/tidy: Use uint32_t and bool types instead of unsigned int type On certain platform an unsigned int is 64bits, which is not needed. --- ext/tidy/tidy.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/ext/tidy/tidy.c b/ext/tidy/tidy.c index ec1b10078d48d..972c972e56e2d 100644 --- a/ext/tidy/tidy.c +++ b/ext/tidy/tidy.c @@ -99,10 +99,10 @@ typedef enum { } tidy_base_nodetypes; struct _PHPTidyDoc { - TidyDoc doc; - TidyBuffer *errbuf; - unsigned int ref_count; - unsigned int initialized:1; + TidyDoc doc; + TidyBuffer *errbuf; + uint32_t ref_count; + bool initialized; }; struct _PHPTidyObj { @@ -412,7 +412,7 @@ static void tidy_object_free_storage(zend_object *object) if (intern->ptdoc) { intern->ptdoc->ref_count--; - if (intern->ptdoc->ref_count <= 0) { + if (intern->ptdoc->ref_count == 0) { tidyBufFree(intern->ptdoc->errbuf); efree(intern->ptdoc->errbuf); tidyRelease(intern->ptdoc->doc); @@ -437,7 +437,7 @@ static zend_object *tidy_object_new(zend_class_entry *class_type, zend_object_ha intern->ptdoc = emalloc(sizeof(PHPTidyDoc)); intern->ptdoc->doc = tidyCreate(); intern->ptdoc->ref_count = 1; - intern->ptdoc->initialized = 0; + intern->ptdoc->initialized = false; intern->ptdoc->errbuf = emalloc(sizeof(TidyBuffer)); tidyBufInit(intern->ptdoc->errbuf); @@ -814,7 +814,7 @@ static int php_tidy_parse_string(PHPTidyObj *obj, const char *string, uint32_t l } } - obj->ptdoc->initialized = 1; + obj->ptdoc->initialized = true; tidyBufInit(&buf); tidyBufAttach(&buf, (byte *) string, len); From b7db5c812973cc1603f27d5b7d3f4f4d2de3962d Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Thu, 5 Jun 2025 12:49:30 +0100 Subject: [PATCH 025/682] ext/tidy: Remove TIDY_SET_CONTEXT macro --- ext/tidy/tidy.c | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/ext/tidy/tidy.c b/ext/tidy/tidy.c index 972c972e56e2d..f794b58ff2b29 100644 --- a/ext/tidy/tidy.c +++ b/ext/tidy/tidy.c @@ -50,9 +50,6 @@ /* {{{ ext/tidy macros */ #define FIX_BUFFER(bptr) do { if ((bptr)->size) { (bptr)->bp[(bptr)->size-1] = '\0'; } } while(0) -#define TIDY_SET_CONTEXT \ - zval *object = getThis(); - #define TIDY_FETCH_OBJECT \ PHPTidyObj *obj; \ zval *object; \ @@ -70,11 +67,10 @@ #define TIDY_FETCH_ONLY_OBJECT \ PHPTidyObj *obj; \ - TIDY_SET_CONTEXT; \ if (zend_parse_parameters_none() != SUCCESS) { \ RETURN_THROWS(); \ } \ - obj = Z_TIDY_P(object); \ + obj = Z_TIDY_P(ZEND_THIS); \ #define TIDY_SET_DEFAULT_CONFIG(_doc) \ if (TG(default_config) && TG(default_config)[0]) { \ @@ -1368,8 +1364,7 @@ PHP_METHOD(tidy, __construct) Z_PARAM_BOOL(use_include_path) ZEND_PARSE_PARAMETERS_END(); - TIDY_SET_CONTEXT; - obj = Z_TIDY_P(object); + obj = Z_TIDY_P(ZEND_THIS); if (inputfile) { if (!(contents = php_tidy_file_to_mem(ZSTR_VAL(inputfile), use_include_path))) { @@ -1415,8 +1410,7 @@ PHP_METHOD(tidy, parseFile) Z_PARAM_BOOL(use_include_path) ZEND_PARSE_PARAMETERS_END(); - TIDY_SET_CONTEXT; - obj = Z_TIDY_P(object); + obj = Z_TIDY_P(ZEND_THIS); if (!(contents = php_tidy_file_to_mem(ZSTR_VAL(inputfile), use_include_path))) { php_error_docref(NULL, E_WARNING, "Cannot load \"%s\" into memory%s", ZSTR_VAL(inputfile), (use_include_path) ? " (using include path)" : ""); @@ -1455,8 +1449,7 @@ PHP_METHOD(tidy, parseString) RETURN_THROWS(); } - TIDY_SET_CONTEXT; - obj = Z_TIDY_P(object); + obj = Z_TIDY_P(ZEND_THIS); RETURN_BOOL(php_tidy_apply_config(obj->ptdoc->doc, options_str, options_ht, 2) == SUCCESS && php_tidy_parse_string(obj, ZSTR_VAL(input), (uint32_t)ZSTR_LEN(input), enc) == SUCCESS); From 4fd91c6fa4cd7d6ceefb2e519670c94827b5f1db Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Thu, 5 Jun 2025 12:50:38 +0100 Subject: [PATCH 026/682] ext/tidy: Use RETURN_THROWS() macro --- ext/tidy/tidy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/tidy/tidy.c b/ext/tidy/tidy.c index f794b58ff2b29..d19870013acbc 100644 --- a/ext/tidy/tidy.c +++ b/ext/tidy/tidy.c @@ -62,7 +62,7 @@ TIDY_FETCH_OBJECT; \ if (!obj->ptdoc->initialized) { \ zend_throw_error(NULL, "tidy object is not initialized"); \ - return; \ + RETURN_THROWS(); \ } #define TIDY_FETCH_ONLY_OBJECT \ From bf3d7d1f9a4bb37d35c4cdcc64ed39c4532cac09 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Thu, 5 Jun 2025 12:51:13 +0100 Subject: [PATCH 027/682] ext/tidy: Use RETURN_STR() instead of RETVAL_STR() + return; --- ext/tidy/tidy.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ext/tidy/tidy.c b/ext/tidy/tidy.c index d19870013acbc..d7c409f592802 100644 --- a/ext/tidy/tidy.c +++ b/ext/tidy/tidy.c @@ -1323,8 +1323,7 @@ PHP_FUNCTION(tidy_getopt) optval = php_tidy_get_opt_val(obj->ptdoc, opt, &optt); switch (optt) { case TidyString: - RETVAL_STR((zend_string*)optval); - return; + RETURN_STR((zend_string*)optval); case TidyInteger: RETURN_LONG((zend_long)optval); From 25057811a575e4a6fa9e0d1c2c4181572f8acd17 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Thu, 5 Jun 2025 12:56:32 +0100 Subject: [PATCH 028/682] ext/tidy: Use RETURN_BOOL() where possible --- ext/tidy/tidy.c | 48 ++++++++---------------------------------------- 1 file changed, 8 insertions(+), 40 deletions(-) diff --git a/ext/tidy/tidy.c b/ext/tidy/tidy.c index d7c409f592802..0dd08495f9230 100644 --- a/ext/tidy/tidy.c +++ b/ext/tidy/tidy.c @@ -1330,11 +1330,7 @@ PHP_FUNCTION(tidy_getopt) break; case TidyBoolean: - if (optval) { - RETURN_TRUE; - } else { - RETURN_FALSE; - } + RETURN_BOOL(optval); break; default: @@ -1488,11 +1484,7 @@ PHP_METHOD(tidyNode, hasChildren) { TIDY_FETCH_ONLY_OBJECT; - if (tidyGetChild(obj->node)) { - RETURN_TRUE; - } else { - RETURN_FALSE; - } + RETURN_BOOL(tidyGetChild(obj->node)); } /* }}} */ @@ -1501,11 +1493,7 @@ PHP_METHOD(tidyNode, hasSiblings) { TIDY_FETCH_ONLY_OBJECT; - if (obj->node && tidyGetNext(obj->node)) { - RETURN_TRUE; - } else { - RETURN_FALSE; - } + RETURN_BOOL(obj->node && tidyGetNext(obj->node)); } /* }}} */ @@ -1514,11 +1502,7 @@ PHP_METHOD(tidyNode, isComment) { TIDY_FETCH_ONLY_OBJECT; - if (tidyNodeGetType(obj->node) == TidyNode_Comment) { - RETURN_TRUE; - } else { - RETURN_FALSE; - } + RETURN_BOOL(tidyNodeGetType(obj->node) == TidyNode_Comment); } /* }}} */ @@ -1543,11 +1527,7 @@ PHP_METHOD(tidyNode, isText) { TIDY_FETCH_ONLY_OBJECT; - if (tidyNodeGetType(obj->node) == TidyNode_Text) { - RETURN_TRUE; - } else { - RETURN_FALSE; - } + RETURN_BOOL(tidyNodeGetType(obj->node) == TidyNode_Text); } /* }}} */ @@ -1556,11 +1536,7 @@ PHP_METHOD(tidyNode, isJste) { TIDY_FETCH_ONLY_OBJECT; - if (tidyNodeGetType(obj->node) == TidyNode_Jste) { - RETURN_TRUE; - } else { - RETURN_FALSE; - } + RETURN_BOOL(tidyNodeGetType(obj->node) == TidyNode_Jste); } /* }}} */ @@ -1569,11 +1545,7 @@ PHP_METHOD(tidyNode, isAsp) { TIDY_FETCH_ONLY_OBJECT; - if (tidyNodeGetType(obj->node) == TidyNode_Asp) { - RETURN_TRUE; - } else { - RETURN_FALSE; - } + RETURN_BOOL(tidyNodeGetType(obj->node) == TidyNode_Asp); } /* }}} */ @@ -1582,11 +1554,7 @@ PHP_METHOD(tidyNode, isPhp) { TIDY_FETCH_ONLY_OBJECT; - if (tidyNodeGetType(obj->node) == TidyNode_Php) { - RETURN_TRUE; - } else { - RETURN_FALSE; - } + RETURN_BOOL(tidyNodeGetType(obj->node) == TidyNode_Php); } /* }}} */ From feff5381b4aa792920f9f5dad07ee064319f0615 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Thu, 5 Jun 2025 13:32:25 +0100 Subject: [PATCH 029/682] ext/tidy: Reduce scope of variable and rename to prevent variable shadowing --- ext/tidy/tidy.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/ext/tidy/tidy.c b/ext/tidy/tidy.c index 0dd08495f9230..fbd9ae102e27e 100644 --- a/ext/tidy/tidy.c +++ b/ext/tidy/tidy.c @@ -666,17 +666,16 @@ static void tidy_add_node_default_properties(PHPTidyObj *obj) tempattr = tidyAttrFirst(obj->node); if (tempattr) { - const char *name, *val; array_init(&attribute); do { - name = (const char *)tidyAttrName(tempattr); - val = (const char *)tidyAttrValue(tempattr); - if (name) { + const char *attr_name = tidyAttrName(tempattr); + if (attr_name) { + const char *val = tidyAttrValue(tempattr); if (val) { - add_assoc_string(&attribute, name, val); + add_assoc_string(&attribute, attr_name, val); } else { - add_assoc_str(&attribute, name, zend_empty_string); + add_assoc_str(&attribute, attr_name, zend_empty_string); } } } while((tempattr = tidyAttrNext(tempattr))); From d97a90acb87173a008626a26dfd8d7e49e8bdad8 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Thu, 5 Jun 2025 13:37:57 +0100 Subject: [PATCH 030/682] ext/tidy: Use zend_result type instead of int type --- ext/tidy/tidy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/tidy/tidy.c b/ext/tidy/tidy.c index fbd9ae102e27e..426db1ee09f85 100644 --- a/ext/tidy/tidy.c +++ b/ext/tidy/tidy.c @@ -798,7 +798,7 @@ static zend_result _php_tidy_apply_config_array(TidyDoc doc, const HashTable *ht } } -static int php_tidy_parse_string(PHPTidyObj *obj, const char *string, uint32_t len, const char *enc) +static zend_result php_tidy_parse_string(PHPTidyObj *obj, const char *string, uint32_t len, const char *enc) { TidyBuffer buf; From 19b1173577a987533b5bd6eec42644315d4bb69b Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Thu, 5 Jun 2025 13:34:28 +0100 Subject: [PATCH 031/682] ext/tidy: Add some const qualifiers --- ext/tidy/tidy.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ext/tidy/tidy.c b/ext/tidy/tidy.c index 426db1ee09f85..18fcc1bfdce68 100644 --- a/ext/tidy/tidy.c +++ b/ext/tidy/tidy.c @@ -124,7 +124,7 @@ static zend_result tidy_doc_cast_handler(zend_object *, zval *, int); static zend_result tidy_node_cast_handler(zend_object *, zval *, int); static void tidy_doc_update_properties(PHPTidyObj *); static void tidy_add_node_default_properties(PHPTidyObj *); -static void *php_tidy_get_opt_val(PHPTidyDoc *, TidyOption, TidyOptionType *); +static void *php_tidy_get_opt_val(const PHPTidyDoc *, TidyOption, TidyOptionType *); static void php_tidy_create_node(INTERNAL_FUNCTION_PARAMETERS, tidy_base_nodetypes); static zend_result _php_tidy_set_tidy_opt(TidyDoc, const char *, zval *, uint32_t arg); static zend_result _php_tidy_apply_config_array(TidyDoc doc, const HashTable *ht_options, uint32_t arg); @@ -417,7 +417,7 @@ static void tidy_object_free_storage(zend_object *object) } } -static zend_object *tidy_object_new(zend_class_entry *class_type, zend_object_handlers *handlers, tidy_obj_type objtype) +static zend_object *tidy_object_new(zend_class_entry *class_type, const zend_object_handlers *handlers, tidy_obj_type objtype) { PHPTidyObj *intern; @@ -718,13 +718,13 @@ static void tidy_add_node_default_properties(PHPTidyObj *obj) zval_ptr_dtor(&children); } -static void *php_tidy_get_opt_val(PHPTidyDoc *ptdoc, TidyOption opt, TidyOptionType *type) +static void *php_tidy_get_opt_val(const PHPTidyDoc *ptdoc, TidyOption opt, TidyOptionType *type) { *type = tidyOptGetType(opt); switch (*type) { case TidyString: { - char *val = (char *) tidyOptGetValue(ptdoc->doc, tidyOptGetId(opt)); + const char *val = tidyOptGetValue(ptdoc->doc, tidyOptGetId(opt)); if (val) { return (void *) zend_string_init(val, strlen(val), 0); } else { From 4162c2078763acfc692cae12e3fbd0807ad2e003 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Thu, 5 Jun 2025 21:51:30 +0200 Subject: [PATCH 032/682] Fix compile without ZEND_MM_STORAGE --- Zend/zend_alloc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Zend/zend_alloc.c b/Zend/zend_alloc.c index d531270d445ed..573fd5fa26b80 100644 --- a/Zend/zend_alloc.c +++ b/Zend/zend_alloc.c @@ -3043,7 +3043,7 @@ ZEND_API zend_mm_storage *zend_mm_get_storage(zend_mm_heap *heap) #if ZEND_MM_STORAGE return heap->storage; #else - return NULL + return NULL; #endif } From 8f3cdf6236bf1f22e9e4328e0a1e19f6f0e0d0c7 Mon Sep 17 00:00:00 2001 From: DanielEScherzer Date: Thu, 5 Jun 2025 14:46:46 -0700 Subject: [PATCH 033/682] gen_stub: Add support for attributes on constants in stubs (#18735) Update to PHP-Parser 5.5.0 and add support for attributes on constants in stubs. For now, I have only migrated over E_STRICT, once the support is in place I'll do a larger migration of the existing deprecated constants. In the process, fix the logic in `copy_zend_constant()` for copying attributes when a constant is copied; just increase the reference count for the attributes table rather than trying to duplicate the contents. --- Zend/tests/e_strict-deprecated.phpt | 2 +- Zend/zend_attributes.h | 7 ++ Zend/zend_constants.c | 3 +- Zend/zend_constants.stub.php | 2 +- Zend/zend_constants_arginfo.h | 16 ++++- build/gen_stub.php | 68 ++++++++++++++++--- ...ectionConstant_getAttributes_internal.phpt | 17 +++++ ext/zend_test/test.stub.php | 6 ++ ext/zend_test/test_arginfo.h | 19 +++++- ext/zend_test/tests/attribute-deprecated.phpt | 15 ++++ ext/zend_test/tests/gh11423.phpt | 4 +- 11 files changed, 143 insertions(+), 16 deletions(-) create mode 100644 ext/reflection/tests/ReflectionConstant_getAttributes_internal.phpt diff --git a/Zend/tests/e_strict-deprecated.phpt b/Zend/tests/e_strict-deprecated.phpt index 71666e75c22f4..5017d45a03a1d 100644 --- a/Zend/tests/e_strict-deprecated.phpt +++ b/Zend/tests/e_strict-deprecated.phpt @@ -10,5 +10,5 @@ var_dump(E_STRICT); --EXPECTF-- int(30719) -Deprecated: Constant E_STRICT is deprecated in %s on line %d +Deprecated: Constant E_STRICT is deprecated since 8.4, the error level was removed in %s on line %d int(2048) diff --git a/Zend/zend_attributes.h b/Zend/zend_attributes.h index eb464772c100c..a4d6b28c0094a 100644 --- a/Zend/zend_attributes.h +++ b/Zend/zend_attributes.h @@ -21,6 +21,7 @@ #define ZEND_ATTRIBUTES_H #include "zend_compile.h" +#include "zend_constants.h" #define ZEND_ATTRIBUTE_TARGET_CLASS (1<<0) #define ZEND_ATTRIBUTE_TARGET_FUNCTION (1<<1) @@ -126,6 +127,12 @@ static zend_always_inline zend_attribute *zend_add_class_constant_attribute(zend return zend_add_attribute(&c->attributes, name, argc, flags, 0, 0); } +static zend_always_inline zend_attribute *zend_add_global_constant_attribute(zend_constant *c, zend_string *name, uint32_t argc) +{ + uint32_t flags = ZEND_CONSTANT_MODULE_NUMBER(c) == PHP_USER_CONSTANT ? 0 : ZEND_ATTRIBUTE_PERSISTENT; + return zend_add_attribute(&c->attributes, name, argc, flags, 0, 0); +} + void zend_register_attribute_ce(void); void zend_attributes_shutdown(void); diff --git a/Zend/zend_constants.c b/Zend/zend_constants.c index 9a184bd931584..ffad8315ae40d 100644 --- a/Zend/zend_constants.c +++ b/Zend/zend_constants.c @@ -85,7 +85,8 @@ static void copy_zend_constant(zval *zv) c->filename = zend_string_copy(c->filename); } if (c->attributes != NULL) { - c->attributes = zend_array_dup(c->attributes); + // Use the same attributes table + GC_ADDREF(c->attributes); } if (Z_TYPE(c->value) == IS_STRING) { Z_STR(c->value) = zend_string_dup(Z_STR(c->value), 1); diff --git a/Zend/zend_constants.stub.php b/Zend/zend_constants.stub.php index ee67966c0151c..763b207641f69 100644 --- a/Zend/zend_constants.stub.php +++ b/Zend/zend_constants.stub.php @@ -71,9 +71,9 @@ /** * @var int * @cvalue E_STRICT - * @deprecated * @todo Remove in PHP 9.0 */ +#[\Deprecated(since: '8.4', message: 'the error level was removed')] const E_STRICT = UNKNOWN; /** diff --git a/Zend/zend_constants_arginfo.h b/Zend/zend_constants_arginfo.h index 8d42345ad69bc..d381bcf64ee18 100644 --- a/Zend/zend_constants_arginfo.h +++ b/Zend/zend_constants_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 65be08c1bdace83ad1fa1175fc824262e07eac2a */ + * Stub hash: 5e224893a5fb72b3f93249235c2a1634233ce505 */ static void register_zend_constants_symbols(int module_number) { @@ -26,4 +26,18 @@ static void register_zend_constants_symbols(int module_number) REGISTER_BOOL_CONSTANT("TRUE", true, CONST_PERSISTENT); REGISTER_BOOL_CONSTANT("FALSE", false, CONST_PERSISTENT); REGISTER_NULL_CONSTANT("NULL", CONST_PERSISTENT); + + zend_constant *const_E_STRICT = zend_hash_str_find_ptr(EG(zend_constants), "E_STRICT", sizeof("E_STRICT") - 1); + + zend_attribute *attribute_Deprecated_const_E_STRICT_0 = zend_add_global_constant_attribute(const_E_STRICT, ZSTR_KNOWN(ZEND_STR_DEPRECATED_CAPITALIZED), 2); + zval attribute_Deprecated_const_E_STRICT_0_arg0; + zend_string *attribute_Deprecated_const_E_STRICT_0_arg0_str = zend_string_init("8.4", strlen("8.4"), 1); + ZVAL_STR(&attribute_Deprecated_const_E_STRICT_0_arg0, attribute_Deprecated_const_E_STRICT_0_arg0_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_E_STRICT_0->args[0].value, &attribute_Deprecated_const_E_STRICT_0_arg0); + attribute_Deprecated_const_E_STRICT_0->args[0].name = ZSTR_KNOWN(ZEND_STR_SINCE); + zval attribute_Deprecated_const_E_STRICT_0_arg1; + zend_string *attribute_Deprecated_const_E_STRICT_0_arg1_str = zend_string_init("the error level was removed", strlen("the error level was removed"), 1); + ZVAL_STR(&attribute_Deprecated_const_E_STRICT_0_arg1, attribute_Deprecated_const_E_STRICT_0_arg1_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_E_STRICT_0->args[1].value, &attribute_Deprecated_const_E_STRICT_0_arg1); + attribute_Deprecated_const_E_STRICT_0->args[1].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); } diff --git a/build/gen_stub.php b/build/gen_stub.php index 5e06a53172e07..13ef9e60f334d 100755 --- a/build/gen_stub.php +++ b/build/gen_stub.php @@ -2622,6 +2622,13 @@ public function __construct( ?ExposedDocComment $exposedDocComment, bool $isFileCacheAllowed ) { + foreach ($attributes as $attr) { + if ($attr->class === "Deprecated") { + $isDeprecated = true; + break; + } + } + $this->name = $name; $this->value = $value; $this->valueString = $valueString; @@ -2915,17 +2922,11 @@ protected function getFlagsByPhpVersion(): array { $flags = parent::getFlagsByPhpVersion(); + // $this->isDeprecated also accounts for any #[\Deprecated] attributes if ($this->isDeprecated) { $flags = $this->addFlagForVersionsAbove($flags, "ZEND_ACC_DEPRECATED", PHP_80_VERSION_ID); } - foreach ($this->attributes as $attr) { - if ($attr->class === "Deprecated") { - $flags = $this->addFlagForVersionsAbove($flags, "ZEND_ACC_DEPRECATED", PHP_80_VERSION_ID); - break; - } - } - if ($this->flags & Modifiers::FINAL) { $flags = $this->addFlagForVersionsAbove($flags, "ZEND_ACC_FINAL", PHP_81_VERSION_ID); } @@ -4340,7 +4341,7 @@ private function handleStatements(array $stmts, PrettyPrinterAbstract $prettyPri $cond, $this->isUndocumentable, $this->getMinimumPhpVersionIdCompatibility(), - [] + AttributeInfo::createFromGroups($stmt->attrGroups) ); } continue; @@ -5177,7 +5178,9 @@ static function (FuncInfo $funcInfo) use ($fileInfo, &$generatedFunctionDeclarat $php80MinimumCompatibility = $fileInfo->getMinimumPhpVersionIdCompatibility() === null || $fileInfo->getMinimumPhpVersionIdCompatibility() >= PHP_80_VERSION_ID; if ($fileInfo->generateClassEntries) { - if ($attributeInitializationCode = generateFunctionAttributeInitialization($fileInfo->funcInfos, $allConstInfos, $fileInfo->getMinimumPhpVersionIdCompatibility(), null)) { + $attributeInitializationCode = generateFunctionAttributeInitialization($fileInfo->funcInfos, $allConstInfos, $fileInfo->getMinimumPhpVersionIdCompatibility(), null); + $attributeInitializationCode .= generateGlobalConstantAttributeInitialization($fileInfo->constInfos, $allConstInfos, $fileInfo->getMinimumPhpVersionIdCompatibility(), null); + if ($attributeInitializationCode) { if (!$php80MinimumCompatibility) { $attributeInitializationCode = "\n#if (PHP_VERSION_ID >= " . PHP_80_VERSION_ID . ")" . $attributeInitializationCode . "#endif\n"; } @@ -5289,6 +5292,51 @@ static function (FuncInfo $funcInfo) use ($allConstInfos, $phpVersionIdMinimumCo ); } +/** + * @param iterable $constInfos + * @param array $allConstInfos + */ +function generateGlobalConstantAttributeInitialization( + iterable $constInfos, + array $allConstInfos, + ?int $phpVersionIdMinimumCompatibility, + ?string $parentCond = null +): string { + $isConditional = false; + if ($phpVersionIdMinimumCompatibility !== null && $phpVersionIdMinimumCompatibility < PHP_85_VERSION_ID) { + $isConditional = true; + $phpVersionIdMinimumCompatibility = PHP_85_VERSION_ID; + } + $code = generateCodeWithConditions( + $constInfos, + "", + static function (ConstInfo $constInfo) use ($allConstInfos, $phpVersionIdMinimumCompatibility) { + if ($constInfo->attributes === []) { + return null; + } + $constName = str_replace('\\', '\\\\', $constInfo->name->__toString()); + $constVarName = 'const_' . $constName; + + $code .= "\tzend_constant *$constVarName = zend_hash_str_find_ptr(EG(zend_constants), \"" . $constName . "\", sizeof(\"" . $constName . "\") - 1);\n"; + foreach ($constInfo->attributes as $key => $attribute) { + $code .= $attribute->generateCode( + "zend_add_global_constant_attribute($constVarName", + $constVarName . "_$key", + $allConstInfos, + $phpVersionIdMinimumCompatibility + ); + } + + return $code; + }, + $parentCond + ); + if ($code && $isConditional) { + return "\n#if (PHP_VERSION_ID >= " . PHP_85_VERSION_ID . ")\n" . $code . "#endif\n"; + } + return $code; +} + /** * @param iterable $constInfos * @param array $allConstInfos @@ -6030,7 +6078,7 @@ function initPhpParser() { } $isInitialized = true; - $version = "5.3.1"; + $version = "5.5.0"; $phpParserDir = __DIR__ . "/PHP-Parser-$version"; if (!is_dir($phpParserDir)) { installPhpParser($version, $phpParserDir); diff --git a/ext/reflection/tests/ReflectionConstant_getAttributes_internal.phpt b/ext/reflection/tests/ReflectionConstant_getAttributes_internal.phpt new file mode 100644 index 0000000000000..ff96fa7cadb58 --- /dev/null +++ b/ext/reflection/tests/ReflectionConstant_getAttributes_internal.phpt @@ -0,0 +1,17 @@ +--TEST-- +ReflectionConstant::getAttributes() with attribute (internal constant) +--FILE-- +getAttributes()); + +?> +--EXPECTF-- +array(1) { + [0]=> + object(ReflectionAttribute)#%d (1) { + ["name"]=> + string(10) "Deprecated" + } +} diff --git a/ext/zend_test/test.stub.php b/ext/zend_test/test.stub.php index 9923da6dceea0..b1debcae74030 100644 --- a/ext/zend_test/test.stub.php +++ b/ext/zend_test/test.stub.php @@ -17,6 +17,12 @@ /** @var string */ const ZEND_CONSTANT_A = "global"; + /** + * @var int + */ + #[\Deprecated(message: "use something else", since: "version 1.5")] + const ZEND_TEST_ATTRIBUTED_CONSTANT = 42; + interface _ZendTestInterface { /** @var int */ diff --git a/ext/zend_test/test_arginfo.h b/ext/zend_test/test_arginfo.h index d0d12eb66bab6..c49c032013e38 100644 --- a/ext/zend_test/test_arginfo.h +++ b/ext/zend_test/test_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 1fd4c80ed74efcc50698748b2afc89391ed69c72 */ + * Stub hash: 37ac76dddea2da24d3275cccc748b8fea4c8d09c */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_array_return, 0, 0, IS_ARRAY, 0) ZEND_END_ARG_INFO() @@ -579,6 +579,7 @@ static void register_test_symbols(int module_number) { REGISTER_LONG_CONSTANT("ZEND_TEST_DEPRECATED", 42, CONST_PERSISTENT | CONST_DEPRECATED); REGISTER_STRING_CONSTANT("ZEND_CONSTANT_A", "global", CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("ZEND_TEST_ATTRIBUTED_CONSTANT", 42, CONST_PERSISTENT | CONST_DEPRECATED); REGISTER_STRING_CONSTANT("ZendTestNS2\\ZEND_CONSTANT_A", "namespaced", CONST_PERSISTENT); REGISTER_STRING_CONSTANT("ZendTestNS2\\ZendSubNS\\ZEND_CONSTANT_A", "namespaced", CONST_PERSISTENT); @@ -635,6 +636,22 @@ static void register_test_symbols(int module_number) ZVAL_STR(&attribute_ZendTestAttributeWithArguments_func_zend_test_attribute_with_named_argument_0_arg0, attribute_ZendTestAttributeWithArguments_func_zend_test_attribute_with_named_argument_0_arg0_str); ZVAL_COPY_VALUE(&attribute_ZendTestAttributeWithArguments_func_zend_test_attribute_with_named_argument_0->args[0].value, &attribute_ZendTestAttributeWithArguments_func_zend_test_attribute_with_named_argument_0_arg0); attribute_ZendTestAttributeWithArguments_func_zend_test_attribute_with_named_argument_0->args[0].name = zend_string_init_interned("arg", sizeof("arg") - 1, 1); + +#if (PHP_VERSION_ID >= 80500) + zend_constant *const_ZEND_TEST_ATTRIBUTED_CONSTANT = zend_hash_str_find_ptr(EG(zend_constants), "ZEND_TEST_ATTRIBUTED_CONSTANT", sizeof("ZEND_TEST_ATTRIBUTED_CONSTANT") - 1); + + zend_attribute *attribute_Deprecated_const_ZEND_TEST_ATTRIBUTED_CONSTANT_0 = zend_add_global_constant_attribute(const_ZEND_TEST_ATTRIBUTED_CONSTANT, ZSTR_KNOWN(ZEND_STR_DEPRECATED_CAPITALIZED), 2); + zval attribute_Deprecated_const_ZEND_TEST_ATTRIBUTED_CONSTANT_0_arg0; + zend_string *attribute_Deprecated_const_ZEND_TEST_ATTRIBUTED_CONSTANT_0_arg0_str = zend_string_init("use something else", strlen("use something else"), 1); + ZVAL_STR(&attribute_Deprecated_const_ZEND_TEST_ATTRIBUTED_CONSTANT_0_arg0, attribute_Deprecated_const_ZEND_TEST_ATTRIBUTED_CONSTANT_0_arg0_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_ZEND_TEST_ATTRIBUTED_CONSTANT_0->args[0].value, &attribute_Deprecated_const_ZEND_TEST_ATTRIBUTED_CONSTANT_0_arg0); + attribute_Deprecated_const_ZEND_TEST_ATTRIBUTED_CONSTANT_0->args[0].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); + zval attribute_Deprecated_const_ZEND_TEST_ATTRIBUTED_CONSTANT_0_arg1; + zend_string *attribute_Deprecated_const_ZEND_TEST_ATTRIBUTED_CONSTANT_0_arg1_str = zend_string_init("version 1.5", strlen("version 1.5"), 1); + ZVAL_STR(&attribute_Deprecated_const_ZEND_TEST_ATTRIBUTED_CONSTANT_0_arg1, attribute_Deprecated_const_ZEND_TEST_ATTRIBUTED_CONSTANT_0_arg1_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_ZEND_TEST_ATTRIBUTED_CONSTANT_0->args[1].value, &attribute_Deprecated_const_ZEND_TEST_ATTRIBUTED_CONSTANT_0_arg1); + attribute_Deprecated_const_ZEND_TEST_ATTRIBUTED_CONSTANT_0->args[1].name = ZSTR_KNOWN(ZEND_STR_SINCE); +#endif } static zend_class_entry *register_class__ZendTestInterface(void) diff --git a/ext/zend_test/tests/attribute-deprecated.phpt b/ext/zend_test/tests/attribute-deprecated.phpt index a52d0e635c655..e076065cc91d2 100644 --- a/ext/zend_test/tests/attribute-deprecated.phpt +++ b/ext/zend_test/tests/attribute-deprecated.phpt @@ -17,6 +17,12 @@ $reflection = new ReflectionClassConstant('_ZendTestClass', 'ZEND_TEST_DEPRECATE var_dump($reflection->getAttributes()[0]->newInstance()); var_dump($reflection->isDeprecated()); +ZEND_TEST_ATTRIBUTED_CONSTANT; + +$reflection = new ReflectionConstant('ZEND_TEST_ATTRIBUTED_CONSTANT'); +var_dump($reflection->getAttributes()[0]->newInstance()); +var_dump($reflection->isDeprecated()); + ?> --EXPECTF-- Deprecated: Function zend_test_deprecated() is deprecated in %s on line %d @@ -38,3 +44,12 @@ object(Deprecated)#%d (2) { NULL } bool(true) + +Deprecated: Constant ZEND_TEST_ATTRIBUTED_CONSTANT is deprecated since version 1.5, use something else in %s on line %d +object(Deprecated)#%d (2) { + ["message"]=> + string(18) "use something else" + ["since"]=> + string(11) "version 1.5" +} +bool(true) diff --git a/ext/zend_test/tests/gh11423.phpt b/ext/zend_test/tests/gh11423.phpt index fd394126075df..7f70ee33015de 100644 --- a/ext/zend_test/tests/gh11423.phpt +++ b/ext/zend_test/tests/gh11423.phpt @@ -13,11 +13,13 @@ var_dump(get_defined_constants(true)["user"]); ?> --EXPECT-- -array(4) { +array(5) { ["ZEND_TEST_DEPRECATED"]=> int(42) ["ZEND_CONSTANT_A"]=> string(6) "global" + ["ZEND_TEST_ATTRIBUTED_CONSTANT"]=> + int(42) ["ZendTestNS2\ZEND_CONSTANT_A"]=> string(10) "namespaced" ["ZendTestNS2\ZendSubNS\ZEND_CONSTANT_A"]=> From 2751064692df1192cb4c4212025139b2797dd879 Mon Sep 17 00:00:00 2001 From: Saki Takamachi <34942839+SakiTakamachi@users.noreply.github.com> Date: Fri, 6 Jun 2025 08:25:52 +0900 Subject: [PATCH 034/682] use XSSE for bcmath (#18770) --- ext/bcmath/libbcmath/src/convert.c | 20 +- ext/bcmath/libbcmath/src/simd.h | 59 - ext/bcmath/libbcmath/src/str2num.c | 36 +- ext/bcmath/libbcmath/src/xsse.h | 1965 ++++++++++++++++++++++++++++ 4 files changed, 1993 insertions(+), 87 deletions(-) delete mode 100644 ext/bcmath/libbcmath/src/simd.h create mode 100644 ext/bcmath/libbcmath/src/xsse.h diff --git a/ext/bcmath/libbcmath/src/convert.c b/ext/bcmath/libbcmath/src/convert.c index 5438b4c1c44e5..ca5b84efde1b2 100644 --- a/ext/bcmath/libbcmath/src/convert.c +++ b/ext/bcmath/libbcmath/src/convert.c @@ -17,22 +17,22 @@ #include "bcmath.h" #include "convert.h" #include "private.h" -#include "simd.h" +#include "xsse.h" char *bc_copy_and_toggle_bcd(char *restrict dest, const char *source, const char *source_end) { const size_t bulk_shift = SWAR_REPEAT('0'); -#ifdef HAVE_BC_SIMD_128 +#ifdef XSSE2 /* SIMD SSE2 or NEON bulk shift + copy */ - bc_simd_128_t shift_vector = bc_simd_set_8x16('0'); - while (source + sizeof(bc_simd_128_t) <= source_end) { - bc_simd_128_t bytes = bc_simd_load_8x16((const bc_simd_128_t *) source); - bytes = bc_simd_xor_8x16(bytes, shift_vector); - bc_simd_store_8x16((bc_simd_128_t *) dest, bytes); - - source += sizeof(bc_simd_128_t); - dest += sizeof(bc_simd_128_t); + __m128i shift_vector = _mm_set1_epi8('0'); + while (source + sizeof(__m128i) <= source_end) { + __m128i bytes = _mm_loadu_si128((const __m128i *) source); + bytes = _mm_xor_si128(bytes, shift_vector); + _mm_storeu_si128((__m128i *) dest, bytes); + + source += sizeof(__m128i); + dest += sizeof(__m128i); } #endif diff --git a/ext/bcmath/libbcmath/src/simd.h b/ext/bcmath/libbcmath/src/simd.h deleted file mode 100644 index af38f8349618c..0000000000000 --- a/ext/bcmath/libbcmath/src/simd.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Copyright (c) The PHP Group | - +----------------------------------------------------------------------+ - | This source file is subject to version 3.01 of the PHP license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | https://www.php.net/license/3_01.txt | - | If you did not receive a copy of the PHP license and are unable to | - | obtain it through the world-wide-web, please send a note to | - | license@php.net so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Authors: Saki Takamachi | - +----------------------------------------------------------------------+ -*/ - - -#ifndef _BCMATH_SIMD_H_ -#define _BCMATH_SIMD_H_ - -#ifdef __SSE2__ -# include - typedef __m128i bc_simd_128_t; -# define HAVE_BC_SIMD_128 -# define bc_simd_set_8x16(x) _mm_set1_epi8(x) -# define bc_simd_load_8x16(ptr) _mm_loadu_si128((const __m128i *) (ptr)) -# define bc_simd_xor_8x16(a, b) _mm_xor_si128(a, b) -# define bc_simd_store_8x16(ptr, val) _mm_storeu_si128((__m128i *) (ptr), val) -# define bc_simd_add_8x16(a, b) _mm_add_epi8(a, b) -# define bc_simd_cmpeq_8x16(a, b) _mm_cmpeq_epi8(a, b) -# define bc_simd_cmplt_8x16(a, b) _mm_cmplt_epi8(a, b) -# define bc_simd_movemask_8x16(a) _mm_movemask_epi8(a) - -#elif defined(__aarch64__) || defined(_M_ARM64) -# include - typedef int8x16_t bc_simd_128_t; -# define HAVE_BC_SIMD_128 -# define bc_simd_set_8x16(x) vdupq_n_s8(x) -# define bc_simd_load_8x16(ptr) vld1q_s8((const int8_t *) (ptr)) -# define bc_simd_xor_8x16(a, b) veorq_s8(a, b) -# define bc_simd_store_8x16(ptr, val) vst1q_s8((int8_t *) (ptr), val) -# define bc_simd_add_8x16(a, b) vaddq_s8(a, b) -# define bc_simd_cmpeq_8x16(a, b) (vreinterpretq_s8_u8(vceqq_s8(a, b))) -# define bc_simd_cmplt_8x16(a, b) (vreinterpretq_s8_u8(vcltq_s8(a, b))) - static inline int bc_simd_movemask_8x16(int8x16_t vec) - { - /** - * based on code from - * https://community.arm.com/arm-community-blogs/b/servers-and-cloud-computing-blog/posts/porting-x86-vector-bitmask-optimizations-to-arm-neon - */ - uint16x8_t high_bits = vreinterpretq_u16_u8(vshrq_n_u8(vreinterpretq_u8_s8(vec), 7)); - uint32x4_t paired16 = vreinterpretq_u32_u16(vsraq_n_u16(high_bits, high_bits, 7)); - uint64x2_t paired32 = vreinterpretq_u64_u32(vsraq_n_u32(paired16, paired16, 14)); - uint8x16_t paired64 = vreinterpretq_u8_u64(vsraq_n_u64(paired32, paired32, 28)); - return vgetq_lane_u8(paired64, 0) | ((int) vgetq_lane_u8(paired64, 8) << 8); - } -#endif - -#endif diff --git a/ext/bcmath/libbcmath/src/str2num.c b/ext/bcmath/libbcmath/src/str2num.c index 945de0cf60003..11d71ae492ad6 100644 --- a/ext/bcmath/libbcmath/src/str2num.c +++ b/ext/bcmath/libbcmath/src/str2num.c @@ -32,7 +32,7 @@ #include "bcmath.h" #include "convert.h" #include "private.h" -#include "simd.h" +#include "xsse.h" #include #include @@ -40,20 +40,20 @@ static inline const char *bc_count_digits(const char *str, const char *end) { /* Process in bulk */ -#ifdef HAVE_BC_SIMD_128 - const bc_simd_128_t offset = bc_simd_set_8x16((signed char) (SCHAR_MIN - '0')); +#ifdef XSSE2 + const __m128i offset = _mm_set1_epi8((signed char) (SCHAR_MIN - '0')); /* we use the less than comparator, so add 1 */ - const bc_simd_128_t threshold = bc_simd_set_8x16(SCHAR_MIN + ('9' + 1 - '0')); + const __m128i threshold = _mm_set1_epi8(SCHAR_MIN + ('9' + 1 - '0')); - while (str + sizeof(bc_simd_128_t) <= end) { - bc_simd_128_t bytes = bc_simd_load_8x16((const bc_simd_128_t *) str); + while (str + sizeof(__m128i) <= end) { + __m128i bytes = _mm_loadu_si128((const __m128i *) str); /* Wrapping-add the offset to the bytes, such that all bytes below '0' are positive and others are negative. * More specifically, '0' will be -128 and '9' will be -119. */ - bytes = bc_simd_add_8x16(bytes, offset); + bytes = _mm_add_epi8(bytes, offset); /* Now mark all bytes that are <= '9', i.e. <= -119, i.e. < -118, i.e. the threshold. */ - bytes = bc_simd_cmplt_8x16(bytes, threshold); + bytes = _mm_cmplt_epi8(bytes, threshold); - int mask = bc_simd_movemask_8x16(bytes); + int mask = _mm_movemask_epi8(bytes); if (mask != 0xffff) { /* At least one of the bytes is not within range. Move to the first offending byte. */ #ifdef PHP_HAVE_BUILTIN_CTZL @@ -63,7 +63,7 @@ static inline const char *bc_count_digits(const char *str, const char *end) #endif } - str += sizeof(bc_simd_128_t); + str += sizeof(__m128i); } #endif @@ -77,19 +77,19 @@ static inline const char *bc_count_digits(const char *str, const char *end) static inline const char *bc_skip_zero_reverse(const char *scanner, const char *stop) { /* Check in bulk */ -#ifdef HAVE_BC_SIMD_128 - const bc_simd_128_t c_zero_repeat = bc_simd_set_8x16('0'); - while (scanner - sizeof(bc_simd_128_t) >= stop) { - scanner -= sizeof(bc_simd_128_t); - bc_simd_128_t bytes = bc_simd_load_8x16((const bc_simd_128_t *) scanner); +#ifdef XSSE2 + const __m128i c_zero_repeat = _mm_set1_epi8('0'); + while (scanner - sizeof(__m128i) >= stop) { + scanner -= sizeof(__m128i); + __m128i bytes = _mm_loadu_si128((const __m128i *) scanner); /* Checks if all numeric strings are equal to '0'. */ - bytes = bc_simd_cmpeq_8x16(bytes, c_zero_repeat); + bytes = _mm_cmpeq_epi8(bytes, c_zero_repeat); - int mask = bc_simd_movemask_8x16(bytes); + int mask = _mm_movemask_epi8(bytes); /* The probability of having 16 trailing 0s in a row is very low, so we use EXPECTED. */ if (EXPECTED(mask != 0xffff)) { /* Move the pointer back and check each character in loop. */ - scanner += sizeof(bc_simd_128_t); + scanner += sizeof(__m128i); break; } } diff --git a/ext/bcmath/libbcmath/src/xsse.h b/ext/bcmath/libbcmath/src/xsse.h new file mode 100644 index 0000000000000..a0d86e335e3f7 --- /dev/null +++ b/ext/bcmath/libbcmath/src/xsse.h @@ -0,0 +1,1965 @@ +/******************************************************************************** + * MIT License + * Copyright (c) 2025 Saki Takamachi + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + ********************************************************************************* + * This is a wrapper library that allows you to use SSE2, SSE3, SSSE3, SSE4.1, + * and SSE4.2 APIs directly with NEON. + * Please note that APIs using `__m64`, `__m128`, or `__m128d` are not supported. + * + * In an SSE environment, if `__SSE2__` is defined, the `XSSE2` macro will be + * defined as well. + * Similarly, `XSSE3` through `XSSE4_2` will also be defined if their + * corresponding features are available.(Note that plain `SSE` is not supported.) + * + * In a NEON environment, all of the macros `XSSE2`, `XSSE3`, `XSSSE3`, + * `XSSE4_1`, and `XSSE4_2` are defined. + *********************************************************************************/ + + +#ifndef XSSE_H +#define XSSE_H + +#define XSSE_VERSION 20000 + +#ifdef _MSC_VER +# define XSSE_FORCE_INLINE __forceinline +# define XSSE_UNREACHABLE() __assume(0) +# define XSSE_EXPECTED(x) (x) +# define XSSE_UNEXPECTED(x) (x) +# define XSSE_ATTR_CONST +# define XSSE_MEMCPY(p, vp, s) memcpy((p), (vp), (s)) +#elif defined(__GNUC__) || defined(__clang__) +# define XSSE_FORCE_INLINE inline __attribute__((always_inline)) +# define XSSE_UNREACHABLE() __builtin_unreachable() +# define XSSE_EXPECTED(x) __builtin_expect(!!(x), 1) +# define XSSE_UNEXPECTED(x) __builtin_expect(!!(x), 0) +# define XSSE_ATTR_CONST __attribute__((const)) +# define XSSE_HAS_MACRO_EXTENSION +# define XSSE_MEMCPY(p, vp, s) __builtin_memcpy((p), (vp), (s)) +# ifdef __OPTIMIZE__ +# define XSSE_IS_OPTIMIZE +# endif +#else +# define XSSE_FORCE_INLINE inline +# define XSSE_UNREACHABLE() do {} while (0) +# define XSSE_EXPECTED(x) (x) +# define XSSE_UNEXPECTED(x) (x) +# define XSSE_ATTR_CONST +# define XSSE_MEMCPY(p, vp, s) memcpy((p), (vp), (s)) +#endif + +#if defined(__GNUC__) && !defined(__clang__) +# define XSSE_ATTR_OPTIMIZE_O2 __attribute__((optimize("O2"))) +#else +# define XSSE_ATTR_OPTIMIZE_O2 +#endif + +#if defined(__aarch64__) || defined(_M_ARM64) +#include +#include +typedef int8x16_t __m128i; +#endif + + +/***************************************************************************** + * * + * SSE2 * + * * + *****************************************************************************/ + +#if defined(__SSE2__) || defined(_M_X64) || defined(_M_AMD64) +#include +#define XSSE2 + + +#elif defined(__aarch64__) || defined(_M_ARM64) +#define XSSE2 + +/***************************************************************************** + * Load / Store * + *****************************************************************************/ + +#define _mm_set_epi8(x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15) \ + ((int8x16_t) { \ + (int8_t) (x15), (int8_t) (x14), (int8_t) (x13), (int8_t) (x12), \ + (int8_t) (x11), (int8_t) (x10), (int8_t) (x9), (int8_t) (x8), \ + (int8_t) (x7), (int8_t) (x6), (int8_t) (x5), (int8_t) (x4), \ + (int8_t) (x3), (int8_t) (x2), (int8_t) (x1), (int8_t) (x0) }) +#define _mm_set_epi16(x0, x1, x2, x3, x4, x5, x6, x7) \ + (vreinterpretq_s8_s16((int16x8_t) { \ + (int16_t) (x7), (int16_t) (x6), (int16_t) (x5), (int16_t) (x4), \ + (int16_t) (x3), (int16_t) (x2), (int16_t) (x1), (int16_t) (x0) })) +#define _mm_set_epi32(x0, x1, x2, x3) \ + (vreinterpretq_s8_s32((int32x4_t) { (int32_t) (x3), (int32_t) (x2), (int32_t) (x1), (int32_t) (x0) })) +#define _mm_set_epi64x(x0, x1) (vreinterpretq_s8_s64((int64x2_t) { (int64_t) (x1), (int64_t) (x0) })) +#define _mm_set1_epi8(x) (vdupq_n_s8((int8_t) (x))) +#define _mm_set1_epi16(x) (vreinterpretq_s8_s16(vdupq_n_s16((int16_t) (x)))) +#define _mm_set1_epi32(x) (vreinterpretq_s8_s32(vdupq_n_s32((int32_t) (x)))) +#define _mm_set1_epi64x(x) (vreinterpretq_s8_s64(vdupq_n_s64((int64_t) (x)))) + +#define _mm_setr_epi8(x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15) \ + ((int8x16_t) { \ + (int8_t) (x0), (int8_t) (x1), (int8_t) (x2), (int8_t) (x3), \ + (int8_t) (x4), (int8_t) (x5), (int8_t) (x6), (int8_t) (x7), \ + (int8_t) (x8), (int8_t) (x9), (int8_t) (x10), (int8_t) (x11), \ + (int8_t) (x12), (int8_t) (x13), (int8_t) (x14), (int8_t) (x15) }) +#define _mm_setr_epi16(x0, x1, x2, x3, x4, x5, x6, x7) \ + (vreinterpretq_s8_s16((int16x8_t) { \ + (int16_t) (x0), (int16_t) (x1), (int16_t) (x2), (int16_t) (x3), \ + (int16_t) (x4), (int16_t) (x5), (int16_t) (x6), (int16_t) (x7) })) +#define _mm_setr_epi32(x0, x1, x2, x3) \ + (vreinterpretq_s8_s32((int32x4_t) { (int32_t) (x0), (int32_t) (x1), (int32_t) (x2), (int32_t) (x3) })) + +#define _mm_setzero_si128() (vdupq_n_s8(0)) +#define _mm_undefined_si128() _mm_setzero_si128() + +#define _mm_load_si128(x) (vld1q_s8((const int8_t*) (x))) +static XSSE_FORCE_INLINE __m128i _mm_loadu_si16(const void *ptr) +{ + int16_t val; + XSSE_MEMCPY(&val, ptr, 2); + return vreinterpretq_s8_s16((int16x8_t) { + (int16_t) val, (int16_t) 0, (int16_t) 0, (int16_t) 0, + (int16_t) 0, (int16_t) 0, (int16_t) 0, (int16_t) 0 + }); +} +static XSSE_FORCE_INLINE __m128i _mm_loadu_si32(const void *ptr) +{ + int32_t val; + XSSE_MEMCPY(&val, ptr, 4); + return vreinterpretq_s8_s32((int32x4_t) { + (int32_t) val, (int32_t) 0, (int32_t) 0, (int32_t) 0 + }); +} +static XSSE_FORCE_INLINE __m128i _mm_loadu_si64(const void *ptr) +{ + int64_t val; + XSSE_MEMCPY(&val, ptr, 8); + return vreinterpretq_s8_s64((int64x2_t) { + (int64_t) val, (int64_t) 0 + }); +} +#define _mm_loadu_si128(x) _mm_load_si128(x) +static XSSE_FORCE_INLINE __m128i _mm_loadl_epi64(__m128i const *x) +{ + int64x1_t zero = vdup_n_s64(0); + int64x1_t x64 = vld1_s64((const int64_t*) x); + return vreinterpretq_s8_s64(vcombine_s64(x64, zero)); +} + +#define _mm_storel_epi64(to, x) (vst1_u64((uint64_t*) (to), vget_low_u64(vreinterpretq_u64_s8(x)))) +#define _mm_store_si128(to, x) (vst1q_s8((int8_t*) (to), x)) +static XSSE_FORCE_INLINE void _mm_storeu_si16(void *ptr, __m128i x) +{ + int16_t val = (int16_t) vgetq_lane_s16(vreinterpretq_s16_s8(x), 0); + XSSE_MEMCPY(ptr, &val, sizeof(val)); +} +static XSSE_FORCE_INLINE void _mm_storeu_si32(void *ptr, __m128i x) +{ + int32_t val = (int32_t) vgetq_lane_s32(vreinterpretq_s32_s8(x), 0); + XSSE_MEMCPY(ptr, &val, sizeof(val)); +} +static XSSE_FORCE_INLINE void _mm_storeu_si64(void *ptr, __m128i x) +{ + int64_t val = (int64_t) vgetq_lane_s64(vreinterpretq_s64_s8(x), 0); + XSSE_MEMCPY(ptr, &val, sizeof(val)); +} +#define _mm_storeu_si128(to, x) _mm_store_si128(to, x) +#define _mm_stream_si128(to, x) _mm_store_si128(to, x) +#define _mm_stream_si32(to, x) (*(int32_t*) (to) = (int32_t) (x)) +#define _mm_stream_si64(to, x) (*(int64_t*) (to) = (int64_t) (x)) + + +/***************************************************************************** + * Bit shift / Bit wise * + *****************************************************************************/ + +#define _mm_or_si128(a, b) (vorrq_s8((a), (b))) +#define _mm_xor_si128(a, b) (veorq_s8((a), (b))) +#define _mm_and_si128(a, b) (vandq_s8((a), (b))) +#define _mm_andnot_si128(a, b) (vbicq_s8((b), (a))) + +#define _mm_slli_epi16(x, count) (vreinterpretq_s8_u16(vshlq_n_u16(vreinterpretq_u16_s8(x), (count)))) +#define _mm_slli_epi32(x, count) (vreinterpretq_s8_u32(vshlq_n_u32(vreinterpretq_u32_s8(x), (count)))) +#define _mm_slli_epi64(x, count) (vreinterpretq_s8_u64(vshlq_n_u64(vreinterpretq_u64_s8(x), (count)))) +static XSSE_FORCE_INLINE __m128i _mm_sll_epi16(__m128i x, __m128i count) +{ + uint16x8_t x16 = vreinterpretq_u16_s8(x); + uint16_t shift = (uint16_t) (vgetq_lane_s64(vreinterpretq_s64_s8(count), 0) & 0xFFFF); + int16x8_t shifts = vdupq_n_s16((int16_t) shift); + return vreinterpretq_s8_u16(vshlq_u16(x16, shifts)); +} +static XSSE_FORCE_INLINE __m128i _mm_sll_epi32(__m128i x, __m128i count) +{ + uint32x4_t x32 = vreinterpretq_u32_s8(x); + uint32_t shift = (uint32_t) (vgetq_lane_s64(vreinterpretq_s64_s8(count), 0) & 0xFFFFFFFF); + int32x4_t shifts = vdupq_n_s32((int32_t) shift); + return vreinterpretq_s8_u32(vshlq_u32(x32, shifts)); +} +static XSSE_FORCE_INLINE __m128i _mm_sll_epi64(__m128i x, __m128i count) +{ + uint64x2_t x64 = vreinterpretq_u64_s8(x); + uint64_t shift = (uint64_t) vgetq_lane_s64(vreinterpretq_s64_s8(count), 0); + int64x2_t shifts = vdupq_n_s64((int64_t) shift); + return vreinterpretq_s8_u64(vshlq_u64(x64, shifts)); +} + +static XSSE_FORCE_INLINE __m128i _mm_slli_si128(__m128i x, int imm) +{ + switch (imm) { + case 0:; + int8x16_t x2 = x; + return x2; + case 1: return vreinterpretq_s8_u8(vextq_u8(vdupq_n_u8(0), vreinterpretq_u8_s8(x), 15)); + case 2: return vreinterpretq_s8_u8(vextq_u8(vdupq_n_u8(0), vreinterpretq_u8_s8(x), 14)); + case 3: return vreinterpretq_s8_u8(vextq_u8(vdupq_n_u8(0), vreinterpretq_u8_s8(x), 13)); + case 4: return vreinterpretq_s8_u8(vextq_u8(vdupq_n_u8(0), vreinterpretq_u8_s8(x), 12)); + case 5: return vreinterpretq_s8_u8(vextq_u8(vdupq_n_u8(0), vreinterpretq_u8_s8(x), 11)); + case 6: return vreinterpretq_s8_u8(vextq_u8(vdupq_n_u8(0), vreinterpretq_u8_s8(x), 10)); + case 7: return vreinterpretq_s8_u8(vextq_u8(vdupq_n_u8(0), vreinterpretq_u8_s8(x), 9)); + case 8: return vreinterpretq_s8_u8(vextq_u8(vdupq_n_u8(0), vreinterpretq_u8_s8(x), 8)); + case 9: return vreinterpretq_s8_u8(vextq_u8(vdupq_n_u8(0), vreinterpretq_u8_s8(x), 7)); + case 10: return vreinterpretq_s8_u8(vextq_u8(vdupq_n_u8(0), vreinterpretq_u8_s8(x), 6)); + case 11: return vreinterpretq_s8_u8(vextq_u8(vdupq_n_u8(0), vreinterpretq_u8_s8(x), 5)); + case 12: return vreinterpretq_s8_u8(vextq_u8(vdupq_n_u8(0), vreinterpretq_u8_s8(x), 4)); + case 13: return vreinterpretq_s8_u8(vextq_u8(vdupq_n_u8(0), vreinterpretq_u8_s8(x), 3)); + case 14: return vreinterpretq_s8_u8(vextq_u8(vdupq_n_u8(0), vreinterpretq_u8_s8(x), 2)); + case 15: return vreinterpretq_s8_u8(vextq_u8(vdupq_n_u8(0), vreinterpretq_u8_s8(x), 1)); + default: return vreinterpretq_s8_u8(vdupq_n_u8(0)); + } +} +#define _mm_bslli_si128(x, imm) _mm_slli_si128(x, imm) + +#define _mm_srai_epi16(x, count) (vreinterpretq_s8_s16(vshrq_n_s16(vreinterpretq_s16_s8(x), (count)))) +#define _mm_srai_epi32(x, count) (vreinterpretq_s8_s32(vshrq_n_s32(vreinterpretq_s32_s8(x), (count)))) +static XSSE_FORCE_INLINE __m128i _mm_sra_epi16(__m128i x, __m128i count) +{ + int16x8_t x16 = vreinterpretq_s16_s8(x); + uint16_t shift = (uint16_t) (vgetq_lane_s64(vreinterpretq_s64_s8(count), 0) & 0xFFFF); + int16x8_t shifts = vdupq_n_s16(-(int16_t) shift); + return vreinterpretq_s8_s16(vshlq_s16(x16, shifts)); +} +static XSSE_FORCE_INLINE __m128i _mm_sra_epi32(__m128i x, __m128i count) +{ + int32x4_t x32 = vreinterpretq_s32_s8(x); + uint32_t shift = (uint32_t) (vgetq_lane_s64(vreinterpretq_s64_s8(count), 0) & 0xFFFFFFFF); + int32x4_t shifts = vdupq_n_s32(-(int32_t) shift); + return vreinterpretq_s8_s32(vshlq_s32(x32, shifts)); +} + +#define _mm_srli_epi16(x, count) (vreinterpretq_s8_u16(vshrq_n_u16(vreinterpretq_u16_s8(x), (count)))) +#define _mm_srli_epi32(x, count) (vreinterpretq_s8_u32(vshrq_n_u32(vreinterpretq_u32_s8(x), (count)))) +#define _mm_srli_epi64(x, count) (vreinterpretq_s8_u64(vshrq_n_u64(vreinterpretq_u64_s8(x), (count)))) +static XSSE_FORCE_INLINE __m128i _mm_srl_epi16(__m128i x, __m128i count) +{ + uint16x8_t x16 = vreinterpretq_u16_s8(x); + uint16_t shift = (uint16_t) (vgetq_lane_s64(vreinterpretq_s64_s8(count), 0) & 0xFFFF); + int16x8_t shifts = vdupq_n_s16(-(int16_t) shift); + return vreinterpretq_s8_u16(vshlq_u16(x16, shifts)); +} +static XSSE_FORCE_INLINE __m128i _mm_srl_epi32(__m128i x, __m128i count) +{ + uint32x4_t x32 = vreinterpretq_u32_s8(x); + uint32_t shift = (uint32_t) (vgetq_lane_s64(vreinterpretq_s64_s8(count), 0) & 0xFFFFFFFF); + int32x4_t shifts = vdupq_n_s32(-(int32_t) shift); + return vreinterpretq_s8_u32(vshlq_u32(x32, shifts)); +} +static XSSE_FORCE_INLINE __m128i _mm_srl_epi64(__m128i x, __m128i count) +{ + uint64x2_t x64 = vreinterpretq_u64_s8(x); + uint64_t shift = (uint64_t) vgetq_lane_s64(vreinterpretq_s64_s8(count), 0); + int64x2_t shifts = vdupq_n_s64(-(int64_t) shift); + return vreinterpretq_s8_u64(vshlq_u64(x64, shifts)); +} + +static XSSE_FORCE_INLINE __m128i _mm_srli_si128(__m128i x, int imm) +{ + switch (imm) { + case 0: return x; + case 1: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(x), vdupq_n_u8(0), 1)); + case 2: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(x), vdupq_n_u8(0), 2)); + case 3: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(x), vdupq_n_u8(0), 3)); + case 4: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(x), vdupq_n_u8(0), 4)); + case 5: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(x), vdupq_n_u8(0), 5)); + case 6: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(x), vdupq_n_u8(0), 6)); + case 7: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(x), vdupq_n_u8(0), 7)); + case 8: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(x), vdupq_n_u8(0), 8)); + case 9: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(x), vdupq_n_u8(0), 9)); + case 10: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(x), vdupq_n_u8(0), 10)); + case 11: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(x), vdupq_n_u8(0), 11)); + case 12: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(x), vdupq_n_u8(0), 12)); + case 13: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(x), vdupq_n_u8(0), 13)); + case 14: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(x), vdupq_n_u8(0), 14)); + case 15: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(x), vdupq_n_u8(0), 15)); + default: return vreinterpretq_s8_u8(vdupq_n_u8(0)); + } +} +#define _mm_bsrli_si128(x, imm) _mm_srli_si128(x, imm) + + +/***************************************************************************** + * Integer Arithmetic Operations * + *****************************************************************************/ + +/** + * In practice, there is no problem, but a runtime error for signed integer overflow is triggered by UBSAN, + * so perform the calculation as unsigned. Since it is optimized at compile time, there are no unnecessary casts at runtime. + */ +#define _mm_add_epi8(a, b) (vreinterpretq_s8_u8(vaddq_u8(vreinterpretq_u8_s8(a), vreinterpretq_u8_s8(b)))) +#define _mm_add_epi16(a, b) (vreinterpretq_s8_u16(vaddq_u16(vreinterpretq_u16_s8(a), vreinterpretq_u16_s8(b)))) +#define _mm_add_epi32(a, b) (vreinterpretq_s8_u32(vaddq_u32(vreinterpretq_u32_s8(a), vreinterpretq_u32_s8(b)))) +#define _mm_add_epi64(a, b) (vreinterpretq_s8_u64(vaddq_u64(vreinterpretq_u64_s8(a), vreinterpretq_u64_s8(b)))) + +#define _mm_adds_epi8(a, b) (vqaddq_s8((a), (b))) +#define _mm_adds_epi16(a, b) (vreinterpretq_s8_s16(vqaddq_s16(vreinterpretq_s16_s8(a), vreinterpretq_s16_s8(b)))) +#define _mm_adds_epu8(a, b) (vreinterpretq_s8_u8(vqaddq_u8(vreinterpretq_u8_s8(a), vreinterpretq_u8_s8(b)))) +#define _mm_adds_epu16(a, b) (vreinterpretq_s8_u16(vqaddq_u16(vreinterpretq_u16_s8(a), vreinterpretq_u16_s8(b)))) + +#define _mm_avg_epu8(a, b) (vreinterpretq_s8_u8(vrhaddq_u8(vreinterpretq_u8_s8(a), vreinterpretq_u8_s8(b)))) +#define _mm_avg_epu16(a, b) (vreinterpretq_s8_u16(vrhaddq_u16(vreinterpretq_u16_s8(a), vreinterpretq_u16_s8(b)))) + +static XSSE_FORCE_INLINE __m128i _mm_madd_epi16(__m128i a, __m128i b) +{ + int16x8_t a16 = vreinterpretq_s16_s8(a); + int16x8_t b16 = vreinterpretq_s16_s8(b); + + int16x4_t a_lo = vget_low_s16(a16); + int16x4_t a_hi = vget_high_s16(a16); + int16x4_t b_lo = vget_low_s16(b16); + int16x4_t b_hi = vget_high_s16(b16); + + int32x4_t mul_lo = vmull_s16(a_lo, b_lo); + int32x4_t mul_hi = vmull_s16(a_hi, b_hi); + return vreinterpretq_s8_s32(vpaddq_s32(mul_lo, mul_hi)); +} + +#define _mm_max_epu8(a, b) (vreinterpretq_s8_u8(vmaxq_u8(vreinterpretq_u8_s8(a), vreinterpretq_u8_s8(b)))) +#define _mm_max_epi16(a, b) (vreinterpretq_s8_s16(vmaxq_s16(vreinterpretq_s16_s8(a), vreinterpretq_s16_s8(b)))) +#define _mm_min_epu8(a, b) (vreinterpretq_s8_u8(vminq_u8(vreinterpretq_u8_s8(a), vreinterpretq_u8_s8(b)))) +#define _mm_min_epi16(a, b) (vreinterpretq_s8_s16(vminq_s16(vreinterpretq_s16_s8(a), vreinterpretq_s16_s8(b)))) + +static XSSE_FORCE_INLINE __m128i _mm_mulhi_epi16(__m128i a, __m128i b) +{ + int16x8_t a16 = vreinterpretq_s16_s8(a); + int16x8_t b16 = vreinterpretq_s16_s8(b); + + int16x4_t a_lo = vget_low_s16(a16); + int16x4_t a_hi = vget_high_s16(a16); + int16x4_t b_lo = vget_low_s16(b16); + int16x4_t b_hi = vget_high_s16(b16); + + int32x4_t mul_lo = vmull_s16(a_lo, b_lo); + int32x4_t mul_hi = vmull_s16(a_hi, b_hi); + + int16x4_t narrowed_lo = vshrn_n_s32(mul_lo, 16); + int16x4_t narrowed_hi = vshrn_n_s32(mul_hi, 16); + + return vreinterpretq_s8_s16(vcombine_s16(narrowed_lo, narrowed_hi)); +} +static XSSE_FORCE_INLINE __m128i _mm_mulhi_epu16(__m128i a, __m128i b) +{ + uint16x8_t a16 = vreinterpretq_u16_s8(a); + uint16x8_t b16 = vreinterpretq_u16_s8(b); + + uint16x4_t a_lo = vget_low_u16(a16); + uint16x4_t a_hi = vget_high_u16(a16); + uint16x4_t b_lo = vget_low_u16(b16); + uint16x4_t b_hi = vget_high_u16(b16); + + uint32x4_t mul_lo = vmull_u16(a_lo, b_lo); + uint32x4_t mul_hi = vmull_u16(a_hi, b_hi); + + uint16x4_t narrowed_lo = vshrn_n_u32(mul_lo, 16); + uint16x4_t narrowed_hi = vshrn_n_u32(mul_hi, 16); + + return vreinterpretq_s8_u16(vcombine_u16(narrowed_lo, narrowed_hi)); +} +static XSSE_FORCE_INLINE __m128i _mm_mullo_epi16(__m128i a, __m128i b) +{ + int16x8_t a16 = vreinterpretq_s16_s8(a); + int16x8_t b16 = vreinterpretq_s16_s8(b); + return vreinterpretq_s8_s16(vmulq_s16(a16, b16)); +} +static XSSE_FORCE_INLINE __m128i _mm_mul_epu32(__m128i a, __m128i b) +{ + uint32x4_t a32 = vreinterpretq_u32_s8(a); + uint32x4_t b32 = vreinterpretq_u32_s8(b); + uint32x4_t evens = vuzp1q_u32(a32, b32); + uint32x2_t lo = vget_low_u32(evens); + uint32x2_t hi = vget_high_u32(evens); + + return vreinterpretq_s8_u64(vmull_u32(lo, hi)); +} +static XSSE_FORCE_INLINE __m128i _mm_sad_epu8(__m128i a, __m128i b) +{ + uint8x16_t ua = vreinterpretq_u8_s8(a); + uint8x16_t ub = vreinterpretq_u8_s8(b); + uint16x8_t abs_diffs_16 = vpaddlq_u8(vabdq_u8(ua, ub)); + uint32x4_t abs_diffs_32 = vpaddlq_u16(abs_diffs_16); + uint64x2_t abs_diffs_64 = vpaddlq_u32(abs_diffs_32); + + return vreinterpretq_s8_u16((uint16x8_t) { + (int16_t) vgetq_lane_u64(abs_diffs_64, 0), 0, 0, 0, + (int16_t) vgetq_lane_u64(abs_diffs_64, 1), 0, 0, 0 + }); +} + +#define _mm_sub_epi8(a, b) (vreinterpretq_s8_u8(vsubq_u8(vreinterpretq_u8_s8(a), vreinterpretq_u8_s8(b)))) +#define _mm_sub_epi16(a, b) (vreinterpretq_s8_u16(vsubq_u16(vreinterpretq_u16_s8(a), vreinterpretq_u16_s8(b)))) +#define _mm_sub_epi32(a, b) (vreinterpretq_s8_u32(vsubq_u32(vreinterpretq_u32_s8(a), vreinterpretq_u32_s8(b)))) +#define _mm_sub_epi64(a, b) (vreinterpretq_s8_u64(vsubq_u64(vreinterpretq_u64_s8(a), vreinterpretq_u64_s8(b)))) + +#define _mm_subs_epi8(a, b) (vqsubq_s8((a), (b))) +#define _mm_subs_epi16(a, b) (vreinterpretq_s8_s16(vqsubq_s16(vreinterpretq_s16_s8(a), vreinterpretq_s16_s8(b)))) +#define _mm_subs_epu8(a, b) (vreinterpretq_s8_u8(vqsubq_u8(vreinterpretq_u8_s8(a), vreinterpretq_u8_s8(b)))) +#define _mm_subs_epu16(a, b) (vreinterpretq_s8_u16(vqsubq_u16(vreinterpretq_u16_s8(a), vreinterpretq_u16_s8(b)))) + + +/***************************************************************************** + * Comparison * + *****************************************************************************/ + +#define _mm_cmpeq_epi8(a, b) (vreinterpretq_s8_u8(vceqq_s8((a), (b)))) +#define _mm_cmpeq_epi16(a, b) (vreinterpretq_s8_u16(vceqq_s16(vreinterpretq_s16_s8(a), vreinterpretq_s16_s8(b)))) +#define _mm_cmpeq_epi32(a, b) (vreinterpretq_s8_u32(vceqq_s32(vreinterpretq_s32_s8(a), vreinterpretq_s32_s8(b)))) + +#define _mm_cmplt_epi8(a, b) (vreinterpretq_s8_u8(vcltq_s8((a), (b)))) +#define _mm_cmplt_epi16(a, b) (vreinterpretq_s8_u16(vcltq_s16(vreinterpretq_s16_s8(a), vreinterpretq_s16_s8(b)))) +#define _mm_cmplt_epi32(a, b) (vreinterpretq_s8_u32(vcltq_s32(vreinterpretq_s32_s8(a), vreinterpretq_s32_s8(b)))) + +#define _mm_cmpgt_epi8(a, b) (vreinterpretq_s8_u8(vcgtq_s8((a), (b)))) +#define _mm_cmpgt_epi16(a, b) (vreinterpretq_s8_u16(vcgtq_s16(vreinterpretq_s16_s8(a), vreinterpretq_s16_s8(b)))) +#define _mm_cmpgt_epi32(a, b) (vreinterpretq_s8_u32(vcgtq_s32(vreinterpretq_s32_s8(a), vreinterpretq_s32_s8(b)))) + + +/***************************************************************************** + * Convert * + *****************************************************************************/ + +#define _mm_cvtsi32_si128(x) (vreinterpretq_s8_s32((int32x4_t) { (int32_t) (x), 0, 0, 0 })) +#define _mm_cvtsi64_si128(x) (vreinterpretq_s8_s64((int64x2_t) { (int64_t) (x), 0 })) +#define _mm_cvtsi128_si32(x) (vgetq_lane_s32(vreinterpretq_s32_s8(x), 0)) +#define _mm_cvtsi128_si64(x) (vgetq_lane_s64(vreinterpretq_s64_s8(x), 0)) + + +/***************************************************************************** + * Others * + *****************************************************************************/ + +#define _mm_packs_epi16(a, b) (vcombine_s8(vqmovn_s16(vreinterpretq_s16_s8(a)), vqmovn_s16(vreinterpretq_s16_s8(b)))) +#define _mm_packs_epi32(a, b) \ + (vreinterpretq_s8_s16(vcombine_s16(vqmovn_s32(vreinterpretq_s32_s8(a)), vqmovn_s32(vreinterpretq_s32_s8(b))))) +#define _mm_packus_epi16(a, b) \ + (vreinterpretq_s8_u8(vcombine_u8(vqmovun_s16(vreinterpretq_s16_s8(a)), vqmovun_s16(vreinterpretq_s16_s8(b))))) + +#define _mm_extract_epi16(x, imm) (vgetq_lane_s16(vreinterpretq_s16_s8(x), (imm))) +#define _mm_insert_epi16(x, val, imm) (vreinterpretq_s8_s16(vsetq_lane_s16((int16_t) (val), vreinterpretq_s16_s8(x), (imm)))) + +static XSSE_FORCE_INLINE void _mm_maskmoveu_si128(__m128i x, __m128i mask, char* dest) +{ + uint8x16_t origin = vld1q_u8((uint8_t*) dest); + + uint8x16_t repeat_0x00 = vdupq_n_u8(0); + uint8x16_t repeat_0x80 = vdupq_n_u8(0x80); + uint8x16_t umask = vreinterpretq_u8_s8(mask); + uint8x16_t ux = vreinterpretq_u8_s8(x); + + uint8x16_t mask_bits = vandq_u8(umask, repeat_0x80); + uint8x16_t mask_bytes = vsubq_u8(repeat_0x00, vshrq_n_u8(mask_bits, 7)); + vst1q_u8((uint8_t*) dest, vbslq_u8(mask_bytes, ux, origin)); +} +static XSSE_FORCE_INLINE int _mm_movemask_epi8(__m128i x) +{ + /** + * based on code from + * https://community.arm.com/arm-community-blogs/b/servers-and-cloud-computing-blog/posts/porting-x86-vector-bitmask-optimizations-to-arm-neon + */ + uint16x8_t high_bits = vreinterpretq_u16_u8(vshrq_n_u8(vreinterpretq_u8_s8(x), 7)); + uint32x4_t paired16 = vreinterpretq_u32_u16(vsraq_n_u16(high_bits, high_bits, 7)); + uint64x2_t paired32 = vreinterpretq_u64_u32(vsraq_n_u32(paired16, paired16, 14)); + uint8x16_t paired64 = vreinterpretq_u8_u64(vsraq_n_u64(paired32, paired32, 28)); + return vgetq_lane_u8(paired64, 0) | ((int) vgetq_lane_u8(paired64, 8) << 8); +} + +#define _MM_SHUFFLE(a, b, c, d) (((a) << 6) | ((b) << 4) | ((c) << 2) | (d)) +#ifdef XSSE_HAS_MACRO_EXTENSION +#define _mm_shuffle_epi32(x, imm) __extension__({ \ + int32x4_t __xsse_tmp = vreinterpretq_s32_s8(x); \ + vreinterpretq_s8_s32((int32x4_t) { \ + (int32_t) vgetq_lane_s32(__xsse_tmp, ((imm) >> 0) & 0x3), \ + (int32_t) vgetq_lane_s32(__xsse_tmp, ((imm) >> 2) & 0x3), \ + (int32_t) vgetq_lane_s32(__xsse_tmp, ((imm) >> 4) & 0x3), \ + (int32_t) vgetq_lane_s32(__xsse_tmp, ((imm) >> 6) & 0x3) \ + }); \ + }) +#define _mm_shufflehi_epi16(x, imm) __extension__({ \ + int16x8_t __xsse_tmp = vreinterpretq_s16_s8(x); \ + vreinterpretq_s8_s16(vcombine_s16( \ + vget_low_s16(__xsse_tmp), \ + (int16x4_t) { \ + (int16_t) vgetq_lane_s16(__xsse_tmp, (((imm) >> 0) & 0x3) + 4), \ + (int16_t) vgetq_lane_s16(__xsse_tmp, (((imm) >> 2) & 0x3) + 4), \ + (int16_t) vgetq_lane_s16(__xsse_tmp, (((imm) >> 4) & 0x3) + 4), \ + (int16_t) vgetq_lane_s16(__xsse_tmp, (((imm) >> 6) & 0x3) + 4) \ + } \ + )); \ + }) +#define _mm_shufflelo_epi16(x, imm) __extension__({ \ + int16x8_t __xsse_tmp = vreinterpretq_s16_s8(x); \ + vreinterpretq_s8_s16(vcombine_s16( \ + (int16x4_t) { \ + (int16_t) vgetq_lane_s16(__xsse_tmp, (((imm) >> 0) & 0x3)), \ + (int16_t) vgetq_lane_s16(__xsse_tmp, (((imm) >> 2) & 0x3)), \ + (int16_t) vgetq_lane_s16(__xsse_tmp, (((imm) >> 4) & 0x3)), \ + (int16_t) vgetq_lane_s16(__xsse_tmp, (((imm) >> 6) & 0x3)) \ + }, \ + vget_high_s16(__xsse_tmp) \ + )); \ + }) +#else +static XSSE_FORCE_INLINE __m128i _mm_shuffle_epi32(__m128i x, int imm) +{ + int32x4_t vec = vreinterpretq_s32_s8(x); + int32_t arr[4]; + vst1q_s32(arr, vec); + + return vreinterpretq_s8_s32((int32x4_t) { + arr[(imm >> 0) & 0x3], + arr[(imm >> 2) & 0x3], + arr[(imm >> 4) & 0x3], + arr[(imm >> 6) & 0x3] + }); +} +static XSSE_FORCE_INLINE __m128i _mm_shufflehi_epi16(__m128i x, int imm) +{ + int16x8_t vec = vreinterpretq_s16_s8(x); + int16_t arr[8]; + vst1q_s16(arr, vec); + + return vreinterpretq_s8_s16((int16x8_t) { + arr[0], arr[1], arr[2], arr[3], + arr[((imm >> 0) & 0x3) + 4], + arr[((imm >> 2) & 0x3) + 4], + arr[((imm >> 4) & 0x3) + 4], + arr[((imm >> 6) & 0x3) + 4] + }); +} +static XSSE_FORCE_INLINE __m128i _mm_shufflelo_epi16(__m128i x, int imm) +{ + int16x8_t vec = vreinterpretq_s16_s8(x); + int16_t arr[8]; + vst1q_s16(arr, vec); + + return vreinterpretq_s8_s16((int16x8_t) { + arr[((imm >> 0) & 0x3)], + arr[((imm >> 2) & 0x3)], + arr[((imm >> 4) & 0x3)], + arr[((imm >> 6) & 0x3)], + arr[4], arr[5], arr[6], arr[7] + }); +} +#endif + +#define _mm_unpackhi_epi8(a, b) (vzip2q_s8((a), (b))) +#define _mm_unpackhi_epi16(a, b) (vreinterpretq_s8_s16(vzip2q_s16(vreinterpretq_s16_s8(a), vreinterpretq_s16_s8(b)))) +#define _mm_unpackhi_epi32(a, b) (vreinterpretq_s8_s32(vzip2q_s32(vreinterpretq_s32_s8(a), vreinterpretq_s32_s8(b)))) +#define _mm_unpackhi_epi64(a, b) (vreinterpretq_s8_s64(vzip2q_s64(vreinterpretq_s64_s8(a), vreinterpretq_s64_s8(b)))) + +#define _mm_unpacklo_epi8(a, b) (vzip1q_s8((a), (b))) +#define _mm_unpacklo_epi16(a, b) (vreinterpretq_s8_s16(vzip1q_s16(vreinterpretq_s16_s8(a), vreinterpretq_s16_s8(b)))) +#define _mm_unpacklo_epi32(a, b) (vreinterpretq_s8_s32(vzip1q_s32(vreinterpretq_s32_s8(a), vreinterpretq_s32_s8(b)))) +#define _mm_unpacklo_epi64(a, b) (vreinterpretq_s8_s64(vzip1q_s64(vreinterpretq_s64_s8(a), vreinterpretq_s64_s8(b)))) + +#define _mm_move_epi64(x) (vreinterpretq_s8_s64((int64x2_t) { vgetq_lane_s64(vreinterpretq_s64_s8(x), 0), 0 })) + +#define _mm_clflush(x) ((void) 0) +static XSSE_FORCE_INLINE void _mm_mfence(void) +{ + __asm__ __volatile__("dsb sy" ::: "memory"); +} +static XSSE_FORCE_INLINE void _mm_lfence(void) +{ + __asm__ __volatile__("dsb ld" ::: "memory"); +} +static XSSE_FORCE_INLINE void _mm_pause(void) +{ + __asm__ __volatile__("yield"); +} + +#endif /* SSE2 */ + + +/***************************************************************************** + * * + * SSE3 * + * * + *****************************************************************************/ + +#if defined(__SSE3__) +#include +#define XSSE3 + + +#elif defined(__aarch64__) || defined(_M_ARM64) +#define XSSE3 + +/***************************************************************************** + * Load / Store * + *****************************************************************************/ + +#define _mm_lddqu_si128(x) _mm_load_si128(x) + +#endif /* SSE3 */ + + +/***************************************************************************** + * * + * SSSE3 * + * * + *****************************************************************************/ + +#if defined(__SSSE3__) +#include +#define XSSSE3 + + +#elif defined(__aarch64__) || defined(_M_ARM64) +#define XSSSE3 + +/***************************************************************************** + * Bit shift / Bit wise * + *****************************************************************************/ + +static XSSE_FORCE_INLINE __m128i _mm_alignr_epi8(__m128i a, __m128i b, int imm) +{ + switch (imm) { + case 0:; + int8x16_t b2 = b; + return b2; + case 1: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(b), vreinterpretq_u8_s8(a), 1)); + case 2: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(b), vreinterpretq_u8_s8(a), 2)); + case 3: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(b), vreinterpretq_u8_s8(a), 3)); + case 4: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(b), vreinterpretq_u8_s8(a), 4)); + case 5: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(b), vreinterpretq_u8_s8(a), 5)); + case 6: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(b), vreinterpretq_u8_s8(a), 6)); + case 7: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(b), vreinterpretq_u8_s8(a), 7)); + case 8: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(b), vreinterpretq_u8_s8(a), 8)); + case 9: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(b), vreinterpretq_u8_s8(a), 9)); + case 10: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(b), vreinterpretq_u8_s8(a), 10)); + case 11: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(b), vreinterpretq_u8_s8(a), 11)); + case 12: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(b), vreinterpretq_u8_s8(a), 12)); + case 13: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(b), vreinterpretq_u8_s8(a), 13)); + case 14: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(b), vreinterpretq_u8_s8(a), 14)); + case 15: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(b), vreinterpretq_u8_s8(a), 15)); + case 16:; + int8x16_t a2 = a; + return a2; + case 17: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(a), vdupq_n_u8(0), 1)); + case 18: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(a), vdupq_n_u8(0), 2)); + case 19: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(a), vdupq_n_u8(0), 3)); + case 20: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(a), vdupq_n_u8(0), 4)); + case 21: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(a), vdupq_n_u8(0), 5)); + case 22: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(a), vdupq_n_u8(0), 6)); + case 23: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(a), vdupq_n_u8(0), 7)); + case 24: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(a), vdupq_n_u8(0), 8)); + case 25: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(a), vdupq_n_u8(0), 9)); + case 26: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(a), vdupq_n_u8(0), 10)); + case 27: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(a), vdupq_n_u8(0), 11)); + case 28: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(a), vdupq_n_u8(0), 12)); + case 29: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(a), vdupq_n_u8(0), 13)); + case 30: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(a), vdupq_n_u8(0), 14)); + case 31: return vreinterpretq_s8_u8(vextq_u8(vreinterpretq_u8_s8(a), vdupq_n_u8(0), 15)); + default: return vdupq_n_s8(0); + } +} + + +/***************************************************************************** + * Integer Arithmetic Operations * + *****************************************************************************/ + +#define _mm_abs_epi8(x) (vabsq_s8(x)) +#define _mm_abs_epi16(x) (vreinterpretq_s8_s16(vabsq_s16(vreinterpretq_s16_s8(x)))) +#define _mm_abs_epi32(x) (vreinterpretq_s8_s32(vabsq_s32(vreinterpretq_s32_s8(x)))) + +#define _mm_hadd_epi16(a, b) (vreinterpretq_s8_u16(vpaddq_u16(vreinterpretq_u16_s8(a), vreinterpretq_u16_s8(b)))) +#define _mm_hadd_epi32(a, b) (vreinterpretq_s8_u32(vpaddq_u32(vreinterpretq_u32_s8(a), vreinterpretq_u32_s8(b)))) +static XSSE_FORCE_INLINE __m128i _mm_hadds_epi16(__m128i a, __m128i b) +{ + int16x8_t a16 = vreinterpretq_s16_s8(a); + int16x8_t b16 = vreinterpretq_s16_s8(b); + int16x8_t evens = vuzp1q_s16(a16, b16); + int16x8_t odds = vuzp2q_s16(a16, b16); + return vreinterpretq_s8_s16(vqaddq_s16(evens, odds)); +} + +static XSSE_FORCE_INLINE __m128i _mm_hsub_epi16(__m128i a, __m128i b) +{ + uint16x8_t a16 = vreinterpretq_u16_s8(a); + uint16x8_t b16 = vreinterpretq_u16_s8(b); + uint16x8_t evens = vuzp1q_u16(a16, b16); + uint16x8_t odds = vuzp2q_u16(a16, b16); + return vreinterpretq_s8_u16(vsubq_u16(evens, odds)); +} +static XSSE_FORCE_INLINE __m128i _mm_hsub_epi32(__m128i a, __m128i b) +{ + uint32x4_t a32 = vreinterpretq_u32_s8(a); + uint32x4_t b32 = vreinterpretq_u32_s8(b); + uint32x4_t evens = vuzp1q_u32(a32, b32); + uint32x4_t odds = vuzp2q_u32(a32, b32); + return vreinterpretq_s8_u32(vsubq_u32(evens, odds)); +} +static XSSE_FORCE_INLINE __m128i _mm_hsubs_epi16(__m128i a, __m128i b) +{ + int16x8_t a16 = vreinterpretq_s16_s8(a); + int16x8_t b16 = vreinterpretq_s16_s8(b); + int16x8_t evens = vuzp1q_s16(a16, b16); + int16x8_t odds = vuzp2q_s16(a16, b16); + return vreinterpretq_s8_s16(vqsubq_s16(evens, odds)); +} + +static XSSE_FORCE_INLINE __m128i _mm_maddubs_epi16(__m128i a, __m128i b) +{ + uint8x16_t ua = vreinterpretq_u8_s8(a); + uint8x8_t ua_lo = vget_low_u8(ua); + uint8x8_t ua_hi = vget_high_u8(ua); + + uint16x8_t wide_ua_lo = vmovl_u8(ua_lo); + uint16x8_t wide_ua_hi = vmovl_u8(ua_hi); + + int16x8_t wide_a_lo = vreinterpretq_s16_u16(wide_ua_lo); + int16x8_t wide_a_hi = vreinterpretq_s16_u16(wide_ua_hi); + + int8x8_t b_lo = vget_low_s8(b); + int8x8_t b_hi = vget_high_s8(b); + + int16x8_t wide_b_lo = vmovl_s8(b_lo); + int16x8_t wide_b_hi = vmovl_s8(b_hi); + + int16x8_t mul_lo = vmulq_s16(wide_a_lo, wide_b_lo); + int16x8_t mul_hi = vmulq_s16(wide_a_hi, wide_b_hi); + + uint16x8_t umul_lo = vreinterpretq_u16_s16(mul_lo); + uint16x8_t umul_hi = vreinterpretq_u16_s16(mul_hi); + + return vreinterpretq_s8_u16(vpaddq_u16(umul_lo, umul_hi)); +} + +static XSSE_FORCE_INLINE __m128i _mm_mulhrs_epi16(__m128i a, __m128i b) +{ + int16x8_t a16 = vreinterpretq_s16_s8(a); + int16x8_t b16 = vreinterpretq_s16_s8(b); + + int16x4_t a_lo = vget_low_s16(a16); + int16x4_t a_hi = vget_high_s16(a16); + int16x4_t b_lo = vget_low_s16(b16); + int16x4_t b_hi = vget_high_s16(b16); + + int32x4_t mul_lo = vmull_s16(a_lo, b_lo); + int32x4_t mul_hi = vmull_s16(a_hi, b_hi); + + int32x4_t repeat_0x4000 = vdupq_n_s32(0x4000); + + int32x4_t add_lo = vaddq_s32(mul_lo, repeat_0x4000); + int32x4_t add_hi = vaddq_s32(mul_hi, repeat_0x4000); + + int32x4_t lo = vshrq_n_s32(add_lo, 15); + int32x4_t hi = vshrq_n_s32(add_hi, 15); + + int16x4_t narrowed_lo = vqmovn_s32(lo); + int16x4_t narrowed_hi = vqmovn_s32(hi); + + return vreinterpretq_s8_s16(vcombine_s16(narrowed_lo, narrowed_hi)); +} + +static XSSE_FORCE_INLINE __m128i _mm_sign_epi8(__m128i a, __m128i b) +{ + int8x16_t repeat_0x00 = vdupq_n_s8(0); + int8x16_t repeat_0x01 = vdupq_n_s8(1); + + uint8x16_t upos_cmp_ret = vcltq_s8(repeat_0x00, b); + uint8x16_t uneg_cmp_ret = vcltq_s8(b, repeat_0x00); + + int8x16_t pos_cmp_ret = vreinterpretq_s8_u8(upos_cmp_ret); + int8x16_t pos_mask = vandq_s8(pos_cmp_ret, repeat_0x01); + int8x16_t neg_mask = vreinterpretq_s8_u8(uneg_cmp_ret); + + int8x16_t mask = vorrq_s8(pos_mask, neg_mask); + + return vmulq_s8(a, mask); +} +static XSSE_FORCE_INLINE __m128i _mm_sign_epi16(__m128i a, __m128i b) +{ + int16x8_t a16 = vreinterpretq_s16_s8(a); + int16x8_t b16 = vreinterpretq_s16_s8(b); + + int16x8_t repeat_0x00 = vdupq_n_s16(0); + int16x8_t repeat_0x01 = vdupq_n_s16(1); + + uint16x8_t upos_cmp_ret = vcltq_s16(repeat_0x00, b16); + uint16x8_t uneg_cmp_ret = vcltq_s16(b16, repeat_0x00); + + int16x8_t pos_cmp_ret = vreinterpretq_s16_u16(upos_cmp_ret); + int16x8_t pos_mask = vandq_s16(pos_cmp_ret, repeat_0x01); + int16x8_t neg_mask = vreinterpretq_s16_u16(uneg_cmp_ret); + + int16x8_t mask = vorrq_s16(pos_mask, neg_mask); + + return vreinterpretq_s8_s16(vmulq_s16(a16, mask)); +} +static XSSE_FORCE_INLINE __m128i _mm_sign_epi32(__m128i a, __m128i b) +{ + int32x4_t a32 = vreinterpretq_s32_s8(a); + int32x4_t b32 = vreinterpretq_s32_s8(b); + + int32x4_t repeat_0x00 = vdupq_n_s32(0); + int32x4_t repeat_0x01 = vdupq_n_s32(1); + + uint32x4_t upos_cmp_ret = vcltq_s32(repeat_0x00, b32); + uint32x4_t uneg_cmp_ret = vcltq_s32(b32, repeat_0x00); + + int32x4_t pos_cmp_ret = vreinterpretq_s32_u32(upos_cmp_ret); + int32x4_t pos_mask = vandq_s32(pos_cmp_ret, repeat_0x01); + int32x4_t neg_mask = vreinterpretq_s32_u32(uneg_cmp_ret); + + int32x4_t mask = vorrq_s32(pos_mask, neg_mask); + + return vreinterpretq_s8_s32(vmulq_s32(a32, mask)); +} + + +/***************************************************************************** + * Others * + *****************************************************************************/ + +static XSSE_FORCE_INLINE __m128i _mm_shuffle_epi8(__m128i a, __m128i b) +{ + uint8x16_t index = vreinterpretq_u8_s8(b); + uint8x16_t repeat_0x0F = vdupq_n_u8(0x0F); + uint8x16_t repeat_0x80 = vdupq_n_u8(0x80); + + uint8x16_t and_mask = vandq_u8(index, repeat_0x0F); + uint8x16_t cmp_mask = vcgeq_u8(index, repeat_0x80); + uint8x16_t masked_index = vorrq_u8(and_mask, cmp_mask); + return vqtbl1q_s8(a, masked_index); +} + +#endif /* SSSE3 */ + + +/***************************************************************************** + * * + * SSE4.1 * + * * + *****************************************************************************/ + +#if defined(__SSE4_1__) +#include +#define XSSE4_1 + + +#elif defined(__aarch64__) || defined(_M_ARM64) +#define XSSE4_1 + +/***************************************************************************** + * Load / Store * + *****************************************************************************/ + +#define _mm_stream_load_si128(x) _mm_load_si128(x) + + +/***************************************************************************** + * Integer Arithmetic Operations * + *****************************************************************************/ + +#define _mm_max_epi8(a, b) (vmaxq_s8((a), (b))) +#define _mm_max_epi32(a, b) (vreinterpretq_s8_s32(vmaxq_s32(vreinterpretq_s32_s8(a), vreinterpretq_s32_s8(b)))) +#define _mm_max_epu16(a, b) (vreinterpretq_s8_u16(vmaxq_u16(vreinterpretq_u16_s8(a), vreinterpretq_u16_s8(b)))) +#define _mm_max_epu32(a, b) (vreinterpretq_s8_u32(vmaxq_u32(vreinterpretq_u32_s8(a), vreinterpretq_u32_s8(b)))) + +#define _mm_min_epi8(a, b) (vminq_s8((a), (b))) +#define _mm_min_epi32(a, b) (vreinterpretq_s8_s32(vminq_s32(vreinterpretq_s32_s8(a), vreinterpretq_s32_s8(b)))) +#define _mm_min_epu16(a, b) (vreinterpretq_s8_u16(vminq_u16(vreinterpretq_u16_s8(a), vreinterpretq_u16_s8(b)))) +#define _mm_min_epu32(a, b) (vreinterpretq_s8_u32(vminq_u32(vreinterpretq_u32_s8(a), vreinterpretq_u32_s8(b)))) + +static XSSE_FORCE_INLINE __m128i _mm_minpos_epu16(__m128i x) +{ + uint16x8_t x16 = vreinterpretq_u16_s8(x); + + uint16x4_t lo = vget_low_u16(x16); + uint16x4_t hi = vget_high_u16(x16); + uint16x4_t min4 = vmin_u16(lo, hi); + + uint16x4_t min2 = vpmin_u16(min4, min4); + uint16x4_t min1 = vpmin_u16(min2, min2); + + uint16_t min = (uint16_t) vget_lane_u16(min1, 0); + uint16x8_t repeat_min = vdupq_n_u16(min); + + uint16x8_t cmp = vceqq_u16(x16, repeat_min); + uint8x8_t narrowed_cmp = vmovn_u16(cmp); + uint64x1_t cmp_64 = vreinterpret_u64_u8(narrowed_cmp); + + uint64_t index_mask8 = (uint64_t) vget_lane_u64(cmp_64, 0); + + uint16_t index = 0; +#if (defined(__has_builtin) && __has_builtin(__builtin_ctzll)) || defined(__GNUC__) || defined(__clang__) + index = (uint16_t) __builtin_ctzll(index_mask8) / 8; +#else + for (int i = 0; i < 8; i++) { + if (index_mask8 & 1) { + index = (uint16_t) i; + break; + } + index_mask8 >>= 8; + } +#endif + return vreinterpretq_s8_u16((uint16x8_t) { min, index, 0, 0, 0, 0, 0, 0 }); +} + +static XSSE_FORCE_INLINE __m128i _mm_mul_epi32(__m128i a, __m128i b) +{ + int32x4_t a32 = vreinterpretq_s32_s8(a); + int32x4_t b32 = vreinterpretq_s32_s8(b); + int32x4_t evens = vuzp1q_s32(a32, b32); + int32x2_t lo = vget_low_s32(evens); + int32x2_t hi = vget_high_s32(evens); + return vreinterpretq_s8_s64(vmull_s32(lo, hi)); +} +static XSSE_FORCE_INLINE __m128i _mm_mullo_epi32(__m128i a, __m128i b) +{ + int32x4_t a32 = vreinterpretq_s32_s8(a); + int32x4_t b32 = vreinterpretq_s32_s8(b); + return vreinterpretq_s8_s32(vmulq_s32(a32, b32)); +} + +static XSSE_FORCE_INLINE __m128i _mm_mpsadbw_epu8(__m128i a, __m128i b, const int imm8) +{ + uint8x16_t ua = vreinterpretq_u8_s8(a); + + uint8x8_t a_sliced_0_4; + uint8x8_t a_sliced_1_5; + uint8x8_t a_sliced_2_6; + uint8x8_t a_sliced_3_7; + uint8x16_t a_pre_sliced_0_4; + uint8x16_t a_pre_sliced_1_5; + uint8x16_t a_pre_sliced_2_6; + uint8x16_t a_pre_sliced_3_7; + if ((imm8 >> 2) & 1) { + a_pre_sliced_0_4 = vextq_u8(ua, ua, 4); + a_pre_sliced_1_5 = vextq_u8(ua, ua, 5); + a_pre_sliced_2_6 = vextq_u8(ua, ua, 6); + a_pre_sliced_3_7 = vextq_u8(ua, ua, 7); + a_sliced_0_4 = vget_low_u8(a_pre_sliced_0_4); + } else { + a_pre_sliced_1_5 = vextq_u8(ua, ua, 1); + a_pre_sliced_2_6 = vextq_u8(ua, ua, 2); + a_pre_sliced_3_7 = vextq_u8(ua, ua, 3); + a_sliced_0_4 = vget_low_u8(ua); + } + a_sliced_1_5 = vget_low_u8(a_pre_sliced_1_5); + a_sliced_2_6 = vget_low_u8(a_pre_sliced_2_6); + a_sliced_3_7 = vget_low_u8(a_pre_sliced_3_7); + + uint32x4_t b32 = vreinterpretq_u32_s8(b); + uint8x8_t b_slicedx2; + switch (imm8 & 0x03) { + case 0: + b_slicedx2 = vreinterpret_u8_u32(vdup_n_u32(vgetq_lane_u32(b32, 0))); + break; + case 1: + b_slicedx2 = vreinterpret_u8_u32(vdup_n_u32(vgetq_lane_u32(b32, 1))); + break; + case 2: + b_slicedx2 = vreinterpret_u8_u32(vdup_n_u32(vgetq_lane_u32(b32, 2))); + break; + case 3: + b_slicedx2 = vreinterpret_u8_u32(vdup_n_u32(vgetq_lane_u32(b32, 3))); + break; + } + + uint16x8_t abs_diffs_0_4 = vabdl_u8(a_sliced_0_4, b_slicedx2); + uint16x8_t abs_diffs_1_5 = vabdl_u8(a_sliced_1_5, b_slicedx2); + uint16x8_t abs_diffs_2_6 = vabdl_u8(a_sliced_2_6, b_slicedx2); + uint16x8_t abs_diffs_3_7 = vabdl_u8(a_sliced_3_7, b_slicedx2); + + uint16x8_t abs_diffs_0_4_2_6 = vpaddq_u16(abs_diffs_0_4, abs_diffs_2_6); + uint16x8_t abs_diffs_1_5_3_7 = vpaddq_u16(abs_diffs_1_5, abs_diffs_3_7); + + uint32x4_t abs_diffs_0_4_2_6x32 = vreinterpretq_u32_u16(abs_diffs_0_4_2_6); + uint32x4_t abs_diffs_1_5_3_7x32 = vreinterpretq_u32_u16(abs_diffs_1_5_3_7); + uint32x4_t abs_diffs_0_1_2_3x32 = vtrn1q_u32(abs_diffs_0_4_2_6x32, abs_diffs_1_5_3_7x32); + uint32x4_t abs_diffs_4_5_6_7x32 = vtrn2q_u32(abs_diffs_0_4_2_6x32, abs_diffs_1_5_3_7x32); + + uint16x8_t abs_diffs_0_1_2_3 = vreinterpretq_u16_u32(abs_diffs_0_1_2_3x32); + uint16x8_t abs_diffs_4_5_6_7 = vreinterpretq_u16_u32(abs_diffs_4_5_6_7x32); + + return vreinterpretq_s8_u16(vpaddq_u16(abs_diffs_0_1_2_3, abs_diffs_4_5_6_7)); +} + + +/***************************************************************************** + * Comparison * + *****************************************************************************/ + +#define _mm_cmpeq_epi64(a, b) (vreinterpretq_s8_u64(vceqq_s64(vreinterpretq_s64_s8(a), vreinterpretq_s64_s8(b)))) + + +/***************************************************************************** + * Convert * + *****************************************************************************/ + +#define _mm_cvtepi8_epi16(x) (vreinterpretq_s8_s16(vmovl_s8(vget_low_s8(x)))) +#define _mm_cvtepi8_epi32(x) (vreinterpretq_s8_s32(vmovl_s16(vget_low_s16(vmovl_s8(vget_low_s8(x)))))) +#define _mm_cvtepi8_epi64(x) \ + (vreinterpretq_s8_s64(vmovl_s32(vget_low_s32(vmovl_s16(vget_low_s16(vmovl_s8(vget_low_s8(x)))))))) +#define _mm_cvtepi16_epi32(x) (vreinterpretq_s8_s32(vmovl_s16(vget_low_s16(vreinterpretq_s16_s8(x))))) +#define _mm_cvtepi16_epi64(x) \ + (vreinterpretq_s8_s64(vmovl_s32(vget_low_s32(vmovl_s16(vget_low_s16(vreinterpretq_s16_s8(x))))))) +#define _mm_cvtepi32_epi64(x) (vreinterpretq_s8_s64(vmovl_s32(vget_low_s32(vreinterpretq_s32_s8(x))))) + +#define _mm_cvtepu8_epi16(x) (vreinterpretq_s8_u16(vmovl_u8(vget_low_u8(vreinterpretq_u8_s8(x))))) +#define _mm_cvtepu8_epi32(x) (vreinterpretq_s8_u32(vmovl_u16(vget_low_u16(vmovl_u8(vget_low_u8(vreinterpretq_u8_s8(x))))))) +#define _mm_cvtepu8_epi64(x) \ + (vreinterpretq_s8_u64(vmovl_u32(vget_low_u32(vmovl_u16(vget_low_u16(vmovl_u8(vget_low_u8(vreinterpretq_u8_s8(x))))))))) +#define _mm_cvtepu16_epi32(x) (vreinterpretq_s8_u32(vmovl_u16(vget_low_u16(vreinterpretq_u16_s8(x))))) +#define _mm_cvtepu16_epi64(x) \ + (vreinterpretq_s8_u64(vmovl_u32(vget_low_u32(vmovl_u16(vget_low_u16(vreinterpretq_u16_s8(x))))))) +#define _mm_cvtepu32_epi64(x) (vreinterpretq_s8_u64(vmovl_u32(vget_low_u32(vreinterpretq_u32_s8(x))))) + + +/***************************************************************************** + * Tests * + *****************************************************************************/ + +static XSSE_FORCE_INLINE int _mm_test_all_ones(__m128i x) +{ + uint64x2_t x64 = vreinterpretq_u64_s8(x); + return (vgetq_lane_u64(x64, 0) == ~0ULL) && (vgetq_lane_u64(x64, 1) == ~0ULL); +} +static XSSE_FORCE_INLINE int _mm_test_all_zeros(__m128i mask, __m128i x) +{ + int8x16_t masked = vandq_s8(x, mask); + uint64x2_t masked64 = vreinterpretq_u64_s8(masked); + return (vgetq_lane_u64(masked64, 0) == 0) && (vgetq_lane_u64(masked64, 1) == 0); +} +static XSSE_FORCE_INLINE int _mm_test_mix_ones_zeros(__m128i mask, __m128i x) +{ + int8x16_t and = vandq_s8(x, mask); + uint64x2_t and64 = vreinterpretq_u64_s8(and); + int has_ones = (vgetq_lane_u64(and64, 0) | vgetq_lane_u64(and64, 1)) != 0; + + int8x16_t andnot = vbicq_s8(x, mask); + uint64x2_t andnot64 = vreinterpretq_u64_s8(andnot); + int has_zeros = (vgetq_lane_u64(andnot64, 0) | vgetq_lane_u64(andnot64, 1)) != 0; + + return has_ones && has_zeros; +} +static XSSE_FORCE_INLINE int _mm_testc_si128(__m128i a, __m128i b) +{ + int8x16_t andnot = vbicq_s8(b, a); + uint64x2_t andnot64 = vreinterpretq_u64_s8(andnot); + return (vgetq_lane_u64(andnot64, 0) == 0) && (vgetq_lane_u64(andnot64, 1) == 0); +} +#define _mm_testnzc_si128(a, b) _mm_test_mix_ones_zeros(a, b) +#define _mm_testz_si128(a, b) _mm_test_all_zeros(a, b) + + +/***************************************************************************** + * Others * + *****************************************************************************/ + +#define _mm_packus_epi32(a, b) \ + (vreinterpretq_s8_u16(vcombine_u16(vqmovun_s32(vreinterpretq_s32_s8(a)), vqmovun_s32(vreinterpretq_s32_s8(b))))) + +#define _mm_extract_epi8(x, imm) (vgetq_lane_s8((x), (imm))) +#define _mm_extract_epi32(x, imm) (vgetq_lane_s32(vreinterpretq_s32_s8(x), (imm))) +#define _mm_extract_epi64(x, imm) (vgetq_lane_s64(vreinterpretq_s64_s8(x), (imm))) + +#define _mm_insert_epi8(x, val, imm) (vsetq_lane_s8((int8_t) (val), (x), (imm))) +#define _mm_insert_epi32(x, val, imm) (vreinterpretq_s8_s32(vsetq_lane_s32((int32_t) (val), vreinterpretq_s32_s8(x), (imm)))) +#define _mm_insert_epi64(x, val, imm) (vreinterpretq_s8_s64(vsetq_lane_s64((int64_t) (val), vreinterpretq_s64_s8(x), (imm)))) + +#define _mm_blend_epi16(a, b, imm8) \ + (vreinterpretq_s8_s16(vbslq_s16( \ + (uint16x8_t){ \ + (imm8 & (1 << 0)) ? 0xFFFF : 0x0000, \ + (imm8 & (1 << 1)) ? 0xFFFF : 0x0000, \ + (imm8 & (1 << 2)) ? 0xFFFF : 0x0000, \ + (imm8 & (1 << 3)) ? 0xFFFF : 0x0000, \ + (imm8 & (1 << 4)) ? 0xFFFF : 0x0000, \ + (imm8 & (1 << 5)) ? 0xFFFF : 0x0000, \ + (imm8 & (1 << 6)) ? 0xFFFF : 0x0000, \ + (imm8 & (1 << 7)) ? 0xFFFF : 0x0000 \ + }, \ + vreinterpretq_s16_s8(b), \ + vreinterpretq_s16_s8(a) \ + ))) +static XSSE_FORCE_INLINE __m128i _mm_blendv_epi8(__m128i a, __m128i b, __m128i mask) +{ + uint8x16_t umask = vreinterpretq_u8_s8(mask); + uint8x16_t repeat_0x80 = vdupq_n_u8(0x80); + uint8x16_t mask_fill = vcgeq_u8(umask, repeat_0x80); + return vbslq_s8(mask_fill, b, a); +} + +#endif /* SSE4_1 */ + + +/***************************************************************************** + * * + * SSE4.2 * + * * + *****************************************************************************/ + +#if defined(__SSE4_2__) +#include +#define XSSE4_2 + + +#elif defined(__aarch64__) || defined(_M_ARM64) +#define XSSE4_2 + +/***************************************************************************** + * Comparison * + *****************************************************************************/ + +#define _mm_cmpgt_epi64(a, b) (vreinterpretq_s8_u64(vcgtq_s64(vreinterpretq_s64_s8(a), vreinterpretq_s64_s8(b)))) + + +/***************************************************************************** + * Packed Compare * + *****************************************************************************/ + +/* [1:0]*/ +#define _SIDD_UBYTE_OPS 0x00 +#define _SIDD_UWORD_OPS 0x01 +#define _SIDD_SBYTE_OPS 0x02 +#define _SIDD_SWORD_OPS 0x03 + +/* [3:2] */ +#define _SIDD_CMP_EQUAL_ANY 0x00 +#define _SIDD_CMP_RANGES 0x04 +#define _SIDD_CMP_EQUAL_EACH 0x08 +#define _SIDD_CMP_EQUAL_ORDERED 0x0C + +/* [5:4] */ +#define _SIDD_POSITIVE_POLARITY 0x00 +#define _SIDD_NEGATIVE_POLARITY 0x10 +#define _SIDD_MASKED_POSITIVE_POLARITY 0x20 +#define _SIDD_MASKED_NEGATIVE_POLARITY 0x30 + +/* [6] */ +#define _SIDD_LEAST_SIGNIFICANT 0x00 +#define _SIDD_MOST_SIGNIFICANT 0x40 + +/* [6] */ +#define _SIDD_BIT_MASK 0x00 +#define _SIDD_UNIT_MASK 0x40 + +typedef struct { + int8_t cf; + int8_t zf; + int8_t sf; + uint8x16_t mask; +} _xsse_pcmp_str_result_t; + +XSSE_ATTR_CONST +static XSSE_FORCE_INLINE int _xsse_seach_most_significant_byte_index(uint64_t mask) +{ +#if (defined(__has_builtin) && __has_builtin(__builtin_clzll)) || defined(__GNUC__) || defined(__clang__) + return (int) 7 - (__builtin_clzll(mask) / 8); +#else + for (int i = 0; i < 8; ++i) { + uint8_t byte = (mask >> ((7 - i) * 8)) & 0xFF; + if (byte != 0) { + return 7 - i; + } + } + XSSE_UNREACHABLE(); +#endif +} + +XSSE_ATTR_CONST +static XSSE_FORCE_INLINE int _xsse_seach_least_significant_byte_index(uint64_t mask) +{ +#if (defined(__has_builtin) && __has_builtin(__builtin_ctzll)) || defined(__GNUC__) || defined(__clang__) + return (int) __builtin_ctzll(mask) / 8; +#else + for (int i = 0; i < 8; i++) { + if (mask & 0xFF) { + return i; + } + mask >>= 8; + } + XSSE_UNREACHABLE(); +#endif +} + +XSSE_ATTR_CONST +static XSSE_FORCE_INLINE int _xsse_seach_most_significant_word_index(uint64_t mask) +{ +#if (defined(__has_builtin) && __has_builtin(__builtin_clzll)) || defined(__GNUC__) || defined(__clang__) + return (int) 3 - (__builtin_clzll(mask) / 16); +#else + for (int i = 0; i < 4; ++i) { + uint16_t byte = (mask >> ((3 - i) * 16)) & 0xFFFF; + if (byte != 0) { + return 3 - i; + } + } + XSSE_UNREACHABLE(); +#endif +} + +XSSE_ATTR_CONST +static XSSE_FORCE_INLINE int _xsse_seach_least_significant_word_index(uint64_t mask) +{ +#if (defined(__has_builtin) && __has_builtin(__builtin_ctzll)) || defined(__GNUC__) || defined(__clang__) + return (int) __builtin_ctzll(mask) / 16; +#else + for (int i = 0; i < 4; i++) { + if (mask & 0xFFFF) { + return i; + } + mask >>= 16; + } + XSSE_UNREACHABLE(); +#endif +} + +#ifndef XSSE_IS_OPTIMIZE +XSSE_ATTR_OPTIMIZE_O2 +#endif +XSSE_ATTR_CONST +static XSSE_FORCE_INLINE _xsse_pcmp_str_result_t _xsse_pcmp_str_core(const __m128i a, int la, const __m128i b, int lb, const int imm8, const int check_null) +{ + if (imm8 & 0x01) { + /* word */ +#define XSSE_PCMP_ANY_WORD(i, l) \ + tmp_cmp_##i = (imm8 & 0x02) ? vceqq_s16(vdupq_n_s16(vgetq_lane_s16(a16, l)), b16) : vceqq_u16(vdupq_n_u16(vgetq_lane_u16(ua16, l)), ub16) +#define XSSE_PCMP_RANGES_WORD(i, l) \ + tmp_cmp_##i = (imm8 & 0x02) \ + ? vandq_u16(vcleq_s16(vdupq_n_s16(vgetq_lane_s16(a16, (l))), b16), vcleq_s16(b16, vdupq_n_s16(vgetq_lane_s16(a16, (l + 1))))) \ + : vandq_u16(vcleq_u16(vdupq_n_u16(vgetq_lane_u16(ua16, (l))), ub16), vcleq_u16(ub16, vdupq_n_u16(vgetq_lane_u16(ua16, (l + 1))))) +#define XSSE_PCMP_EACH_WORD() cmp_ret = (imm8 & 0x02) ? vceqq_s16(a16, b16) : vceqq_u16(ua16, ub16) +#define XSSE_PCMP_ORDERED_WORD(i, l) \ + tmp_cmp_##i = (imm8 & 0x02) \ + ? vextq_u16(vbicq_u16(vceqq_s16(vdupq_n_s16(vgetq_lane_s16(a16, l)), b16), b_invalid_mask), repeat_full_bits, l) \ + : vextq_u16(vbicq_u16(vceqq_u16(vdupq_n_u16(vgetq_lane_u16(ua16, l)), ub16), b_invalid_mask), repeat_full_bits, l) + +#define XSSE_VORR_U16(a, b) tmp_cmp_##a = vorrq_u16(tmp_cmp_##a, tmp_cmp_##b); +#define XSSE_VAND_U16(a, b) tmp_cmp_##a = vandq_u16(tmp_cmp_##a, tmp_cmp_##b); + + if (check_null) { + uint16x8_t repeat_nul = vdupq_n_u16(0); + uint16x8_t cmp_nul_a = vceqq_u16(vreinterpretq_u16_s8(a), repeat_nul); + uint16x8_t cmp_nul_b = vceqq_u16(vreinterpretq_u16_s8(b), repeat_nul); + + uint64_t low_a = vgetq_lane_u64(vreinterpretq_u64_u16(cmp_nul_a), 0); + uint64_t high_a = vgetq_lane_u64(vreinterpretq_u64_u16(cmp_nul_a), 1); + if (low_a != 0) { + la = _xsse_seach_least_significant_word_index(low_a); + } else if (high_a != 0) { + la = _xsse_seach_least_significant_word_index(high_a) + 4; + } else { + la = 8; + } + + uint64_t low_b = vgetq_lane_u64(vreinterpretq_u64_u16(cmp_nul_b), 0); + uint64_t high_b = vgetq_lane_u64(vreinterpretq_u64_u16(cmp_nul_b), 1); + if (low_b != 0) { + lb = _xsse_seach_least_significant_word_index(low_b); + } else if (high_b != 0) { + lb = _xsse_seach_least_significant_word_index(high_b) + 4; + } else { + lb = 8; + } + } + + uint16x8_t cmp_ret; + uint16x8_t lanes = { 0, 1, 2, 3, 4, 5, 6, 7 }; + uint16x8_t a_threshold = vdupq_n_u16(la); + uint16x8_t b_threshold = vdupq_n_u16(lb); + uint16x8_t a_invalid_mask = vcgeq_u16(lanes, a_threshold); + uint16x8_t b_invalid_mask = vcgeq_u16(lanes, b_threshold); + + int16x8_t a16, b16; + uint16x8_t ua16, ub16; + if (imm8 & 0x02) { + a16 = vreinterpretq_s16_s8(a); + b16 = vreinterpretq_s16_s8(b); + } else { + ua16 = vreinterpretq_u16_s8(a); + ub16 = vreinterpretq_u16_s8(b); + } + + /* mode */ + switch ((imm8 >> 2) & 0x03) { + case _SIDD_CMP_EQUAL_ANY >> 2: + { + cmp_ret = vdupq_n_u16(0); + if (la >= 8) { + uint16x8_t tmp_cmp_0, tmp_cmp_1, tmp_cmp_2, tmp_cmp_3, tmp_cmp_4, tmp_cmp_5, tmp_cmp_6, tmp_cmp_7; + XSSE_PCMP_ANY_WORD(0, 0); XSSE_PCMP_ANY_WORD(1, 1); XSSE_PCMP_ANY_WORD(2, 2); XSSE_PCMP_ANY_WORD(3, 3); + XSSE_PCMP_ANY_WORD(4, 4); XSSE_PCMP_ANY_WORD(5, 5); XSSE_PCMP_ANY_WORD(6, 6); XSSE_PCMP_ANY_WORD(7, 7); + + XSSE_VORR_U16(0, 1); XSSE_VORR_U16(2, 3); XSSE_VORR_U16(4, 5); XSSE_VORR_U16(6, 7); + XSSE_VORR_U16(0, 2); XSSE_VORR_U16(4, 6); XSSE_VORR_U16(0, 4); + cmp_ret = vorrq_u16(cmp_ret, tmp_cmp_0); + } else { + int checked = 0; + int la2 = la; + if (la2 >= 4) { + uint16x8_t tmp_cmp_0, tmp_cmp_1, tmp_cmp_2, tmp_cmp_3; + XSSE_PCMP_ANY_WORD(0, 0); XSSE_PCMP_ANY_WORD(1, 1); XSSE_PCMP_ANY_WORD(2, 2); XSSE_PCMP_ANY_WORD(3, 3); + XSSE_VORR_U16(0, 1); XSSE_VORR_U16(2, 3); XSSE_VORR_U16(0, 2); + cmp_ret = vorrq_u16(cmp_ret, tmp_cmp_0); + checked = 4; + la2 -= 4; + } + if (la2 >= 2) { + uint16x8_t tmp_cmp_0, tmp_cmp_1; + if (checked == 4) { + XSSE_PCMP_ANY_WORD(0, 4); XSSE_PCMP_ANY_WORD(1, 5); + } else { + XSSE_PCMP_ANY_WORD(0, 0); XSSE_PCMP_ANY_WORD(1, 1); + } + XSSE_VORR_U16(0, 1); + cmp_ret = vorrq_u16(cmp_ret, tmp_cmp_0); + checked += 2; + la2 -= 2; + } + if (la2 >= 1) { + uint16x8_t tmp_cmp_0; + switch (checked) { + case 6: XSSE_PCMP_ANY_WORD(0, 6); break; + case 4: XSSE_PCMP_ANY_WORD(0, 4); break; + case 2: XSSE_PCMP_ANY_WORD(0, 2); break; + case 0: XSSE_PCMP_ANY_WORD(0, 0); break; + default: XSSE_UNREACHABLE(); + } + cmp_ret = vorrq_u16(cmp_ret, tmp_cmp_0); + } + } + cmp_ret = vbicq_u16(cmp_ret, b_invalid_mask); + } + break; + + case _SIDD_CMP_RANGES >> 2: + { + cmp_ret = vdupq_n_u16(0); + if (la >= 8) { + uint16x8_t tmp_cmp_0, tmp_cmp_1, tmp_cmp_2, tmp_cmp_3; + XSSE_PCMP_RANGES_WORD(0, 0); XSSE_PCMP_RANGES_WORD(1, 2); XSSE_PCMP_RANGES_WORD(2, 4); XSSE_PCMP_RANGES_WORD(3, 6); + XSSE_VORR_U16(0, 1); XSSE_VORR_U16(2, 3); XSSE_VORR_U16(0, 2); + cmp_ret = vorrq_u16(cmp_ret, tmp_cmp_0); + } else { + int checked = 0; + int la2 = la; + if (la2 >= 4) { + uint16x8_t tmp_cmp_0, tmp_cmp_1; + XSSE_PCMP_RANGES_WORD(0, 0); XSSE_PCMP_RANGES_WORD(1, 2); + XSSE_VORR_U16(0, 1); + cmp_ret = vorrq_u16(cmp_ret, tmp_cmp_0); + checked = 4; + la2 -= 4; + } + if (la2 >= 2) { + uint16x8_t tmp_cmp_0; + if (checked == 4) { + XSSE_PCMP_RANGES_WORD(0, 4); + } else { + XSSE_PCMP_RANGES_WORD(0, 0); + } + cmp_ret = vorrq_u16(cmp_ret, tmp_cmp_0); + } + } + cmp_ret = vbicq_u16(cmp_ret, b_invalid_mask); + } + break; + + case _SIDD_CMP_EQUAL_EACH >> 2: + { + uint16x8_t and_invalid_mask = vandq_u16(a_invalid_mask, b_invalid_mask); + uint16x8_t xor_invalid_mask = veorq_u16(a_invalid_mask, b_invalid_mask); + XSSE_PCMP_EACH_WORD(); + cmp_ret = vorrq_u16(cmp_ret, and_invalid_mask); + cmp_ret = vbicq_u16(cmp_ret, xor_invalid_mask); + } + break; + + case _SIDD_CMP_EQUAL_ORDERED >> 2: + { + cmp_ret = vdupq_n_u16(0xFFFF); + uint16x8_t repeat_full_bits = vdupq_n_u16(0xFFFF); + if (la >= 8) { + uint16x8_t tmp_cmp_0, tmp_cmp_1, tmp_cmp_2, tmp_cmp_3, tmp_cmp_4, tmp_cmp_5, tmp_cmp_6, tmp_cmp_7; + XSSE_PCMP_ORDERED_WORD(0, 0); XSSE_PCMP_ORDERED_WORD(1, 1); XSSE_PCMP_ORDERED_WORD(2, 2); XSSE_PCMP_ORDERED_WORD(3, 3); + XSSE_PCMP_ORDERED_WORD(4, 4); XSSE_PCMP_ORDERED_WORD(5, 5); XSSE_PCMP_ORDERED_WORD(6, 6); XSSE_PCMP_ORDERED_WORD(7, 7); + + XSSE_VAND_U16(0, 1); XSSE_VAND_U16(2, 3); XSSE_VAND_U16(4, 5); XSSE_VAND_U16(6, 7); + XSSE_VAND_U16(0, 2); XSSE_VAND_U16(4, 6); XSSE_VAND_U16(0, 4); + cmp_ret = vandq_u16(cmp_ret, tmp_cmp_0); + } else { + int checked = 0; + int la2 = la; + if (la2 >= 4) { + uint16x8_t tmp_cmp_0, tmp_cmp_1, tmp_cmp_2, tmp_cmp_3; + XSSE_PCMP_ORDERED_WORD(0, 0); XSSE_PCMP_ORDERED_WORD(1, 1); XSSE_PCMP_ORDERED_WORD(2, 2); XSSE_PCMP_ORDERED_WORD(3, 3); + XSSE_VAND_U16(0, 1); XSSE_VAND_U16(2, 3); XSSE_VAND_U16(0, 2); + cmp_ret = vandq_u16(cmp_ret, tmp_cmp_0); + checked = 4; + la2 -= 4; + } + if (la2 >= 2) { + uint16x8_t tmp_cmp_0, tmp_cmp_1; + if (checked == 4) { + XSSE_PCMP_ORDERED_WORD(0, 4); XSSE_PCMP_ORDERED_WORD(1, 5); + } else { + XSSE_PCMP_ORDERED_WORD(0, 0); XSSE_PCMP_ORDERED_WORD(1, 1); + } + XSSE_VAND_U16(0, 1); + cmp_ret = vandq_u16(cmp_ret, tmp_cmp_0); + checked += 2; + la2 -= 2; + } + if (la2 >= 1) { + uint16x8_t tmp_cmp_0; + switch (checked) { + case 6: XSSE_PCMP_ORDERED_WORD(0, 6); break; + case 4: XSSE_PCMP_ORDERED_WORD(0, 4); break; + case 2: XSSE_PCMP_ORDERED_WORD(0, 2); break; + case 0: XSSE_PCMP_ORDERED_WORD(0, 0); break; + default: XSSE_UNREACHABLE(); + } + cmp_ret = vandq_u16(cmp_ret, tmp_cmp_0); + } + } + } + break; + } + + /* negate */ + if (imm8 & _SIDD_NEGATIVE_POLARITY) { + uint16x8_t not_cmp_ret = vmvnq_u16(cmp_ret); + if (imm8 & _SIDD_MASKED_NEGATIVE_POLARITY) { + cmp_ret = vbslq_u16(cmp_ret, not_cmp_ret, b_invalid_mask); + } else { + cmp_ret = not_cmp_ret; + } + } + + uint64x2_t cmp_ret_64 = vreinterpretq_u64_u16(cmp_ret); + + _xsse_pcmp_str_result_t result; + result.cf = (vgetq_lane_u64(cmp_ret_64, 0) | vgetq_lane_u64(cmp_ret_64, 1)) != 0; + result.zf = lb < 8; + result.sf = la < 8; + result.mask = vreinterpretq_u8_u16(cmp_ret); + return result; + +#undef XSSE_VAND_U16 +#undef XSSE_VORR_U16 +#undef XSSE_PCMP_ORDERED_WORD +#undef XSSE_PCMP_EACH_WORD +#undef XSSE_PCMP_ANY_WORD +#undef XSSE_VORR_U16 +#undef XSSE_VAND_U16 + } else { + /* byte */ +#define XSSE_PCMP_ANY_BYTE(i, l) \ + tmp_cmp_##i = (imm8 & 0x02) ? vceqq_s8(vdupq_n_s8(vgetq_lane_s8(a, l)), b) : vceqq_u8(vdupq_n_u8(vgetq_lane_u8(ua, l)), ub) +#define XSSE_PCMP_RANGES_BYTE(i, l) \ + tmp_cmp_##i = (imm8 & 0x02) \ + ? vandq_u8(vcleq_s8(vdupq_n_s8(vgetq_lane_s8(a, (l))), b), vcleq_s8(b, vdupq_n_s8(vgetq_lane_s8(a, (l + 1))))) \ + : vandq_u8(vcleq_u8(vdupq_n_u8(vgetq_lane_u8(ua, (l))), ub), vcleq_u8(ub, vdupq_n_u8(vgetq_lane_u8(ua, (l + 1))))) +#define XSSE_PCMP_EACH_BYTE() cmp_ret = (imm8 & 0x02) ? vceqq_s8(a, b) : vceqq_u8(ua, ub) +#define XSSE_PCMP_ORDERED_BYTE(i, l) \ + tmp_cmp_##i = (imm8 & 0x02) \ + ? vextq_u8(vbicq_u8(vceqq_s8(vdupq_n_s8(vgetq_lane_s8(a, l)), b), b_invalid_mask), repeat_full_bits, l) \ + : vextq_u8(vbicq_u8(vceqq_u8(vdupq_n_u8(vgetq_lane_u8(ua, l)), ub), b_invalid_mask), repeat_full_bits, l) + +#define XSSE_VORR_U8(a, b) tmp_cmp_##a = vorrq_u8(tmp_cmp_##a, tmp_cmp_##b); +#define XSSE_VAND_U8(a, b) tmp_cmp_##a = vandq_u8(tmp_cmp_##a, tmp_cmp_##b); + + if (check_null) { + uint8x16_t repeat_nul = vdupq_n_u8(0); + uint8x16_t cmp_nul_a = vceqq_u8(vreinterpretq_u8_s8(a), repeat_nul); + uint8x16_t cmp_nul_b = vceqq_u8(vreinterpretq_u8_s8(b), repeat_nul); + + uint64_t low_a = vgetq_lane_u64(vreinterpretq_u64_u8(cmp_nul_a), 0); + uint64_t high_a = vgetq_lane_u64(vreinterpretq_u64_u8(cmp_nul_a), 1); + if (low_a != 0) { + la = _xsse_seach_least_significant_byte_index(low_a); + } else if (high_a != 0) { + la = _xsse_seach_least_significant_byte_index(high_a) + 8; + } else { + la = 16; + } + + uint64_t low_b = vgetq_lane_u64(vreinterpretq_u64_u8(cmp_nul_b), 0); + uint64_t high_b = vgetq_lane_u64(vreinterpretq_u64_u8(cmp_nul_b), 1); + if (low_b != 0) { + lb = _xsse_seach_least_significant_byte_index(low_b); + } else if (high_b != 0) { + lb = _xsse_seach_least_significant_byte_index(high_b) + 8; + } else { + lb = 16; + } + } + + uint8x16_t cmp_ret; + uint8x16_t lanes = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; + uint8x16_t a_threshold = vdupq_n_u8(la); + uint8x16_t b_threshold = vdupq_n_u8(lb); + uint8x16_t a_invalid_mask = vcgeq_u8(lanes, a_threshold); + uint8x16_t b_invalid_mask = vcgeq_u8(lanes, b_threshold); + + uint8x16_t ua, ub; + if ((imm8 & 0x02) == 0) { + ua = vreinterpretq_u8_s8(a); + ub = vreinterpretq_u8_s8(b); + } + + /* mode */ + switch ((imm8 >> 2) & 0x03) { + case _SIDD_CMP_EQUAL_ANY >> 2: + { + cmp_ret = vdupq_n_u8(0); + if (la >= 16) { + uint8x16_t tmp_cmp_0, tmp_cmp_1, tmp_cmp_2, tmp_cmp_3, tmp_cmp_4, tmp_cmp_5, tmp_cmp_6, tmp_cmp_7; + uint8x16_t tmp_cmp_8, tmp_cmp_9, tmp_cmp_10, tmp_cmp_11, tmp_cmp_12, tmp_cmp_13, tmp_cmp_14, tmp_cmp_15; + XSSE_PCMP_ANY_BYTE(0, 0); XSSE_PCMP_ANY_BYTE(1, 1); XSSE_PCMP_ANY_BYTE(2, 2); XSSE_PCMP_ANY_BYTE(3, 3); + XSSE_PCMP_ANY_BYTE(4, 4); XSSE_PCMP_ANY_BYTE(5, 5); XSSE_PCMP_ANY_BYTE(6, 6); XSSE_PCMP_ANY_BYTE(7, 7); + XSSE_PCMP_ANY_BYTE(8, 8); XSSE_PCMP_ANY_BYTE(9, 9); XSSE_PCMP_ANY_BYTE(10, 10); XSSE_PCMP_ANY_BYTE(11, 11); + XSSE_PCMP_ANY_BYTE(12, 12); XSSE_PCMP_ANY_BYTE(13, 13); XSSE_PCMP_ANY_BYTE(14, 14); XSSE_PCMP_ANY_BYTE(15, 15); + + XSSE_VORR_U8(0, 1); XSSE_VORR_U8(2, 3); XSSE_VORR_U8(4, 5); XSSE_VORR_U8(6, 7); + XSSE_VORR_U8(8, 9); XSSE_VORR_U8(10, 11); XSSE_VORR_U8(12, 13); XSSE_VORR_U8(14, 15); + XSSE_VORR_U8(0, 2); XSSE_VORR_U8(4, 6); XSSE_VORR_U8(8, 10); XSSE_VORR_U8(12, 14); + XSSE_VORR_U8(0, 4); XSSE_VORR_U8(8, 12); XSSE_VORR_U8(0, 8); + cmp_ret = vorrq_u8(cmp_ret, tmp_cmp_0); + } else { + int checked = 0; + int la2 = la; + if (la2 >= 8) { + uint8x16_t tmp_cmp_0, tmp_cmp_1, tmp_cmp_2, tmp_cmp_3, tmp_cmp_4, tmp_cmp_5, tmp_cmp_6, tmp_cmp_7; + XSSE_PCMP_ANY_BYTE(0, 0); XSSE_PCMP_ANY_BYTE(1, 1); XSSE_PCMP_ANY_BYTE(2, 2); XSSE_PCMP_ANY_BYTE(3, 3); + XSSE_PCMP_ANY_BYTE(4, 4); XSSE_PCMP_ANY_BYTE(5, 5); XSSE_PCMP_ANY_BYTE(6, 6); XSSE_PCMP_ANY_BYTE(7, 7); + + XSSE_VORR_U8(0, 1); XSSE_VORR_U8(2, 3); XSSE_VORR_U8(4, 5); XSSE_VORR_U8(6, 7); + XSSE_VORR_U8(0, 2); XSSE_VORR_U8(4, 6); XSSE_VORR_U8(0, 4); + cmp_ret = vorrq_u8(cmp_ret, tmp_cmp_0); + checked = 8; + la2 -= 8; + } + if (la2 >= 4) { + uint8x16_t tmp_cmp_0, tmp_cmp_1, tmp_cmp_2, tmp_cmp_3; + if (checked == 8) { + XSSE_PCMP_ANY_BYTE(0, 8); XSSE_PCMP_ANY_BYTE(1, 9); XSSE_PCMP_ANY_BYTE(2, 10); XSSE_PCMP_ANY_BYTE(3, 11); + } else { + XSSE_PCMP_ANY_BYTE(0, 0); XSSE_PCMP_ANY_BYTE(1, 1); XSSE_PCMP_ANY_BYTE(2, 2); XSSE_PCMP_ANY_BYTE(3, 3); + } + XSSE_VORR_U8(0, 1); XSSE_VORR_U8(2, 3); XSSE_VORR_U8(0, 2); + cmp_ret = vorrq_u8(cmp_ret, tmp_cmp_0); + checked += 4; + la2 -= 4; + } + if (la2 >= 2) { + uint8x16_t tmp_cmp_0, tmp_cmp_1; + switch (checked) { + case 12: XSSE_PCMP_ANY_BYTE(0, 12); XSSE_PCMP_ANY_BYTE(1, 13); break; + case 8: XSSE_PCMP_ANY_BYTE(0, 8); XSSE_PCMP_ANY_BYTE(1, 9); break; + case 4: XSSE_PCMP_ANY_BYTE(0, 4); XSSE_PCMP_ANY_BYTE(1, 5); break; + case 0: XSSE_PCMP_ANY_BYTE(0, 0); XSSE_PCMP_ANY_BYTE(1, 1); break; + default: XSSE_UNREACHABLE(); + } + XSSE_VORR_U8(0, 1); + cmp_ret = vorrq_u8(cmp_ret, tmp_cmp_0); + checked += 2; + la2 -= 2; + } + if (la2 == 1) { + uint8x16_t tmp_cmp_0; + switch (checked) { + case 14: XSSE_PCMP_ANY_BYTE(0, 14); break; + case 12: XSSE_PCMP_ANY_BYTE(0, 12); break; + case 10: XSSE_PCMP_ANY_BYTE(0, 10); break; + case 8: XSSE_PCMP_ANY_BYTE(0, 8); break; + case 6: XSSE_PCMP_ANY_BYTE(0, 6); break; + case 4: XSSE_PCMP_ANY_BYTE(0, 4); break; + case 2: XSSE_PCMP_ANY_BYTE(0, 2); break; + case 0: XSSE_PCMP_ANY_BYTE(0, 0); break; + default: XSSE_UNREACHABLE(); + } + cmp_ret = vorrq_u8(cmp_ret, tmp_cmp_0); + } + } + cmp_ret = vbicq_u8(cmp_ret, b_invalid_mask); + } + break; + + case _SIDD_CMP_RANGES >> 2: + { + cmp_ret = vdupq_n_u8(0); + if (la >= 16) { + uint8x16_t tmp_cmp_0, tmp_cmp_1, tmp_cmp_2, tmp_cmp_3, tmp_cmp_4, tmp_cmp_5, tmp_cmp_6, tmp_cmp_7; + XSSE_PCMP_RANGES_BYTE(0, 0); XSSE_PCMP_RANGES_BYTE(1, 2); XSSE_PCMP_RANGES_BYTE(2, 4); XSSE_PCMP_RANGES_BYTE(3, 6); + XSSE_PCMP_RANGES_BYTE(4, 8); XSSE_PCMP_RANGES_BYTE(5, 10); XSSE_PCMP_RANGES_BYTE(6, 12); XSSE_PCMP_RANGES_BYTE(7, 14); + + XSSE_VORR_U8(0, 1); XSSE_VORR_U8(2, 3); XSSE_VORR_U8(4, 5); XSSE_VORR_U8(6, 7); + XSSE_VORR_U8(0, 2); XSSE_VORR_U8(4, 6); XSSE_VORR_U8(0, 4); + cmp_ret = vorrq_u8(cmp_ret, tmp_cmp_0); + } else { + int checked = 0; + int la2 = la; + if (la2 >= 8) { + uint8x16_t tmp_cmp_0, tmp_cmp_1, tmp_cmp_2, tmp_cmp_3; + XSSE_PCMP_RANGES_BYTE(0, 0); XSSE_PCMP_RANGES_BYTE(1, 2); XSSE_PCMP_RANGES_BYTE(2, 4); XSSE_PCMP_RANGES_BYTE(3, 6); + + XSSE_VORR_U8(0, 1); XSSE_VORR_U8(2, 3); XSSE_VORR_U8(0, 2); + cmp_ret = vorrq_u8(cmp_ret, tmp_cmp_0); + checked = 8; + la2 -= 8; + } + if (la2 >= 4) { + uint8x16_t tmp_cmp_0, tmp_cmp_1; + if (checked == 8) { + XSSE_PCMP_RANGES_BYTE(0, 8); XSSE_PCMP_RANGES_BYTE(1, 10); + } else { + XSSE_PCMP_RANGES_BYTE(0, 0); XSSE_PCMP_RANGES_BYTE(1, 2); + } + XSSE_VORR_U8(0, 1); + cmp_ret = vorrq_u8(cmp_ret, tmp_cmp_0); + checked += 4; + la2 -= 4; + } + if (la2 >= 2) { + uint8x16_t tmp_cmp_0; + switch (checked) { + case 12: XSSE_PCMP_RANGES_BYTE(0, 12); break; + case 8: XSSE_PCMP_RANGES_BYTE(0, 8); break; + case 4: XSSE_PCMP_RANGES_BYTE(0, 4); break; + case 0: XSSE_PCMP_RANGES_BYTE(0, 0); break; + default: XSSE_UNREACHABLE(); + } + cmp_ret = vorrq_u8(cmp_ret, tmp_cmp_0); + } + } + cmp_ret = vbicq_u8(cmp_ret, b_invalid_mask); + } + break; + + case _SIDD_CMP_EQUAL_EACH >> 2: + { + uint8x16_t and_invalid_mask = vandq_u8(a_invalid_mask, b_invalid_mask); + uint8x16_t xor_invalid_mask = veorq_u8(a_invalid_mask, b_invalid_mask); + XSSE_PCMP_EACH_BYTE(); + cmp_ret = vorrq_u8(cmp_ret, and_invalid_mask); + cmp_ret = vbicq_u8(cmp_ret, xor_invalid_mask); + } + break; + + case _SIDD_CMP_EQUAL_ORDERED >> 2: + { + cmp_ret = vdupq_n_u8(0xFF); + uint8x16_t repeat_full_bits = vdupq_n_u8(0xFF); + if (la >= 16) { + uint8x16_t tmp_cmp_0, tmp_cmp_1, tmp_cmp_2, tmp_cmp_3, tmp_cmp_4, tmp_cmp_5, tmp_cmp_6, tmp_cmp_7; + uint8x16_t tmp_cmp_8, tmp_cmp_9, tmp_cmp_10, tmp_cmp_11, tmp_cmp_12, tmp_cmp_13, tmp_cmp_14, tmp_cmp_15; + XSSE_PCMP_ORDERED_BYTE(0, 0); XSSE_PCMP_ORDERED_BYTE(1, 1); XSSE_PCMP_ORDERED_BYTE(2, 2); XSSE_PCMP_ORDERED_BYTE(3, 3); + XSSE_PCMP_ORDERED_BYTE(4, 4); XSSE_PCMP_ORDERED_BYTE(5, 5); XSSE_PCMP_ORDERED_BYTE(6, 6); XSSE_PCMP_ORDERED_BYTE(7, 7); + XSSE_PCMP_ORDERED_BYTE(8, 8); XSSE_PCMP_ORDERED_BYTE(9, 9); XSSE_PCMP_ORDERED_BYTE(10, 10); XSSE_PCMP_ORDERED_BYTE(11, 11); + XSSE_PCMP_ORDERED_BYTE(12, 12); XSSE_PCMP_ORDERED_BYTE(13, 13); XSSE_PCMP_ORDERED_BYTE(14, 14); XSSE_PCMP_ORDERED_BYTE(15, 15); + + XSSE_VAND_U8(0, 1); XSSE_VAND_U8(2, 3); XSSE_VAND_U8(4, 5); XSSE_VAND_U8(6, 7); + XSSE_VAND_U8(8, 9); XSSE_VAND_U8(10, 11); XSSE_VAND_U8(12, 13); XSSE_VAND_U8(14, 15); + XSSE_VAND_U8(0, 2); XSSE_VAND_U8(4, 6); XSSE_VAND_U8(8, 10); XSSE_VAND_U8(12, 14); + XSSE_VAND_U8(0, 4); XSSE_VAND_U8(8, 12); XSSE_VAND_U8(0, 8); + cmp_ret = vandq_u8(cmp_ret, tmp_cmp_0); + } else { + int checked = 0; + int la2 = la; + if (la2 >= 8) { + uint8x16_t tmp_cmp_0, tmp_cmp_1, tmp_cmp_2, tmp_cmp_3, tmp_cmp_4, tmp_cmp_5, tmp_cmp_6, tmp_cmp_7; + XSSE_PCMP_ORDERED_BYTE(0, 0); XSSE_PCMP_ORDERED_BYTE(1, 1); XSSE_PCMP_ORDERED_BYTE(2, 2); XSSE_PCMP_ORDERED_BYTE(3, 3); + XSSE_PCMP_ORDERED_BYTE(4, 4); XSSE_PCMP_ORDERED_BYTE(5, 5); XSSE_PCMP_ORDERED_BYTE(6, 6); XSSE_PCMP_ORDERED_BYTE(7, 7); + + XSSE_VAND_U8(0, 1); XSSE_VAND_U8(2, 3); XSSE_VAND_U8(4, 5); XSSE_VAND_U8(6, 7); + XSSE_VAND_U8(0, 2); XSSE_VAND_U8(4, 6); XSSE_VAND_U8(0, 4); + cmp_ret = vandq_u8(cmp_ret, tmp_cmp_0); + checked = 8; + la2 -= 8; + } + if (la2 >= 4) { + uint8x16_t tmp_cmp_0, tmp_cmp_1, tmp_cmp_2, tmp_cmp_3; + if (checked == 8) { + XSSE_PCMP_ORDERED_BYTE(0, 8); XSSE_PCMP_ORDERED_BYTE(1, 9); XSSE_PCMP_ORDERED_BYTE(2, 10); XSSE_PCMP_ORDERED_BYTE(3, 11); + } else { + XSSE_PCMP_ORDERED_BYTE(0, 0); XSSE_PCMP_ORDERED_BYTE(1, 1); XSSE_PCMP_ORDERED_BYTE(2, 2); XSSE_PCMP_ORDERED_BYTE(3, 3); + } + XSSE_VAND_U8(0, 1); XSSE_VAND_U8(2, 3); XSSE_VAND_U8(0, 2); + cmp_ret = vandq_u8(cmp_ret, tmp_cmp_0); + checked += 4; + la2 -= 4; + } + if (la2 >= 2) { + uint8x16_t tmp_cmp_0, tmp_cmp_1; + switch (checked) { + case 12: XSSE_PCMP_ORDERED_BYTE(0, 12); XSSE_PCMP_ORDERED_BYTE(1, 13); break; + case 8: XSSE_PCMP_ORDERED_BYTE(0, 8); XSSE_PCMP_ORDERED_BYTE(1, 9); break; + case 4: XSSE_PCMP_ORDERED_BYTE(0, 4); XSSE_PCMP_ORDERED_BYTE(1, 5); break; + case 0: XSSE_PCMP_ORDERED_BYTE(0, 0); XSSE_PCMP_ORDERED_BYTE(1, 1); break; + default: XSSE_UNREACHABLE(); + } + XSSE_VAND_U8(0, 1); + cmp_ret = vandq_u8(cmp_ret, tmp_cmp_0); + checked += 2; + la2 -= 2; + } + if (la2 == 1) { + uint8x16_t tmp_cmp_0; + switch (checked) { + case 14: XSSE_PCMP_ORDERED_BYTE(0, 14); break; + case 12: XSSE_PCMP_ORDERED_BYTE(0, 12); break; + case 10: XSSE_PCMP_ORDERED_BYTE(0, 10); break; + case 8: XSSE_PCMP_ORDERED_BYTE(0, 8); break; + case 6: XSSE_PCMP_ORDERED_BYTE(0, 6); break; + case 4: XSSE_PCMP_ORDERED_BYTE(0, 4); break; + case 2: XSSE_PCMP_ORDERED_BYTE(0, 2); break; + case 0: XSSE_PCMP_ORDERED_BYTE(0, 0); break; + default: XSSE_UNREACHABLE(); + } + cmp_ret = vandq_u8(cmp_ret, tmp_cmp_0); + } + } + } + break; + } + + /* negate */ + if (imm8 & _SIDD_NEGATIVE_POLARITY) { + uint8x16_t not_cmp_ret = vmvnq_u8(cmp_ret); + if (imm8 & _SIDD_MASKED_NEGATIVE_POLARITY) { + cmp_ret = vbslq_u8(cmp_ret, not_cmp_ret, b_invalid_mask); + } else { + cmp_ret = not_cmp_ret; + } + } + + uint64x2_t cmp_ret_64 = vreinterpretq_u64_u8(cmp_ret); + + _xsse_pcmp_str_result_t result; + result.cf = (vgetq_lane_u64(cmp_ret_64, 0) | vgetq_lane_u64(cmp_ret_64, 1)) != 0; + result.zf = lb < 16; + result.sf = la < 16; + result.mask = cmp_ret; + return result; + +#undef XSSE_PCMP_ANY_BYTE +#undef XSSE_PCMP_RANGES_BYTE +#undef XSSE_PCMP_EACH_BYTE +#undef XSSE_PCMP_EACH_BYTE +#undef XSSE_PCMP_ORDERED_BYTE +#undef XSSE_VORR_U8 +#undef XSSE_VAND_U8 + } +} + +XSSE_ATTR_CONST +static XSSE_FORCE_INLINE int _xsse_pcmp_str_a(const __m128i a, const int la, const __m128i b, const int lb, const int imm8, const int check_null) +{ + _xsse_pcmp_str_result_t result = _xsse_pcmp_str_core(a, la, b, lb, imm8 & 0x3F, check_null); + return result.cf == 0 && result.zf == 0; +} + +XSSE_ATTR_CONST +static XSSE_FORCE_INLINE int _xsse_pcmp_str_c(const __m128i a, const int la, const __m128i b, const int lb, const int imm8, const int check_null) +{ + _xsse_pcmp_str_result_t result = _xsse_pcmp_str_core(a, la, b, lb, imm8 & 0x3F, check_null); + return result.cf; +} + +XSSE_ATTR_CONST +static XSSE_FORCE_INLINE int _xsse_pcmp_str_i(const __m128i a, const int la, const __m128i b, const int lb, const int imm8, const int check_null) +{ + _xsse_pcmp_str_result_t result = _xsse_pcmp_str_core(a, la, b, lb, imm8 & 0x3F, check_null); + + uint64_t low = vgetq_lane_u64(vreinterpretq_u64_u8(result.mask), 0); + uint64_t high = vgetq_lane_u64(vreinterpretq_u64_u8(result.mask), 1); + if (imm8 & 0x01) { + if (imm8 & _SIDD_MOST_SIGNIFICANT) { + if (high != 0) { + return _xsse_seach_most_significant_word_index(high) + 4; + } else if (low != 0) { + return _xsse_seach_most_significant_word_index(low); + } else { + return 8; + } + } else { + if (low != 0) { + return _xsse_seach_least_significant_word_index(low); + } else if (high != 0) { + return _xsse_seach_least_significant_word_index(high) + 4; + } else { + return 8; + } + } + } else { + if (imm8 & _SIDD_MOST_SIGNIFICANT) { + if (high != 0) { + return _xsse_seach_most_significant_byte_index(high) + 8; + } else if (low != 0) { + return _xsse_seach_most_significant_byte_index(low); + } else { + return 16; + } + } else { + if (low != 0) { + return _xsse_seach_least_significant_byte_index(low); + } else if (high != 0) { + return _xsse_seach_least_significant_byte_index(high) + 8; + } else { + return 16; + } + } + } +} + +XSSE_ATTR_CONST +static XSSE_FORCE_INLINE __m128i _xsse_pcmp_str_m(const __m128i a, const int la, const __m128i b, const int lb, const int imm8, const int check_null) +{ + _xsse_pcmp_str_result_t result = _xsse_pcmp_str_core(a, la, b, lb, imm8 & 0x3F, check_null); + + if (imm8 & _SIDD_UNIT_MASK) { + return vreinterpretq_s8_u8(result.mask); + } + + if (imm8 & 0x01) { + uint32x4_t high_bits = vreinterpretq_u32_u16(vshrq_n_u16(vreinterpretq_u16_u8(result.mask), 15)); + uint64x2_t paired32 = vreinterpretq_u64_u32(vsraq_n_u32(high_bits, high_bits, 15)); + uint8x16_t paired64 = vreinterpretq_u8_u64(vsraq_n_u64(paired32, paired32, 30)); + + return vreinterpretq_s8_u8((uint8x16_t) { + (vgetq_lane_u8(paired64, 0) | (vgetq_lane_u8(paired64, 8) << 4)), 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 + }); + } else { + uint16x8_t high_bits = vreinterpretq_u16_u8(vshrq_n_u8(result.mask, 7)); + uint32x4_t paired16 = vreinterpretq_u32_u16(vsraq_n_u16(high_bits, high_bits, 7)); + uint64x2_t paired32 = vreinterpretq_u64_u32(vsraq_n_u32(paired16, paired16, 14)); + uint8x16_t paired64 = vreinterpretq_u8_u64(vsraq_n_u64(paired32, paired32, 28)); + + return vreinterpretq_s8_u8((uint8x16_t) { + vgetq_lane_u8(paired64, 0), vgetq_lane_u8(paired64, 8), 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 + }); + } +} + +XSSE_ATTR_CONST +static XSSE_FORCE_INLINE int _xsse_pcmp_str_o(const __m128i a, const int la, const __m128i b, const int lb, const int imm8, const int check_null) +{ + _xsse_pcmp_str_result_t result = _xsse_pcmp_str_core(a, la, b, lb, imm8 & 0x3F, check_null); + uint64_t low = vgetq_lane_u64(vreinterpretq_u64_u8(result.mask), 0); + return low & 1; +} + +XSSE_ATTR_CONST +static XSSE_FORCE_INLINE int _xsse_pcmp_str_s(const __m128i a, const int la, const __m128i b, const int lb, const int imm8, const int check_null) +{ + _xsse_pcmp_str_result_t result = _xsse_pcmp_str_core(a, la, b, lb, imm8 & 0x3F, check_null); + return result.sf; +} + +XSSE_ATTR_CONST +static XSSE_FORCE_INLINE int _xsse_pcmp_str_z(const __m128i a, const int la, const __m128i b, const int lb, const int imm8, const int check_null) +{ + _xsse_pcmp_str_result_t result = _xsse_pcmp_str_core(a, la, b, lb, imm8 & 0x3F, check_null); + return result.zf; +} + +#define _mm_cmpestra(a, la, b, lb, imm8) _xsse_pcmp_str_a(a, la, b, lb, imm8, 0) +#define _mm_cmpestrc(a, la, b, lb, imm8) _xsse_pcmp_str_c(a, la, b, lb, imm8, 0) +#define _mm_cmpestri(a, la, b, lb, imm8) _xsse_pcmp_str_i(a, la, b, lb, imm8, 0) +#define _mm_cmpestrm(a, la, b, lb, imm8) _xsse_pcmp_str_m(a, la, b, lb, imm8, 0) +#define _mm_cmpestro(a, la, b, lb, imm8) _xsse_pcmp_str_o(a, la, b, lb, imm8, 0) +#define _mm_cmpestrs(a, la, b, lb, imm8) _xsse_pcmp_str_s(a, la, b, lb, imm8, 0) +#define _mm_cmpestrz(a, la, b, lb, imm8) _xsse_pcmp_str_z(a, la, b, lb, imm8, 0) + +#define _mm_cmpistra(a, b, imm8) _xsse_pcmp_str_a(a, 0, b, 0, imm8, 1) +#define _mm_cmpistrc(a, b, imm8) _xsse_pcmp_str_c(a, 0, b, 0, imm8, 1) +#define _mm_cmpistri(a, b, imm8) _xsse_pcmp_str_i(a, 0, b, 0, imm8, 1) +#define _mm_cmpistrm(a, b, imm8) _xsse_pcmp_str_m(a, 0, b, 0, imm8, 1) +#define _mm_cmpistro(a, b, imm8) _xsse_pcmp_str_o(a, 0, b, 0, imm8, 1) +#define _mm_cmpistrs(a, b, imm8) _xsse_pcmp_str_s(a, 0, b, 0, imm8, 1) +#define _mm_cmpistrz(a, b, imm8) _xsse_pcmp_str_z(a, 0, b, 0, imm8, 1) + + +/***************************************************************************** + * CRC * + *****************************************************************************/ + +#ifdef __ARM_FEATURE_CRC32 +#include +#define _mm_crc32_u8(crc, v) (__crc32cb(crc, v)) +#define _mm_crc32_u16(crc, v) (__crc32ch(crc, v)) +#define _mm_crc32_u32(crc, v) (__crc32cw(crc, v)) +#define _mm_crc32_u64(crc, v) (__crc32cd(crc, v)) +#else +static XSSE_FORCE_INLINE int _mm_crc32_u8(unsigned int crc, unsigned char v) +{ + crc ^= v; + static const uint32_t crc32_nibble_tbl[] = { + 0x00000000, 0x105ec76f, 0x20bd8ede, 0x30e349b1, + 0x417b1dbc, 0x5125dad3, 0x61c69362, 0x7198540d, + 0x82f63b78, 0x92a8fc17, 0xa24bb5a6, 0xb21572c9, + 0xc38d26c4, 0xd3d3e1ab, 0xe330a81a, 0xf36e6f75 + }; + crc = (crc >> 4) ^ crc32_nibble_tbl[crc & 0x0F]; + crc = (crc >> 4) ^ crc32_nibble_tbl[crc & 0x0F]; + return crc; +} +static XSSE_FORCE_INLINE int _mm_crc32_u16(unsigned int crc, unsigned short v) +{ + crc = _mm_crc32_u8(crc, (unsigned char) (v & 0xFF)); + crc = _mm_crc32_u8(crc, (unsigned char) (v >> 8)); + return crc; +} +static XSSE_FORCE_INLINE int _mm_crc32_u32(unsigned int crc, unsigned int v) +{ + crc = _mm_crc32_u16(crc, (unsigned short) (v & 0xFFFF)); + crc = _mm_crc32_u16(crc, (unsigned short) (v >> 16)); + return crc; +} +static XSSE_FORCE_INLINE uint64_t _mm_crc32_u64(uint64_t crc, uint64_t v) +{ + unsigned int crc32 = (unsigned int) crc; + crc32 = _mm_crc32_u32(crc32, (unsigned int) (v & 0xFFFFFFFF)); + crc32 = _mm_crc32_u32(crc32, (unsigned int) (v >> 32)); + return crc32; +} +#endif /* __ARM_FEATURE_CRC32 */ + +#endif /* SSE4_2 */ + +#endif /* XSSE_H */ From e549ccb32eae11c5affd3bb288e57f1cc0295436 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Vr=C3=A1na?= Date: Sat, 19 Apr 2025 21:31:08 +0200 Subject: [PATCH 035/682] ext/pdo_pgsql: Delete unused constants These constants were added by 6ed1819bf4faaee1ef3650d59089c66181b1f8ba but they are not used anymore. They are undocumented which is why I've stumbled upon this. close GH-18358 --- NEWS | 1 + UPGRADING | 3 +++ ext/pdo_pgsql/pdo_pgsql.c | 5 ----- ext/pdo_pgsql/pdo_pgsql.stub.php | 15 --------------- ext/pdo_pgsql/pdo_pgsql_arginfo.h | 32 +------------------------------ ext/pdo_pgsql/php_pdo_pgsql_int.h | 8 -------- 6 files changed, 5 insertions(+), 59 deletions(-) diff --git a/NEWS b/NEWS index 88ba1f1e5bcc2..cf80083f9b97d 100644 --- a/NEWS +++ b/NEWS @@ -137,6 +137,7 @@ PHP NEWS . Implement GH-15387 Pdo\Pgsql::setAttribute(PDO::ATTR_PREFETCH, 0) or Pdo\Pgsql::prepare(…, [ PDO::ATTR_PREFETCH => 0 ]) make fetch() lazy instead of storing the whole result set in memory (Guillaume Outters) + . Removed unused constants Pdo\Pgsql::TRANSACTION_*. - PDO_SQLITE: . throw on null bytes / resolve GH-13952 (divinity76). diff --git a/UPGRADING b/UPGRADING index 0a639508ef696..1f55fa8ce8946 100644 --- a/UPGRADING +++ b/UPGRADING @@ -101,6 +101,9 @@ PHP 8.5 UPGRADE NOTES . A ValueError is now thrown when trying to set a cursor name that is too long on a PDOStatement resulting from the Firebird driver. +- PDO_PGSQL: + . Removed unused constants Pdo\Pgsql::TRANSACTION_*. + - Session: . Attempting to write session data where $_SESSION has a key containing the pipe character will now emit a warning instead of silently failing. diff --git a/ext/pdo_pgsql/pdo_pgsql.c b/ext/pdo_pgsql/pdo_pgsql.c index 49efa0b238484..af9dcd0ec4102 100644 --- a/ext/pdo_pgsql/pdo_pgsql.c +++ b/ext/pdo_pgsql/pdo_pgsql.c @@ -179,11 +179,6 @@ PHP_METHOD(Pdo_Pgsql, setNoticeCallback) PHP_MINIT_FUNCTION(pdo_pgsql) { REGISTER_PDO_CLASS_CONST_LONG("PGSQL_ATTR_DISABLE_PREPARES", PDO_PGSQL_ATTR_DISABLE_PREPARES); - REGISTER_PDO_CLASS_CONST_LONG("PGSQL_TRANSACTION_IDLE", (zend_long)PGSQL_TRANSACTION_IDLE); - REGISTER_PDO_CLASS_CONST_LONG("PGSQL_TRANSACTION_ACTIVE", (zend_long)PGSQL_TRANSACTION_ACTIVE); - REGISTER_PDO_CLASS_CONST_LONG("PGSQL_TRANSACTION_INTRANS", (zend_long)PGSQL_TRANSACTION_INTRANS); - REGISTER_PDO_CLASS_CONST_LONG("PGSQL_TRANSACTION_INERROR", (zend_long)PGSQL_TRANSACTION_INERROR); - REGISTER_PDO_CLASS_CONST_LONG("PGSQL_TRANSACTION_UNKNOWN", (zend_long)PGSQL_TRANSACTION_UNKNOWN); PdoPgsql_ce = register_class_Pdo_Pgsql(pdo_dbh_ce); PdoPgsql_ce->create_object = pdo_dbh_new; diff --git a/ext/pdo_pgsql/pdo_pgsql.stub.php b/ext/pdo_pgsql/pdo_pgsql.stub.php index 8e96a55266824..93415369c625d 100644 --- a/ext/pdo_pgsql/pdo_pgsql.stub.php +++ b/ext/pdo_pgsql/pdo_pgsql.stub.php @@ -18,21 +18,6 @@ class Pgsql extends \PDO public const int ATTR_RESULT_MEMORY_SIZE = UNKNOWN; #endif - /** @cvalue PGSQL_TRANSACTION_IDLE */ - public const int TRANSACTION_IDLE = UNKNOWN; - - /** @cvalue PGSQL_TRANSACTION_ACTIVE */ - public const int TRANSACTION_ACTIVE = UNKNOWN; - - /** @cvalue PGSQL_TRANSACTION_INTRANS */ - public const int TRANSACTION_INTRANS = UNKNOWN; - - /** @cvalue PGSQL_TRANSACTION_INERROR */ - public const int TRANSACTION_INERROR = UNKNOWN; - - /** @cvalue PGSQL_TRANSACTION_UNKNOWN */ - public const int TRANSACTION_UNKNOWN = UNKNOWN; - public function escapeIdentifier(string $input): string {} public function copyFromArray(string $tableName, array $rows, string $separator = "\t", string $nullAs = "\\\\N", ?string $fields = null): bool {} diff --git a/ext/pdo_pgsql/pdo_pgsql_arginfo.h b/ext/pdo_pgsql/pdo_pgsql_arginfo.h index f7f54cb600c72..8efdfe53a0d16 100644 --- a/ext/pdo_pgsql/pdo_pgsql_arginfo.h +++ b/ext/pdo_pgsql/pdo_pgsql_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 225cbb077d441f93b7c6bdb9826ab3e8f634b79d */ + * Stub hash: 81399a3d342a9327733f86f6ab733bb317a4599e */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Pdo_Pgsql_escapeIdentifier, 0, 1, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, input, IS_STRING, 0) @@ -102,35 +102,5 @@ static zend_class_entry *register_class_Pdo_Pgsql(zend_class_entry *class_entry_ zend_string_release(const_ATTR_RESULT_MEMORY_SIZE_name); #endif - zval const_TRANSACTION_IDLE_value; - ZVAL_LONG(&const_TRANSACTION_IDLE_value, PGSQL_TRANSACTION_IDLE); - zend_string *const_TRANSACTION_IDLE_name = zend_string_init_interned("TRANSACTION_IDLE", sizeof("TRANSACTION_IDLE") - 1, 1); - zend_declare_typed_class_constant(class_entry, const_TRANSACTION_IDLE_name, &const_TRANSACTION_IDLE_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); - zend_string_release(const_TRANSACTION_IDLE_name); - - zval const_TRANSACTION_ACTIVE_value; - ZVAL_LONG(&const_TRANSACTION_ACTIVE_value, PGSQL_TRANSACTION_ACTIVE); - zend_string *const_TRANSACTION_ACTIVE_name = zend_string_init_interned("TRANSACTION_ACTIVE", sizeof("TRANSACTION_ACTIVE") - 1, 1); - zend_declare_typed_class_constant(class_entry, const_TRANSACTION_ACTIVE_name, &const_TRANSACTION_ACTIVE_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); - zend_string_release(const_TRANSACTION_ACTIVE_name); - - zval const_TRANSACTION_INTRANS_value; - ZVAL_LONG(&const_TRANSACTION_INTRANS_value, PGSQL_TRANSACTION_INTRANS); - zend_string *const_TRANSACTION_INTRANS_name = zend_string_init_interned("TRANSACTION_INTRANS", sizeof("TRANSACTION_INTRANS") - 1, 1); - zend_declare_typed_class_constant(class_entry, const_TRANSACTION_INTRANS_name, &const_TRANSACTION_INTRANS_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); - zend_string_release(const_TRANSACTION_INTRANS_name); - - zval const_TRANSACTION_INERROR_value; - ZVAL_LONG(&const_TRANSACTION_INERROR_value, PGSQL_TRANSACTION_INERROR); - zend_string *const_TRANSACTION_INERROR_name = zend_string_init_interned("TRANSACTION_INERROR", sizeof("TRANSACTION_INERROR") - 1, 1); - zend_declare_typed_class_constant(class_entry, const_TRANSACTION_INERROR_name, &const_TRANSACTION_INERROR_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); - zend_string_release(const_TRANSACTION_INERROR_name); - - zval const_TRANSACTION_UNKNOWN_value; - ZVAL_LONG(&const_TRANSACTION_UNKNOWN_value, PGSQL_TRANSACTION_UNKNOWN); - zend_string *const_TRANSACTION_UNKNOWN_name = zend_string_init_interned("TRANSACTION_UNKNOWN", sizeof("TRANSACTION_UNKNOWN") - 1, 1); - zend_declare_typed_class_constant(class_entry, const_TRANSACTION_UNKNOWN_name, &const_TRANSACTION_UNKNOWN_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); - zend_string_release(const_TRANSACTION_UNKNOWN_name); - return class_entry; } diff --git a/ext/pdo_pgsql/php_pdo_pgsql_int.h b/ext/pdo_pgsql/php_pdo_pgsql_int.h index 881b4e7046504..cb07a10de16f0 100644 --- a/ext/pdo_pgsql/php_pdo_pgsql_int.h +++ b/ext/pdo_pgsql/php_pdo_pgsql_int.h @@ -104,14 +104,6 @@ struct pdo_pgsql_lob_self { Oid oid; }; -enum pdo_pgsql_specific_constants { - PGSQL_TRANSACTION_IDLE = PQTRANS_IDLE, - PGSQL_TRANSACTION_ACTIVE = PQTRANS_ACTIVE, - PGSQL_TRANSACTION_INTRANS = PQTRANS_INTRANS, - PGSQL_TRANSACTION_INERROR = PQTRANS_INERROR, - PGSQL_TRANSACTION_UNKNOWN = PQTRANS_UNKNOWN -}; - php_stream *pdo_pgsql_create_lob_stream(zend_object *pdh, int lfd, Oid oid); extern const php_stream_ops pdo_pgsql_lob_stream_ops; From cc1f1c60eee9e473626a3f643f98bdf64cd35612 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Fri, 6 Jun 2025 08:17:30 +0100 Subject: [PATCH 036/682] [skip ci] giving proper credits to last changes in ext/pdo_pgsql --- NEWS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS b/NEWS index cf80083f9b97d..9e1944e1fba38 100644 --- a/NEWS +++ b/NEWS @@ -137,7 +137,7 @@ PHP NEWS . Implement GH-15387 Pdo\Pgsql::setAttribute(PDO::ATTR_PREFETCH, 0) or Pdo\Pgsql::prepare(…, [ PDO::ATTR_PREFETCH => 0 ]) make fetch() lazy instead of storing the whole result set in memory (Guillaume Outters) - . Removed unused constants Pdo\Pgsql::TRANSACTION_*. + . Removed unused constants Pdo\Pgsql::TRANSACTION_* (vrana). - PDO_SQLITE: . throw on null bytes / resolve GH-13952 (divinity76). From ae92b85572ac9a5f14103cdedbf6448f9e8db09d Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Wed, 14 Feb 2024 13:52:01 +0100 Subject: [PATCH 037/682] Fix linking ext/curl against OpenSSL (#13262) This is backport for 8.3 of b222c020bfa876ae1cea87406beb8af0b53f0fab that originally targeted only 8.4+. This is however a bug fix. Following 68f6ab711323678382d2746e57358d3f57a3446b, the ext/curl doesn't need to be linked against OpenSSL anymore, if curl_version_info_data ssl_version is OpenSSL/1.1 or later. With OpenSSL 3 and later the check for old SSL crypto locking callbacks was detected here. This also uses a common PHP_SETUP_OPENSSL macro for checking OpenSSL and syncs the minimum OpenSSL version (currently 1.0.2 or later) across the PHP build system. --- NEWS | 1 + ext/curl/config.m4 | 22 ++++++++++++++-------- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/NEWS b/NEWS index 6867a3dbcaeb8..5eb1694b5e816 100644 --- a/NEWS +++ b/NEWS @@ -8,6 +8,7 @@ PHP NEWS - Curl: . Fix memory leak when setting a list via curl_setopt fails. (nielsdos) + . Fix incorrect OpenSSL version detection. (Peter Kokot) - Date: . Fix leaks with multiple calls to DatePeriod iterator current(). (nielsdos) diff --git a/ext/curl/config.m4 b/ext/curl/config.m4 index 3b11739654bd6..c0325f990ad11 100644 --- a/ext/curl/config.m4 +++ b/ext/curl/config.m4 @@ -28,6 +28,7 @@ if test "$PHP_CURL" != "no"; then AC_MSG_CHECKING([for libcurl linked against old openssl]) AC_RUN_IFELSE([AC_LANG_SOURCE([[ +#include #include #include @@ -39,9 +40,18 @@ int main(int argc, char *argv[]) const char *ptr = data->ssl_version; while(*ptr == ' ') ++ptr; - if (strncasecmp(ptr, "OpenSSL/1.1", sizeof("OpenSSL/1.1")-1) == 0) { - /* New OpenSSL version */ - return 3; + int major, minor; + if (sscanf(ptr, "OpenSSL/%d", &major) == 1) { + if (major >= 3) { + /* OpenSSL version 3 or later */ + return 4; + } + } + if (sscanf(ptr, "OpenSSL/%d.%d", &major, &minor) == 2) { + if (major > 1 || (major == 1 && minor >= 1)) { + /* OpenSSL version 1.1 or later */ + return 3; + } } if (strncasecmp(ptr, "OpenSSL", sizeof("OpenSSL")-1) == 0) { /* Old OpenSSL version */ @@ -56,11 +66,7 @@ int main(int argc, char *argv[]) ]])],[ AC_MSG_RESULT([yes]) AC_DEFINE([HAVE_CURL_OLD_OPENSSL], [1], [Have cURL with old OpenSSL]) - PKG_CHECK_MODULES([OPENSSL], [openssl], [ - PHP_EVAL_LIBLINE($OPENSSL_LIBS, CURL_SHARED_LIBADD) - PHP_EVAL_INCLINE($OPENSSL_CFLAGS) - AC_CHECK_HEADERS([openssl/crypto.h]) - ], []) + PHP_SETUP_OPENSSL(CURL_SHARED_LIBADD,[AC_CHECK_HEADERS([openssl/crypto.h])],[]) ], [ AC_MSG_RESULT([no]) ], [ From 01abca98525c3ac4196ea8dd22d8dd89b325e5eb Mon Sep 17 00:00:00 2001 From: NickSdot <32384907+NickSdot@users.noreply.github.com> Date: Fri, 6 Jun 2025 20:25:19 +0800 Subject: [PATCH 038/682] [skip ci ] fix: deleted stray semicolon (GH-18782) --- scripts/dev/search_underscores.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/dev/search_underscores.php b/scripts/dev/search_underscores.php index 4f2f8e8eebc9a..42e4c876e0541 100755 --- a/scripts/dev/search_underscores.php +++ b/scripts/dev/search_underscores.php @@ -45,7 +45,7 @@ if (strpos($c, "_") !== false) { $err++; $ref = new ReflectionClass($c); - if (!($ext = $ref->getExtensionName())) {; + if (!($ext = $ref->getExtensionName())) { $ext = $ref->isInternal() ? "" : ""; } if (!array_key_exists($ext, $extensions)) { From cce0efdff87de3cea3e2326cdb36c8adc58a4bdb Mon Sep 17 00:00:00 2001 From: David Carlier Date: Fri, 6 Jun 2025 14:45:59 +0100 Subject: [PATCH 039/682] Revert "ext/pdo_pgsql: Delete unused constants" This reverts commit e549ccb32eae11c5affd3bb288e57f1cc0295436. --- NEWS | 1 - UPGRADING | 3 --- ext/pdo_pgsql/pdo_pgsql.c | 5 +++++ ext/pdo_pgsql/pdo_pgsql.stub.php | 15 +++++++++++++++ ext/pdo_pgsql/pdo_pgsql_arginfo.h | 32 ++++++++++++++++++++++++++++++- ext/pdo_pgsql/php_pdo_pgsql_int.h | 8 ++++++++ 6 files changed, 59 insertions(+), 5 deletions(-) diff --git a/NEWS b/NEWS index 9e1944e1fba38..88ba1f1e5bcc2 100644 --- a/NEWS +++ b/NEWS @@ -137,7 +137,6 @@ PHP NEWS . Implement GH-15387 Pdo\Pgsql::setAttribute(PDO::ATTR_PREFETCH, 0) or Pdo\Pgsql::prepare(…, [ PDO::ATTR_PREFETCH => 0 ]) make fetch() lazy instead of storing the whole result set in memory (Guillaume Outters) - . Removed unused constants Pdo\Pgsql::TRANSACTION_* (vrana). - PDO_SQLITE: . throw on null bytes / resolve GH-13952 (divinity76). diff --git a/UPGRADING b/UPGRADING index 1f55fa8ce8946..0a639508ef696 100644 --- a/UPGRADING +++ b/UPGRADING @@ -101,9 +101,6 @@ PHP 8.5 UPGRADE NOTES . A ValueError is now thrown when trying to set a cursor name that is too long on a PDOStatement resulting from the Firebird driver. -- PDO_PGSQL: - . Removed unused constants Pdo\Pgsql::TRANSACTION_*. - - Session: . Attempting to write session data where $_SESSION has a key containing the pipe character will now emit a warning instead of silently failing. diff --git a/ext/pdo_pgsql/pdo_pgsql.c b/ext/pdo_pgsql/pdo_pgsql.c index af9dcd0ec4102..49efa0b238484 100644 --- a/ext/pdo_pgsql/pdo_pgsql.c +++ b/ext/pdo_pgsql/pdo_pgsql.c @@ -179,6 +179,11 @@ PHP_METHOD(Pdo_Pgsql, setNoticeCallback) PHP_MINIT_FUNCTION(pdo_pgsql) { REGISTER_PDO_CLASS_CONST_LONG("PGSQL_ATTR_DISABLE_PREPARES", PDO_PGSQL_ATTR_DISABLE_PREPARES); + REGISTER_PDO_CLASS_CONST_LONG("PGSQL_TRANSACTION_IDLE", (zend_long)PGSQL_TRANSACTION_IDLE); + REGISTER_PDO_CLASS_CONST_LONG("PGSQL_TRANSACTION_ACTIVE", (zend_long)PGSQL_TRANSACTION_ACTIVE); + REGISTER_PDO_CLASS_CONST_LONG("PGSQL_TRANSACTION_INTRANS", (zend_long)PGSQL_TRANSACTION_INTRANS); + REGISTER_PDO_CLASS_CONST_LONG("PGSQL_TRANSACTION_INERROR", (zend_long)PGSQL_TRANSACTION_INERROR); + REGISTER_PDO_CLASS_CONST_LONG("PGSQL_TRANSACTION_UNKNOWN", (zend_long)PGSQL_TRANSACTION_UNKNOWN); PdoPgsql_ce = register_class_Pdo_Pgsql(pdo_dbh_ce); PdoPgsql_ce->create_object = pdo_dbh_new; diff --git a/ext/pdo_pgsql/pdo_pgsql.stub.php b/ext/pdo_pgsql/pdo_pgsql.stub.php index 93415369c625d..8e96a55266824 100644 --- a/ext/pdo_pgsql/pdo_pgsql.stub.php +++ b/ext/pdo_pgsql/pdo_pgsql.stub.php @@ -18,6 +18,21 @@ class Pgsql extends \PDO public const int ATTR_RESULT_MEMORY_SIZE = UNKNOWN; #endif + /** @cvalue PGSQL_TRANSACTION_IDLE */ + public const int TRANSACTION_IDLE = UNKNOWN; + + /** @cvalue PGSQL_TRANSACTION_ACTIVE */ + public const int TRANSACTION_ACTIVE = UNKNOWN; + + /** @cvalue PGSQL_TRANSACTION_INTRANS */ + public const int TRANSACTION_INTRANS = UNKNOWN; + + /** @cvalue PGSQL_TRANSACTION_INERROR */ + public const int TRANSACTION_INERROR = UNKNOWN; + + /** @cvalue PGSQL_TRANSACTION_UNKNOWN */ + public const int TRANSACTION_UNKNOWN = UNKNOWN; + public function escapeIdentifier(string $input): string {} public function copyFromArray(string $tableName, array $rows, string $separator = "\t", string $nullAs = "\\\\N", ?string $fields = null): bool {} diff --git a/ext/pdo_pgsql/pdo_pgsql_arginfo.h b/ext/pdo_pgsql/pdo_pgsql_arginfo.h index 8efdfe53a0d16..f7f54cb600c72 100644 --- a/ext/pdo_pgsql/pdo_pgsql_arginfo.h +++ b/ext/pdo_pgsql/pdo_pgsql_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 81399a3d342a9327733f86f6ab733bb317a4599e */ + * Stub hash: 225cbb077d441f93b7c6bdb9826ab3e8f634b79d */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Pdo_Pgsql_escapeIdentifier, 0, 1, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, input, IS_STRING, 0) @@ -102,5 +102,35 @@ static zend_class_entry *register_class_Pdo_Pgsql(zend_class_entry *class_entry_ zend_string_release(const_ATTR_RESULT_MEMORY_SIZE_name); #endif + zval const_TRANSACTION_IDLE_value; + ZVAL_LONG(&const_TRANSACTION_IDLE_value, PGSQL_TRANSACTION_IDLE); + zend_string *const_TRANSACTION_IDLE_name = zend_string_init_interned("TRANSACTION_IDLE", sizeof("TRANSACTION_IDLE") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_TRANSACTION_IDLE_name, &const_TRANSACTION_IDLE_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_TRANSACTION_IDLE_name); + + zval const_TRANSACTION_ACTIVE_value; + ZVAL_LONG(&const_TRANSACTION_ACTIVE_value, PGSQL_TRANSACTION_ACTIVE); + zend_string *const_TRANSACTION_ACTIVE_name = zend_string_init_interned("TRANSACTION_ACTIVE", sizeof("TRANSACTION_ACTIVE") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_TRANSACTION_ACTIVE_name, &const_TRANSACTION_ACTIVE_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_TRANSACTION_ACTIVE_name); + + zval const_TRANSACTION_INTRANS_value; + ZVAL_LONG(&const_TRANSACTION_INTRANS_value, PGSQL_TRANSACTION_INTRANS); + zend_string *const_TRANSACTION_INTRANS_name = zend_string_init_interned("TRANSACTION_INTRANS", sizeof("TRANSACTION_INTRANS") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_TRANSACTION_INTRANS_name, &const_TRANSACTION_INTRANS_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_TRANSACTION_INTRANS_name); + + zval const_TRANSACTION_INERROR_value; + ZVAL_LONG(&const_TRANSACTION_INERROR_value, PGSQL_TRANSACTION_INERROR); + zend_string *const_TRANSACTION_INERROR_name = zend_string_init_interned("TRANSACTION_INERROR", sizeof("TRANSACTION_INERROR") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_TRANSACTION_INERROR_name, &const_TRANSACTION_INERROR_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_TRANSACTION_INERROR_name); + + zval const_TRANSACTION_UNKNOWN_value; + ZVAL_LONG(&const_TRANSACTION_UNKNOWN_value, PGSQL_TRANSACTION_UNKNOWN); + zend_string *const_TRANSACTION_UNKNOWN_name = zend_string_init_interned("TRANSACTION_UNKNOWN", sizeof("TRANSACTION_UNKNOWN") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_TRANSACTION_UNKNOWN_name, &const_TRANSACTION_UNKNOWN_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_TRANSACTION_UNKNOWN_name); + return class_entry; } diff --git a/ext/pdo_pgsql/php_pdo_pgsql_int.h b/ext/pdo_pgsql/php_pdo_pgsql_int.h index cb07a10de16f0..881b4e7046504 100644 --- a/ext/pdo_pgsql/php_pdo_pgsql_int.h +++ b/ext/pdo_pgsql/php_pdo_pgsql_int.h @@ -104,6 +104,14 @@ struct pdo_pgsql_lob_self { Oid oid; }; +enum pdo_pgsql_specific_constants { + PGSQL_TRANSACTION_IDLE = PQTRANS_IDLE, + PGSQL_TRANSACTION_ACTIVE = PQTRANS_ACTIVE, + PGSQL_TRANSACTION_INTRANS = PQTRANS_INTRANS, + PGSQL_TRANSACTION_INERROR = PQTRANS_INERROR, + PGSQL_TRANSACTION_UNKNOWN = PQTRANS_UNKNOWN +}; + php_stream *pdo_pgsql_create_lob_stream(zend_object *pdh, int lfd, Oid oid); extern const php_stream_ops pdo_pgsql_lob_stream_ops; From d15c61e84cad4a274cf748d01d0599730b951663 Mon Sep 17 00:00:00 2001 From: NickSdot <32384907+NickSdot@users.noreply.github.com> Date: Sat, 7 Jun 2025 14:31:51 +0800 Subject: [PATCH 040/682] ext/gettext: fixed typo in config.m4 (#18790) --- ext/gettext/config.m4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/gettext/config.m4 b/ext/gettext/config.m4 index ae3eaf38761a1..6235658987e5a 100644 --- a/ext/gettext/config.m4 +++ b/ext/gettext/config.m4 @@ -27,7 +27,7 @@ if test "$PHP_GETTEXT" != "no"; then dnl If libintl.h is provided by libc, it's possible that libc is musl. dnl The gettext family of functions under musl ignores the codeset dnl suffix on directories like "en_US.UTF-8"; instead they look only - dnl in "en_US". To accomodate that, we symlink some test data from one + dnl in "en_US". To accommodate that, we symlink some test data from one dnl to the other. AC_MSG_NOTICE([symlinking en_US.UTF-8 messages to en_US in case you are on musl]) _linkdest="${srcdir%/}"/ext/gettext/tests/locale/en_US From 1044558b6484d21873039cad04961013f49d08b7 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sat, 7 Jun 2025 13:31:55 +0100 Subject: [PATCH 041/682] ext/pdo_sqlite: createCollation memory leaks fix. coming from callback arguments when its return type is incorrect. close GH-18796 --- NEWS | 4 ++++ ext/pdo_sqlite/pdo_sqlite.c | 6 ++--- ...sqlite_createcollation_wrong_callback.phpt | 24 +++++++++++++++++++ 3 files changed, 31 insertions(+), 3 deletions(-) create mode 100644 ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createcollation_wrong_callback.phpt diff --git a/NEWS b/NEWS index 4c70733379d5c..8f4ae644a9299 100644 --- a/NEWS +++ b/NEWS @@ -37,6 +37,10 @@ PHP NEWS . Fixed bug #74796 (Requests through http proxy set peer name). (Jakub Zelenka) +- PDO Sqlite: + . Fixed memory leak with Pdo_Sqlite::createCollation when the callback + has an incorrect return type. (David Carlier) + - Phar: . Add missing filter cleanups on phar failure. (nielsdos) . Fixed bug GH-18642 (Signed integer overflow in ext/phar fseek). (nielsdos) diff --git a/ext/pdo_sqlite/pdo_sqlite.c b/ext/pdo_sqlite/pdo_sqlite.c index ff56d04049424..493ba3f36009d 100644 --- a/ext/pdo_sqlite/pdo_sqlite.c +++ b/ext/pdo_sqlite/pdo_sqlite.c @@ -346,6 +346,9 @@ static int php_sqlite_collation_callback(void *context, int string1_len, const v zend_call_known_fcc(&collation->callback, &retval, /* argc */ 2, zargs, /* named_params */ NULL); + zval_ptr_dtor(&zargs[0]); + zval_ptr_dtor(&zargs[1]); + if (!Z_ISUNDEF(retval)) { if (Z_TYPE(retval) != IS_LONG) { zend_string *func_name = get_active_function_or_method_name(); @@ -362,9 +365,6 @@ static int php_sqlite_collation_callback(void *context, int string1_len, const v } } - zval_ptr_dtor(&zargs[0]); - zval_ptr_dtor(&zargs[1]); - return ret; } diff --git a/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createcollation_wrong_callback.phpt b/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createcollation_wrong_callback.phpt new file mode 100644 index 0000000000000..a9d17bb230d56 --- /dev/null +++ b/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createcollation_wrong_callback.phpt @@ -0,0 +1,24 @@ +--TEST-- +Pdo\Sqlite::createCollation() memory leaks on wrong callback return type +--EXTENSIONS-- +pdo_sqlite +--FILE-- +exec("CREATE TABLE test (c string)"); +$db->exec("INSERT INTO test VALUES('youwontseeme')"); +$db->exec("INSERT INTO test VALUES('neither')"); +$db->createCollation('NAT', function($a, $b): string { return $a . $b; }); + +try { + $db->query("SELECT c FROM test ORDER BY c COLLATE NAT"); +} catch (\TypeError $e) { + echo $e->getMessage(), PHP_EOL; +} +?> +--EXPECT-- +PDO::query(): Return value of the callback must be of type int, string returned From ceffa70b9713dec0dad3d07faeceb7b7e508b006 Mon Sep 17 00:00:00 2001 From: David CARLIER Date: Sat, 7 Jun 2025 15:53:46 +0100 Subject: [PATCH 042/682] ext/pdo_sqlite: Fix GH-18796 test exception message. (#18798) --- .../subclasses/pdo_sqlite_createcollation_wrong_callback.phpt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createcollation_wrong_callback.phpt b/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createcollation_wrong_callback.phpt index a9d17bb230d56..2a493c2117972 100644 --- a/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createcollation_wrong_callback.phpt +++ b/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createcollation_wrong_callback.phpt @@ -21,4 +21,4 @@ try { } ?> --EXPECT-- -PDO::query(): Return value of the callback must be of type int, string returned +PDO::query(): Return value of the collation callback must be of type int, string returned From cb04226b4aa2960ed22cbd09e5fe826f69aca7b0 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sun, 8 Jun 2025 11:23:31 +0200 Subject: [PATCH 043/682] Avoid making a redundant copy in php_filter_callback() (#18794) `call_user_function` already makes a copy to the call frame for its arguments, there's no need to do this ourselves. --- ext/filter/callback_filter.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/ext/filter/callback_filter.c b/ext/filter/callback_filter.c index 067f16a03ea24..b6d57739b2b9b 100644 --- a/ext/filter/callback_filter.c +++ b/ext/filter/callback_filter.c @@ -19,7 +19,6 @@ void php_filter_callback(PHP_INPUT_FILTER_PARAM_DECL) { zval retval; - zval args[1]; int status; if (!option_array || !zend_is_callable(option_array, IS_CALLABLE_SUPPRESS_DEPRECATIONS, NULL)) { @@ -29,8 +28,7 @@ void php_filter_callback(PHP_INPUT_FILTER_PARAM_DECL) return; } - ZVAL_COPY(&args[0], value); - status = call_user_function(NULL, NULL, option_array, &retval, 1, args); + status = call_user_function(NULL, NULL, option_array, &retval, 1, value); if (status == SUCCESS && !Z_ISUNDEF(retval)) { zval_ptr_dtor(value); @@ -39,6 +37,4 @@ void php_filter_callback(PHP_INPUT_FILTER_PARAM_DECL) zval_ptr_dtor(value); ZVAL_NULL(value); } - - zval_ptr_dtor(&args[0]); } From eac91d0453a913e2862ce948b458eb300185370f Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sun, 8 Jun 2025 13:25:56 +0200 Subject: [PATCH 044/682] [ci skip] Fix UPGRADING formatting --- UPGRADING | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UPGRADING b/UPGRADING index 0a639508ef696..7cb73a1271d2f 100644 --- a/UPGRADING +++ b/UPGRADING @@ -552,7 +552,7 @@ PHP 8.5 UPGRADE NOTES . Now avoids creating extra string copies when converting strings for use in the collator. -MBString: +- MBString: . The parts of the code that used SSE2 have been adapted to use SIMD with ARM NEON as well. From e3cfa4bcae8b476e9d0192f938de579caed9f6a0 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sat, 7 Jun 2025 16:35:30 +0100 Subject: [PATCH 045/682] ext/pdo_sqlite: PDO::sqliteCreateCollection return type strenghtening. Is supposed to be Pdo_Sqlite::createCollation but behavior differs in regard of return type checks. close GH-18799 --- NEWS | 2 ++ UPGRADING | 3 +++ ext/pdo_sqlite/pdo_sqlite.c | 4 ++-- ext/pdo_sqlite/sqlite_driver.c | 12 ++++++++---- ext/pdo_sqlite/tests/pdo_sqlite_createcollation.phpt | 8 ++++++++ 5 files changed, 23 insertions(+), 6 deletions(-) diff --git a/NEWS b/NEWS index 88ba1f1e5bcc2..9e6d47ed2a003 100644 --- a/NEWS +++ b/NEWS @@ -141,6 +141,8 @@ PHP NEWS - PDO_SQLITE: . throw on null bytes / resolve GH-13952 (divinity76). . Implement GH-17321: Add setAuthorizer to Pdo\Sqlite. (nielsdos) + . PDO::sqliteCreateCollation now throws a TypeError if the callback + has a wrong return type. (David Carlier) - PGSQL: . Added pg_close_stmt to close a prepared statement while allowing diff --git a/UPGRADING b/UPGRADING index 7cb73a1271d2f..aebda12cf500e 100644 --- a/UPGRADING +++ b/UPGRADING @@ -268,6 +268,9 @@ PHP 8.5 UPGRADE NOTES - PDO_SQLITE: . SQLite PDO::quote() will now throw an exception or emit a warning, depending on the error mode, if the string contains a null byte. + . PDO::sqliteCreateCollation will now throw an exception + if the callback has the wrong return type, making it more + in line with Pdo_Sqlite::createCollation behavior. - PGSQL: . pg_copy_from also supports inputs as Iterable. diff --git a/ext/pdo_sqlite/pdo_sqlite.c b/ext/pdo_sqlite/pdo_sqlite.c index 02fbf0d3e9ecc..fbbb336c1af1e 100644 --- a/ext/pdo_sqlite/pdo_sqlite.c +++ b/ext/pdo_sqlite/pdo_sqlite.c @@ -385,14 +385,14 @@ static int php_sqlite_collation_callback(void *context, int string1_len, const v zend_type_error("%s(): Return value of the collation callback must be of type int, %s returned", ZSTR_VAL(func_name), zend_zval_value_name(&retval)); zend_string_release(func_name); - zval_ptr_dtor(&retval); - return FAILURE; + ret = FAILURE; } if (Z_LVAL(retval) > 0) { ret = 1; } else if (Z_LVAL(retval) < 0) { ret = -1; } + zval_ptr_dtor(&retval); } return ret; diff --git a/ext/pdo_sqlite/sqlite_driver.c b/ext/pdo_sqlite/sqlite_driver.c index ddf25d4965f06..d06d255c14cd8 100644 --- a/ext/pdo_sqlite/sqlite_driver.c +++ b/ext/pdo_sqlite/sqlite_driver.c @@ -483,9 +483,16 @@ static int php_sqlite3_collation_callback(void *context, int string1_len, const zend_call_known_fcc(&collation->callback, &retval, /* argc */ 2, zargs, /* named_params */ NULL); + zval_ptr_dtor(&zargs[0]); + zval_ptr_dtor(&zargs[1]); + if (!Z_ISUNDEF(retval)) { if (Z_TYPE(retval) != IS_LONG) { - convert_to_long(&retval); + zend_string *func_name = get_active_function_or_method_name(); + zend_type_error("%s(): Return value of the collation callback must be of type int, %s returned", + ZSTR_VAL(func_name), zend_zval_value_name(&retval)); + zend_string_release(func_name); + ret = FAILURE; } if (Z_LVAL(retval) > 0) { ret = 1; @@ -495,9 +502,6 @@ static int php_sqlite3_collation_callback(void *context, int string1_len, const zval_ptr_dtor(&retval); } - zval_ptr_dtor(&zargs[0]); - zval_ptr_dtor(&zargs[1]); - return ret; } diff --git a/ext/pdo_sqlite/tests/pdo_sqlite_createcollation.phpt b/ext/pdo_sqlite/tests/pdo_sqlite_createcollation.phpt index 9e4751e33aa01..fdfb8dda448fd 100644 --- a/ext/pdo_sqlite/tests/pdo_sqlite_createcollation.phpt +++ b/ext/pdo_sqlite/tests/pdo_sqlite_createcollation.phpt @@ -24,6 +24,13 @@ foreach ($result as $row) { echo $row['name'] . "\n"; } +$db->sqliteCreateCollation('MYCOLLATEBAD', function($a, $b) { return $a; }); + +try { + $db->query('SELECT name FROM test_pdo_sqlite_createcollation ORDER BY name COLLATE MYCOLLATEBAD'); +} catch (\TypeError $e) { + echo $e->getMessage(), PHP_EOL; +} ?> --EXPECT-- 1 @@ -32,3 +39,4 @@ foreach ($result as $row) { 1 10 2 +PDO::query(): Return value of the collation callback must be of type int, string returned From 1a18012be3c109457945980a5341546a16a6ac8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Mon, 9 Jun 2025 09:34:49 +0200 Subject: [PATCH 046/682] zend_vm_gen: Fix GET_OP*_OBJ_ZVAL_PTR_DEREF for ANY (#18746) Fixes php/php-src#18745 --- Zend/zend_execute.c | 9 +++++++++ Zend/zend_vm_gen.php | 4 ++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 0fbfdfa07ef04..5d8d9f4caeb86 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -126,6 +126,7 @@ typedef int (ZEND_FASTCALL *incdec_t)(zval *); #define get_zval_ptr_ptr(op_type, node, type) _get_zval_ptr_ptr(op_type, node, type EXECUTE_DATA_CC) #define get_zval_ptr_ptr_undef(op_type, node, type) _get_zval_ptr_ptr(op_type, node, type EXECUTE_DATA_CC) #define get_obj_zval_ptr(op_type, node, type) _get_obj_zval_ptr(op_type, node, type EXECUTE_DATA_CC OPLINE_CC) +#define get_obj_zval_ptr_deref(op_type, node, type) _get_obj_zval_ptr_deref(op_type, node, type EXECUTE_DATA_CC OPLINE_CC) #define get_obj_zval_ptr_undef(op_type, node, type) _get_obj_zval_ptr_undef(op_type, node, type EXECUTE_DATA_CC OPLINE_CC) #define get_obj_zval_ptr_ptr(op_type, node, type) _get_obj_zval_ptr_ptr(op_type, node, type EXECUTE_DATA_CC) @@ -537,6 +538,14 @@ static inline ZEND_ATTRIBUTE_UNUSED zval *_get_obj_zval_ptr(int op_type, znode_o return get_zval_ptr(op_type, op, type); } +static inline ZEND_ATTRIBUTE_UNUSED zval *_get_obj_zval_ptr_deref(int op_type, znode_op op, int type EXECUTE_DATA_DC OPLINE_DC) +{ + if (op_type == IS_UNUSED) { + return &EX(This); + } + return get_zval_ptr_deref(op_type, op, type); +} + static inline ZEND_ATTRIBUTE_UNUSED zval *_get_obj_zval_ptr_undef(int op_type, znode_op op, int type EXECUTE_DATA_DC OPLINE_DC) { if (op_type == IS_UNUSED) { diff --git a/Zend/zend_vm_gen.php b/Zend/zend_vm_gen.php index 5a4a31b60b8d3..dbd7da0430f1c 100755 --- a/Zend/zend_vm_gen.php +++ b/Zend/zend_vm_gen.php @@ -363,7 +363,7 @@ ); $op1_get_obj_zval_ptr_deref = array( - "ANY" => "get_obj_zval_ptr(opline->op1_type, opline->op1, \\1)", + "ANY" => "get_obj_zval_ptr_deref(opline->op1_type, opline->op1, \\1)", "TMP" => "_get_zval_ptr_tmp(opline->op1.var EXECUTE_DATA_CC)", "VAR" => "_get_zval_ptr_var_deref(opline->op1.var EXECUTE_DATA_CC)", "CONST" => "RT_CONSTANT(opline, opline->op1)", @@ -374,7 +374,7 @@ ); $op2_get_obj_zval_ptr_deref = array( - "ANY" => "get_obj_zval_ptr(opline->op2_type, opline->op2, \\1)", + "ANY" => "get_obj_zval_ptr_deref(opline->op2_type, opline->op2, \\1)", "TMP" => "_get_zval_ptr_tmp(opline->op2.var EXECUTE_DATA_CC)", "VAR" => "_get_zval_ptr_var_deref(opline->op2.var EXECUTE_DATA_CC)", "CONST" => "RT_CONSTANT(opline, opline->op2)", From 31b4f39d3edbfe1862e1559ae1f217c06a6e5ed4 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sat, 7 Jun 2025 14:23:35 +0200 Subject: [PATCH 047/682] Use ZVAL_NEW_STR() for new string in php_filter_encode_html() --- ext/filter/sanitizing_filters.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/filter/sanitizing_filters.c b/ext/filter/sanitizing_filters.c index 7f8b4948d5818..d619bb0fc13a7 100644 --- a/ext/filter/sanitizing_filters.c +++ b/ext/filter/sanitizing_filters.c @@ -49,7 +49,7 @@ static void php_filter_encode_html(zval *value, const unsigned char *chars) } zval_ptr_dtor(value); - ZVAL_STR(value, smart_str_extract(&str)); + ZVAL_NEW_STR(value, smart_str_extract(&str)); } static const unsigned char hexchars[] = "0123456789ABCDEF"; From c02f6fb3feb77ca868dab2ba47702f145a7f030d Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sat, 7 Jun 2025 14:28:44 +0200 Subject: [PATCH 048/682] Output blocks of safe chars in php_filter_encode_html() Fixes a long-standing TODO, and is faster. --- ext/filter/sanitizing_filters.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/ext/filter/sanitizing_filters.c b/ext/filter/sanitizing_filters.c index d619bb0fc13a7..ebc20e47711db 100644 --- a/ext/filter/sanitizing_filters.c +++ b/ext/filter/sanitizing_filters.c @@ -31,6 +31,7 @@ static void php_filter_encode_html(zval *value, const unsigned char *chars) size_t len = Z_STRLEN_P(value); unsigned char *s = (unsigned char *)Z_STRVAL_P(value); unsigned char *e = s + len; + unsigned char *last_output = s; if (Z_STRLEN_P(value) == 0) { return; @@ -38,16 +39,17 @@ static void php_filter_encode_html(zval *value, const unsigned char *chars) while (s < e) { if (chars[*s]) { + smart_str_appendl(&str, (const char *) last_output, s - last_output); smart_str_appendl(&str, "&#", 2); smart_str_append_unsigned(&str, (zend_ulong)*s); smart_str_appendc(&str, ';'); - } else { - /* XXX: this needs to be optimized to work with blocks of 'safe' chars */ - smart_str_appendc(&str, *s); + last_output = s + 1; } s++; } + smart_str_appendl(&str, (const char *) last_output, s - last_output); + zval_ptr_dtor(value); ZVAL_NEW_STR(value, smart_str_extract(&str)); } From 4852a2c5cc71907e56a3d53058ddcbfa2b365a6b Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Mon, 9 Jun 2025 11:13:46 +0200 Subject: [PATCH 049/682] pdo_dblib: Use stack local array instead of heap allocation (#18801) --- ext/pdo_dblib/dblib_driver.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/ext/pdo_dblib/dblib_driver.c b/ext/pdo_dblib/dblib_driver.c index 0055dcb03b3c5..132e5c2e4dee1 100644 --- a/ext/pdo_dblib/dblib_driver.c +++ b/ext/pdo_dblib/dblib_driver.c @@ -233,9 +233,8 @@ zend_string *dblib_handle_last_id(pdo_dbh_t *dbh, const zend_string *name) pdo_dblib_db_handle *H = (pdo_dblib_db_handle *)dbh->driver_data; RETCODE ret; - char *id = NULL; + BYTE id[32]; size_t len; - zend_string *ret_id; /* * Would use scope_identity() but it's not implemented on Sybase @@ -267,13 +266,10 @@ zend_string *dblib_handle_last_id(pdo_dbh_t *dbh, const zend_string *name) return NULL; } - id = emalloc(32); len = dbconvert(NULL, (dbcoltype(H->link, 1)) , (dbdata(H->link, 1)) , (dbdatlen(H->link, 1)), SQLCHAR, (BYTE *)id, (DBINT)-1); dbcancel(H->link); - ret_id = zend_string_init(id, len, 0); - efree(id); - return ret_id; + return zend_string_init((const char *) id, len, 0); } static bool dblib_set_attr(pdo_dbh_t *dbh, zend_long attr, zval *val) From 9a9d98e02fe19c0f0df5aca9da50da5f74446515 Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Wed, 4 Jun 2025 12:09:37 +0200 Subject: [PATCH 050/682] Do not delete main chunk in zend_gc Closes GH-18756. Co-authored-by: Arnaud Le Blanc --- NEWS | 1 + Zend/tests/gh18756.phpt | 13 +++++++++++++ Zend/zend_alloc.c | 2 +- ext/zend_test/test.c | 10 ++++++++++ ext/zend_test/test.stub.php | 2 ++ ext/zend_test/test_arginfo.h | 6 +++++- 6 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 Zend/tests/gh18756.phpt diff --git a/NEWS b/NEWS index 5eb1694b5e816..4db7d93ff9808 100644 --- a/NEWS +++ b/NEWS @@ -5,6 +5,7 @@ PHP NEWS - Core: . Fixed GH-18695 (zend_ast_export() - float number is not preserved). (Oleg Efimov) + . Do not delete main chunk in zend_gc. (danog, Arnaud) - Curl: . Fix memory leak when setting a list via curl_setopt fails. (nielsdos) diff --git a/Zend/tests/gh18756.phpt b/Zend/tests/gh18756.phpt new file mode 100644 index 0000000000000..6e112d9060499 --- /dev/null +++ b/Zend/tests/gh18756.phpt @@ -0,0 +1,13 @@ +--TEST-- +Bug GH-18756: Zend MM may delete the main chunk +--EXTENSIONS-- +zend_test +--FILE-- + +==DONE== +--EXPECT-- +==DONE== diff --git a/Zend/zend_alloc.c b/Zend/zend_alloc.c index 573fd5fa26b80..2f80bdae3cfbd 100644 --- a/Zend/zend_alloc.c +++ b/Zend/zend_alloc.c @@ -2047,7 +2047,7 @@ ZEND_API size_t zend_mm_gc(zend_mm_heap *heap) i++; } } - if (chunk->free_pages == ZEND_MM_PAGES - ZEND_MM_FIRST_PAGE) { + if (chunk->free_pages == ZEND_MM_PAGES - ZEND_MM_FIRST_PAGE && chunk != heap->main_chunk) { zend_mm_chunk *next_chunk = chunk->next; zend_mm_delete_chunk(heap, chunk); diff --git a/ext/zend_test/test.c b/ext/zend_test/test.c index a7dd604d89ef3..04ece8bd2537e 100644 --- a/ext/zend_test/test.c +++ b/ext/zend_test/test.c @@ -1516,3 +1516,13 @@ static PHP_FUNCTION(zend_test_create_throwing_resource) zend_resource *res = zend_register_resource(NULL, le_throwing_resource); ZVAL_RES(return_value, res); } + +static PHP_FUNCTION(zend_test_gh18756) +{ + ZEND_PARSE_PARAMETERS_NONE(); + + zend_mm_heap *heap = zend_mm_startup(); + zend_mm_gc(heap); + zend_mm_gc(heap); + zend_mm_shutdown(heap, true, false); +} diff --git a/ext/zend_test/test.stub.php b/ext/zend_test/test.stub.php index c9477eef52712..f9cb93b5a1ccb 100644 --- a/ext/zend_test/test.stub.php +++ b/ext/zend_test/test.stub.php @@ -262,6 +262,8 @@ function zend_test_cast_fread($stream): void {} function zend_test_is_zend_ptr(int $addr): bool {} function zend_test_log_err_debug(string $str): void {} + + function zend_test_gh18756(): void {} } namespace ZendTestNS { diff --git a/ext/zend_test/test_arginfo.h b/ext/zend_test/test_arginfo.h index 5947a6587bbed..c7e3df5c58d24 100644 --- a/ext/zend_test/test_arginfo.h +++ b/ext/zend_test/test_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 9ddaf4d659226c55d49221c71702fa373d42695e */ + * Stub hash: 2f161861ab09b6b5b594dc2db7c2c9df49d76aa7 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_array_return, 0, 0, IS_ARRAY, 0) ZEND_END_ARG_INFO() @@ -162,6 +162,8 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_log_err_debug, 0, 1, I ZEND_ARG_TYPE_INFO(0, str, IS_STRING, 0) ZEND_END_ARG_INFO() +#define arginfo_zend_test_gh18756 arginfo_zend_test_void_return + #define arginfo_ZendTestNS2_namespaced_func arginfo_zend_test_is_pcre_bundled #define arginfo_ZendTestNS2_namespaced_deprecated_func arginfo_zend_test_void_return @@ -292,6 +294,7 @@ static ZEND_FUNCTION(zend_test_set_fmode); static ZEND_FUNCTION(zend_test_cast_fread); static ZEND_FUNCTION(zend_test_is_zend_ptr); static ZEND_FUNCTION(zend_test_log_err_debug); +static ZEND_FUNCTION(zend_test_gh18756); static ZEND_FUNCTION(ZendTestNS2_namespaced_func); static ZEND_FUNCTION(ZendTestNS2_namespaced_deprecated_func); static ZEND_FUNCTION(ZendTestNS2_ZendSubNS_namespaced_func); @@ -372,6 +375,7 @@ static const zend_function_entry ext_functions[] = { ZEND_FE(zend_test_cast_fread, arginfo_zend_test_cast_fread) ZEND_FE(zend_test_is_zend_ptr, arginfo_zend_test_is_zend_ptr) ZEND_FE(zend_test_log_err_debug, arginfo_zend_test_log_err_debug) + ZEND_FE(zend_test_gh18756, arginfo_zend_test_gh18756) ZEND_NS_FALIAS("ZendTestNS2", namespaced_func, ZendTestNS2_namespaced_func, arginfo_ZendTestNS2_namespaced_func) ZEND_NS_DEP_FALIAS("ZendTestNS2", namespaced_deprecated_func, ZendTestNS2_namespaced_deprecated_func, arginfo_ZendTestNS2_namespaced_deprecated_func) ZEND_NS_FALIAS("ZendTestNS2", namespaced_aliased_func, zend_test_void_return, arginfo_ZendTestNS2_namespaced_aliased_func) From ef92e06de19b51f5655b80ab41e5abd849ca6ffa Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sat, 7 Jun 2025 00:33:34 +0200 Subject: [PATCH 051/682] Fix memory leak on php_odbc_fetch_hash() failure The array is initialized but not freed. Closes GH-18787. --- NEWS | 3 +++ ext/odbc/php_odbc.c | 1 + 2 files changed, 4 insertions(+) diff --git a/NEWS b/NEWS index 4db7d93ff9808..2956cb8c6af0f 100644 --- a/NEWS +++ b/NEWS @@ -21,6 +21,9 @@ PHP NEWS . Fix memory leak in intl_datetime_decompose() on failure. (nielsdos) . Fix memory leak in locale lookup on failure. (nielsdos) +- ODBC: + . Fix memory leak on php_odbc_fetch_hash() failure. (nielsdos) + - OpenSSL: . Fix memory leak of X509_STORE in php_openssl_setup_verify() on failure. (nielsdos) diff --git a/ext/odbc/php_odbc.c b/ext/odbc/php_odbc.c index 579b5e989bd3a..77ba85fe12ae8 100644 --- a/ext/odbc/php_odbc.c +++ b/ext/odbc/php_odbc.c @@ -1370,6 +1370,7 @@ static void php_odbc_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, int result_type) if (rc == SQL_ERROR) { odbc_sql_error(result->conn_ptr, result->stmt, "SQLGetData"); efree(buf); + zval_ptr_dtor(return_value); RETURN_FALSE; } From 786090b35d2c339912c76588dacf32698f2e2d31 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sat, 7 Jun 2025 00:36:08 +0200 Subject: [PATCH 052/682] pdo_odbc: Fix memory leak if WideCharToMultiByte() fails Closes GH-18788. --- NEWS | 3 +++ ext/pdo_odbc/odbc_stmt.c | 1 + 2 files changed, 4 insertions(+) diff --git a/NEWS b/NEWS index 2956cb8c6af0f..2208cd6e59aad 100644 --- a/NEWS +++ b/NEWS @@ -34,6 +34,9 @@ PHP NEWS . Add missing filter cleanups on phar failure. (nielsdos) . Fixed bug GH-18642 (Signed integer overflow in ext/phar fseek). (nielsdos) +- PDO ODBC: + . Fix memory leak if WideCharToMultiByte() fails. (nielsdos) + - PGSQL: . Fix warning not being emitted when failure to cancel a query with pg_cancel_query(). (Girgias) diff --git a/ext/pdo_odbc/odbc_stmt.c b/ext/pdo_odbc/odbc_stmt.c index 4bf7162ea06e6..9f7ab24f8fadc 100644 --- a/ext/pdo_odbc/odbc_stmt.c +++ b/ext/pdo_odbc/odbc_stmt.c @@ -104,6 +104,7 @@ static int pdo_odbc_ucs22utf8(pdo_stmt_t *stmt, int is_unicode, zval *result) zend_string *str = zend_string_alloc(ret, 0); ret = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR) Z_STRVAL_P(result), Z_STRLEN_P(result)/sizeof(WCHAR), ZSTR_VAL(str), ZSTR_LEN(str), NULL, NULL); if (ret == 0) { + zend_string_efree(str); return PDO_ODBC_CONV_FAIL; } From b3c8afe272a6919248986c703c2e1defc73ff707 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Thu, 5 Jun 2025 19:37:46 +0200 Subject: [PATCH 053/682] Fix GH-18743: Incompatibility in Inline TLS Assembly on Alpine 3.22 GAS started checking the relocation for tlsgd: it must use the %rdi register. However, the inline assembly now uses %rax instead. Fix it by changing the "=a" output register to "=D". Source: https://github.com/bminor/binutils-gdb/blob/ec181e1710e37007a8d95c284609bfaa5868d086/gas/config/tc-i386.c#L6793 gottpoff is unaffected. Closes GH-18779. --- NEWS | 4 ++++ ext/opcache/jit/zend_jit_x86.dasc | 6 +++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/NEWS b/NEWS index 2208cd6e59aad..bf705c1da0b4d 100644 --- a/NEWS +++ b/NEWS @@ -24,6 +24,10 @@ PHP NEWS - ODBC: . Fix memory leak on php_odbc_fetch_hash() failure. (nielsdos) +- Opcache: + . Fixed bug GH-18743 (Incompatibility in Inline TLS Assembly on Alpine 3.22). + (nielsdos, Arnaud) + - OpenSSL: . Fix memory leak of X509_STORE in php_openssl_setup_verify() on failure. (nielsdos) diff --git a/ext/opcache/jit/zend_jit_x86.dasc b/ext/opcache/jit/zend_jit_x86.dasc index 1f1abb59a1c24..7061f6b2b73ad 100644 --- a/ext/opcache/jit/zend_jit_x86.dasc +++ b/ext/opcache/jit/zend_jit_x86.dasc @@ -2910,7 +2910,7 @@ static int zend_jit_setup(void) __asm__( "leaq _tsrm_ls_cache@tlsgd(%%rip), %0\n" - : "=a" (ti)); + : "=D" (ti)); tsrm_tls_offset = ti[1]; tsrm_tls_index = ti[0] * 8; #elif defined(__FreeBSD__) @@ -2918,7 +2918,7 @@ static int zend_jit_setup(void) __asm__( "leaq _tsrm_ls_cache@tlsgd(%%rip), %0\n" - : "=a" (ti)); + : "=D" (ti)); tsrm_tls_offset = ti[1]; /* Index is offset by 1 on FreeBSD (https://github.com/freebsd/freebsd-src/blob/bf56e8b9c8639ac4447d223b83cdc128107cc3cd/libexec/rtld-elf/rtld.c#L5260) */ tsrm_tls_index = (ti[0] + 1) * 8; @@ -2927,7 +2927,7 @@ static int zend_jit_setup(void) __asm__( "leaq _tsrm_ls_cache@tlsgd(%%rip), %0\n" - : "=a" (ti)); + : "=D" (ti)); tsrm_tls_offset = ti[1]; tsrm_tls_index = ti[0] * 16; #endif From 4f0554fa549e02b172afcbd17bac20cac1cd5b47 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Wed, 4 Jun 2025 20:07:13 +0200 Subject: [PATCH 054/682] Properly handle __debugInfo() returning an array reference Currently, this fails because the type is IS_REFERENCE instead of IS_ARRAY, but this could be confusing because a function return value is normally dereferenced automatically in a lot of cases. Closes GH-18762. --- NEWS | 1 + Zend/tests/__debugInfo_reference.phpt | 22 ++++++++++++++++++++++ Zend/zend_object_handlers.c | 3 +++ 3 files changed, 26 insertions(+) create mode 100644 Zend/tests/__debugInfo_reference.phpt diff --git a/NEWS b/NEWS index 9e6d47ed2a003..deae7fdbecc7a 100644 --- a/NEWS +++ b/NEWS @@ -14,6 +14,7 @@ PHP NEWS . Drop support for -z CLI/CGI flag. (nielsdos) . Fixed GH-17956 - development server 404 page does not adapt to mobiles. (pascalchevrel) + . Properly handle __debugInfo() returning an array reference. (nielsdos) - CURL: . Added CURLFOLLOW_ALL, CURLFOLLOW_OBEYCODE and CURLFOLLOW_FIRSTONLY diff --git a/Zend/tests/__debugInfo_reference.phpt b/Zend/tests/__debugInfo_reference.phpt new file mode 100644 index 0000000000000..8b5519f408915 --- /dev/null +++ b/Zend/tests/__debugInfo_reference.phpt @@ -0,0 +1,22 @@ +--TEST-- +__debugInfo with reference return +--FILE-- + 1]; + + public function &__debugInfo(): array + { + return $this->tmp; + } +} + +var_dump(new Test); + +?> +--EXPECT-- +object(Test)#1 (1) { + ["x"]=> + int(1) +} diff --git a/Zend/zend_object_handlers.c b/Zend/zend_object_handlers.c index ba26ac7128a3d..f79023ade1c25 100644 --- a/Zend/zend_object_handlers.c +++ b/Zend/zend_object_handlers.c @@ -206,6 +206,9 @@ ZEND_API HashTable *zend_std_get_debug_info(zend_object *object, int *is_temp) / } zend_call_known_instance_method_with_0_params(ce->__debugInfo, object, &retval); + if (UNEXPECTED(Z_ISREF(retval))) { + zend_unwrap_reference(&retval); + } if (Z_TYPE(retval) == IS_ARRAY) { if (!Z_REFCOUNTED(retval)) { *is_temp = 1; From b41a8aaffda6ab1cb1070b3516d9f445cd8d4685 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Mon, 9 Jun 2025 11:55:39 +0200 Subject: [PATCH 055/682] [ci skip] Fix NEWS location --- NEWS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS b/NEWS index deae7fdbecc7a..67f4d43e62edf 100644 --- a/NEWS +++ b/NEWS @@ -14,7 +14,6 @@ PHP NEWS . Drop support for -z CLI/CGI flag. (nielsdos) . Fixed GH-17956 - development server 404 page does not adapt to mobiles. (pascalchevrel) - . Properly handle __debugInfo() returning an array reference. (nielsdos) - CURL: . Added CURLFOLLOW_ALL, CURLFOLLOW_OBEYCODE and CURLFOLLOW_FIRSTONLY @@ -54,6 +53,7 @@ PHP NEWS evaluation) and GH-18464 (Recursion protection for deprecation constants not released on bailout). (DanielEScherzer and ilutov) . Fixed AST printing for immediately invoked Closure. (Dmitrii Derepko) + . Properly handle __debugInfo() returning an array reference. (nielsdos) - Curl: . Added curl_multi_get_handles(). (timwolla) From 7b6c0b99bbd2d2f4545d681d039856f448392c5e Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Mon, 9 Jun 2025 12:44:41 +0200 Subject: [PATCH 056/682] zend_alloc: Fix compilation with ZEND_MM_CUSTOM=0 (#18808) The poison feature relies on ZEND_MM_CUSTOM=1. If ZEND_MM_CUSTOM=0, the build fails. To fix this, move some `#endif`. --- Zend/zend_alloc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Zend/zend_alloc.c b/Zend/zend_alloc.c index 92873be7bfcef..74c8ae9c4eff4 100644 --- a/Zend/zend_alloc.c +++ b/Zend/zend_alloc.c @@ -2400,7 +2400,6 @@ static void zend_mm_check_leaks(zend_mm_heap *heap) static void *tracked_malloc(size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC); static void tracked_free_all(zend_mm_heap *heap); static void *poison_malloc(size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC); -#endif static void zend_mm_check_freelists(zend_mm_heap *heap) { @@ -2411,6 +2410,7 @@ static void zend_mm_check_freelists(zend_mm_heap *heap) } } } +#endif ZEND_API void zend_mm_shutdown(zend_mm_heap *heap, bool full, bool silent) { @@ -3041,7 +3041,6 @@ static void tracked_free_all(zend_mm_heap *heap) { free(ptr); } ZEND_HASH_FOREACH_END(); } -#endif static void* poison_malloc(size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) { @@ -3236,6 +3235,7 @@ static void poison_enable(zend_mm_heap *heap, char *parameters) zend_mm_set_custom_handlers_ex(heap, poison_malloc, poison_free, poison_realloc, poison_gc, poison_shutdown); } +#endif static void alloc_globals_ctor(zend_alloc_globals *alloc_globals) { From 186a8116beaf1148911032016e539e0ed52524f0 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Mon, 9 Jun 2025 12:16:57 +0200 Subject: [PATCH 057/682] Fix test conflict between copy_variation2-win32-mb.phpt and copy_variation2-win32.phpt Closes GH-18809. --- .../tests/file/copy_variation2-win32-mb.phpt | 64 +++++++++---------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/ext/standard/tests/file/copy_variation2-win32-mb.phpt b/ext/standard/tests/file/copy_variation2-win32-mb.phpt index 4251a24e54cf7..67d84ee3e32c1 100644 --- a/ext/standard/tests/file/copy_variation2-win32-mb.phpt +++ b/ext/standard/tests/file/copy_variation2-win32-mb.phpt @@ -22,24 +22,24 @@ fclose($file_handle); $dest_files = array( /* File names containing special(non-alpha numeric) characters */ - "_copy_variation2.tmp", - "@copy_variation2.tmp", - "#copy_variation2.tmp", - "+copy_variation2.tmp", - "?copy_variation2.tmp", - ">copy_variation2.tmp", - "!copy_variation2.tmp", - "©_variation2.tmp", - "(copy_variation2.tmp", - ":copy_variation2.tmp", - ";copy_variation2.tmp", - "=copy_variation2.tmp", - "[copy_variation2.tmp", - "^copy_variation2.tmp", - "{copy_variation2.tmp", - "|copy_variation2.tmp", - "~copy_variation2.tmp", - "\$copy_variation2.tmp" + "_copy_variation2_mb.tmp", + "@copy_variation2_mb.tmp", + "#copy_variation2_mb.tmp", + "+copy_variation2_mb.tmp", + "?copy_variation2_mb.tmp", + ">copy_variation2_mb.tmp", + "!copy_variation2_mb.tmp", + "©_variation2_mb.tmp", + "(copy_variation2_mb.tmp", + ":copy_variation2_mb.tmp", + ";copy_variation2_mb.tmp", + "=copy_variation2_mb.tmp", + "[copy_variation2_mb.tmp", + "^copy_variation2_mb.tmp", + "{copy_variation2_mb.tmp", + "|copy_variation2_mb.tmp", + "~copy_variation2_mb.tmp", + "\$copy_variation2_mb.tmp" ); echo "Size of the source file before copy operation => "; @@ -90,28 +90,28 @@ Size of the source file before copy operation => int(1500) -- Iteration 1 -- Copy operation => bool(true) Existence of destination file => bool(true) -Destination file name => %s/_copy_variation2.tmp +Destination file name => %s/_copy_variation2_mb.tmp Size of source file => int(1500) Size of destination file => int(1500) -- Iteration 2 -- Copy operation => bool(true) Existence of destination file => bool(true) -Destination file name => %s/@copy_variation2.tmp +Destination file name => %s/@copy_variation2_mb.tmp Size of source file => int(1500) Size of destination file => int(1500) -- Iteration 3 -- Copy operation => bool(true) Existence of destination file => bool(true) -Destination file name => %s/#copy_variation2.tmp +Destination file name => %s/#copy_variation2_mb.tmp Size of source file => int(1500) Size of destination file => int(1500) -- Iteration 4 -- Copy operation => bool(true) Existence of destination file => bool(true) -Destination file name => %s/+copy_variation2.tmp +Destination file name => %s/+copy_variation2_mb.tmp Size of source file => int(1500) Size of destination file => int(1500) @@ -130,21 +130,21 @@ Existence of destination file => bool(false) -- Iteration 7 -- Copy operation => bool(true) Existence of destination file => bool(true) -Destination file name => %s/!copy_variation2.tmp +Destination file name => %s/!copy_variation2_mb.tmp Size of source file => int(1500) Size of destination file => int(1500) -- Iteration 8 -- Copy operation => bool(true) Existence of destination file => bool(true) -Destination file name => %s/©_variation2.tmp +Destination file name => %s/©_variation2_mb.tmp Size of source file => int(1500) Size of destination file => int(1500) -- Iteration 9 -- Copy operation => bool(true) Existence of destination file => bool(true) -Destination file name => %s/(copy_variation2.tmp +Destination file name => %s/(copy_variation2_mb.tmp Size of source file => int(1500) Size of destination file => int(1500) @@ -157,35 +157,35 @@ Existence of destination file => bool(false) -- Iteration 11 -- Copy operation => bool(true) Existence of destination file => bool(true) -Destination file name => %s/;copy_variation2.tmp +Destination file name => %s/;copy_variation2_mb.tmp Size of source file => int(1500) Size of destination file => int(1500) -- Iteration 12 -- Copy operation => bool(true) Existence of destination file => bool(true) -Destination file name => %s/=copy_variation2.tmp +Destination file name => %s/=copy_variation2_mb.tmp Size of source file => int(1500) Size of destination file => int(1500) -- Iteration 13 -- Copy operation => bool(true) Existence of destination file => bool(true) -Destination file name => %s/[copy_variation2.tmp +Destination file name => %s/[copy_variation2_mb.tmp Size of source file => int(1500) Size of destination file => int(1500) -- Iteration 14 -- Copy operation => bool(true) Existence of destination file => bool(true) -Destination file name => %s/^copy_variation2.tmp +Destination file name => %s/^copy_variation2_mb.tmp Size of source file => int(1500) Size of destination file => int(1500) -- Iteration 15 -- Copy operation => bool(true) Existence of destination file => bool(true) -Destination file name => %s/{copy_variation2.tmp +Destination file name => %s/{copy_variation2_mb.tmp Size of source file => int(1500) Size of destination file => int(1500) @@ -198,14 +198,14 @@ Existence of destination file => bool(false) -- Iteration 17 -- Copy operation => bool(true) Existence of destination file => bool(true) -Destination file name => %s/~copy_variation2.tmp +Destination file name => %s/~copy_variation2_mb.tmp Size of source file => int(1500) Size of destination file => int(1500) -- Iteration 18 -- Copy operation => bool(true) Existence of destination file => bool(true) -Destination file name => %s/$copy_variation2.tmp +Destination file name => %s/$copy_variation2_mb.tmp Size of source file => int(1500) Size of destination file => int(1500) *** Done *** From d11f9717fdb10bccc4e17bd20508fb7b6d5c9359 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Mon, 9 Jun 2025 16:34:22 +0200 Subject: [PATCH 058/682] zend_alloc: Fix compile with ZEND_MM_STAT=0 Closes GH-18811. --- NEWS | 1 + Zend/zend_alloc.c | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/NEWS b/NEWS index bf705c1da0b4d..11fb787662c4a 100644 --- a/NEWS +++ b/NEWS @@ -6,6 +6,7 @@ PHP NEWS . Fixed GH-18695 (zend_ast_export() - float number is not preserved). (Oleg Efimov) . Do not delete main chunk in zend_gc. (danog, Arnaud) + . Fix compile issues with zend_alloc and some non-default options. (nielsdos) - Curl: . Fix memory leak when setting a list via curl_setopt fails. (nielsdos) diff --git a/Zend/zend_alloc.c b/Zend/zend_alloc.c index 2f80bdae3cfbd..47e9967a1e29f 100644 --- a/Zend/zend_alloc.c +++ b/Zend/zend_alloc.c @@ -2274,7 +2274,9 @@ void zend_mm_shutdown(zend_mm_heap *heap, bool full, bool silent) /* Make sure the heap free below does not use tracked_free(). */ heap->custom_heap.std._free = free; } +#if ZEND_MM_STAT heap->size = 0; +#endif } if (full) { @@ -2820,6 +2822,7 @@ static zend_always_inline zval *tracked_get_size_zv(zend_mm_heap *heap, void *pt } static zend_always_inline void tracked_check_limit(zend_mm_heap *heap, size_t add_size) { +#if ZEND_MM_STAT if (add_size > heap->limit - heap->size && !heap->overflow) { #if ZEND_DEBUG zend_mm_safe_error(heap, @@ -2831,6 +2834,7 @@ static zend_always_inline void tracked_check_limit(zend_mm_heap *heap, size_t ad heap->limit, add_size); #endif } +#endif } static void *tracked_malloc(size_t size) @@ -2844,7 +2848,9 @@ static void *tracked_malloc(size_t size) } tracked_add(heap, ptr, size); +#if ZEND_MM_STAT heap->size += size; +#endif return ptr; } @@ -2855,7 +2861,9 @@ static void tracked_free(void *ptr) { zend_mm_heap *heap = AG(mm_heap); zval *size_zv = tracked_get_size_zv(heap, ptr); +#if ZEND_MM_STAT heap->size -= Z_LVAL_P(size_zv); +#endif zend_hash_del_bucket(heap->tracked_allocs, (Bucket *) size_zv); free(ptr); } @@ -2880,7 +2888,9 @@ static void *tracked_realloc(void *ptr, size_t new_size) { ptr = __zend_realloc(ptr, new_size); tracked_add(heap, ptr, new_size); +#if ZEND_MM_STAT heap->size += new_size - old_size; +#endif return ptr; } From 53231a81dda7427b0d4e68d9cec66ad78b2688b3 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sun, 8 Jun 2025 14:07:47 +0100 Subject: [PATCH 059/682] ext/pdo_sqlite: adding Pdo_Sqlite::ATTR_BUSY_STATEMENT allow to check if a statement is still running before reusage. close GH-18804 --- NEWS | 3 ++- UPGRADING | 6 ++++++ build/php.m4 | 2 +- ext/pdo_sqlite/pdo_sqlite.c | 10 +++------- ext/pdo_sqlite/pdo_sqlite.stub.php | 3 +++ ext/pdo_sqlite/pdo_sqlite_arginfo.h | 8 +++++++- ext/pdo_sqlite/php_pdo_sqlite_int.h | 3 ++- ext/pdo_sqlite/sqlite_driver.c | 10 +++------- ext/pdo_sqlite/sqlite_statement.c | 15 ++++++++++----- .../subclasses/pdo_sqlite_constants.phpt | 2 ++ .../subclasses/pdo_sqlite_getattr_busy.phpt | 19 +++++++++++++++++++ 11 files changed, 58 insertions(+), 23 deletions(-) create mode 100644 ext/pdo_sqlite/tests/subclasses/pdo_sqlite_getattr_busy.phpt diff --git a/NEWS b/NEWS index 67f4d43e62edf..1c28ce97b7bd6 100644 --- a/NEWS +++ b/NEWS @@ -53,7 +53,6 @@ PHP NEWS evaluation) and GH-18464 (Recursion protection for deprecation constants not released on bailout). (DanielEScherzer and ilutov) . Fixed AST printing for immediately invoked Closure. (Dmitrii Derepko) - . Properly handle __debugInfo() returning an array reference. (nielsdos) - Curl: . Added curl_multi_get_handles(). (timwolla) @@ -144,6 +143,8 @@ PHP NEWS . Implement GH-17321: Add setAuthorizer to Pdo\Sqlite. (nielsdos) . PDO::sqliteCreateCollation now throws a TypeError if the callback has a wrong return type. (David Carlier) + . Added Pdo_Sqlite::ATTR_BUSY_STATEMENT constant to check + if a statement is currently executing. (David Carlier) - PGSQL: . Added pg_close_stmt to close a prepared statement while allowing diff --git a/UPGRADING b/UPGRADING index aebda12cf500e..b38fd2c7a2deb 100644 --- a/UPGRADING +++ b/UPGRADING @@ -194,6 +194,9 @@ PHP 8.5 UPGRADE NOTES IntlListFormatter::WIDTH_NARROW widths. It is supported from icu 67. +- PDO_Sqlite: + . Added class constant Pdo_Sqlite::ATTR_BUSY_STATEMENT. + - SOAP: . Enumeration cases are now dumped in __getTypes(). @@ -424,6 +427,9 @@ PHP 8.5 UPGRADE NOTES - PCRE: . Upgraded to pcre2lib from 10.44 to 10.45. +- PDO_Sqlite: + . Increased minimum release version support from 3.7.7 to 3.7.17. + - Readline: . The return types of readline_add_history(), readline_clear_history(), and readline_callback_handler_install() have been changed to true, rather diff --git a/build/php.m4 b/build/php.m4 index 640f01008009d..aa49766fedd7f 100644 --- a/build/php.m4 +++ b/build/php.m4 @@ -1923,7 +1923,7 @@ dnl dnl Common setup macro for SQLite library. dnl AC_DEFUN([PHP_SETUP_SQLITE], [ -PKG_CHECK_MODULES([SQLITE], [sqlite3 >= 3.7.7], [ +PKG_CHECK_MODULES([SQLITE], [sqlite3 >= 3.7.17], [ PHP_EVAL_INCLINE([$SQLITE_CFLAGS]) PHP_EVAL_LIBLINE([$SQLITE_LIBS], [$1]) ]) diff --git a/ext/pdo_sqlite/pdo_sqlite.c b/ext/pdo_sqlite/pdo_sqlite.c index fbbb336c1af1e..023e35a2bc33c 100644 --- a/ext/pdo_sqlite/pdo_sqlite.c +++ b/ext/pdo_sqlite/pdo_sqlite.c @@ -385,14 +385,10 @@ static int php_sqlite_collation_callback(void *context, int string1_len, const v zend_type_error("%s(): Return value of the collation callback must be of type int, %s returned", ZSTR_VAL(func_name), zend_zval_value_name(&retval)); zend_string_release(func_name); - ret = FAILURE; + zval_ptr_dtor(&retval); + return FAILURE; } - if (Z_LVAL(retval) > 0) { - ret = 1; - } else if (Z_LVAL(retval) < 0) { - ret = -1; - } - zval_ptr_dtor(&retval); + ret = ZEND_NORMALIZE_BOOL(Z_LVAL(retval)); } return ret; diff --git a/ext/pdo_sqlite/pdo_sqlite.stub.php b/ext/pdo_sqlite/pdo_sqlite.stub.php index 3832683598ed5..4cb6c14eae0a4 100644 --- a/ext/pdo_sqlite/pdo_sqlite.stub.php +++ b/ext/pdo_sqlite/pdo_sqlite.stub.php @@ -33,6 +33,9 @@ class Sqlite extends \PDO /** @cvalue PDO_SQLITE_ATTR_EXTENDED_RESULT_CODES */ public const int ATTR_EXTENDED_RESULT_CODES = UNKNOWN; + /** @cvalue PDO_SQLITE_ATTR_BUSY_STATEMENT */ + public const int ATTR_BUSY_STATEMENT = UNKNOWN; + /** @cvalue SQLITE_OK */ public const int OK = UNKNOWN; diff --git a/ext/pdo_sqlite/pdo_sqlite_arginfo.h b/ext/pdo_sqlite/pdo_sqlite_arginfo.h index 75de256e55c7b..ec826bc4bbc5a 100644 --- a/ext/pdo_sqlite/pdo_sqlite_arginfo.h +++ b/ext/pdo_sqlite/pdo_sqlite_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: f8cd6b3c6aa662d76dca3d0a28d61acfb5a611b5 */ + * Stub hash: ae1e62d72c3c8290c9f39f21b583e980ea9b8eb2 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Pdo_Sqlite_createAggregate, 0, 3, _IS_BOOL, 0) ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 0) @@ -110,6 +110,12 @@ static zend_class_entry *register_class_Pdo_Sqlite(zend_class_entry *class_entry zend_declare_typed_class_constant(class_entry, const_ATTR_EXTENDED_RESULT_CODES_name, &const_ATTR_EXTENDED_RESULT_CODES_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release(const_ATTR_EXTENDED_RESULT_CODES_name); + zval const_ATTR_BUSY_STATEMENT_value; + ZVAL_LONG(&const_ATTR_BUSY_STATEMENT_value, PDO_SQLITE_ATTR_BUSY_STATEMENT); + zend_string *const_ATTR_BUSY_STATEMENT_name = zend_string_init_interned("ATTR_BUSY_STATEMENT", sizeof("ATTR_BUSY_STATEMENT") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_ATTR_BUSY_STATEMENT_name, &const_ATTR_BUSY_STATEMENT_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_ATTR_BUSY_STATEMENT_name); + zval const_OK_value; ZVAL_LONG(&const_OK_value, SQLITE_OK); zend_string *const_OK_name = zend_string_init_interned("OK", sizeof("OK") - 1, 1); diff --git a/ext/pdo_sqlite/php_pdo_sqlite_int.h b/ext/pdo_sqlite/php_pdo_sqlite_int.h index 4a39781f85c96..8acb95015e79a 100644 --- a/ext/pdo_sqlite/php_pdo_sqlite_int.h +++ b/ext/pdo_sqlite/php_pdo_sqlite_int.h @@ -73,7 +73,8 @@ extern const struct pdo_stmt_methods sqlite_stmt_methods; enum { PDO_SQLITE_ATTR_OPEN_FLAGS = PDO_ATTR_DRIVER_SPECIFIC, PDO_SQLITE_ATTR_READONLY_STATEMENT, - PDO_SQLITE_ATTR_EXTENDED_RESULT_CODES + PDO_SQLITE_ATTR_EXTENDED_RESULT_CODES, + PDO_SQLITE_ATTR_BUSY_STATEMENT }; typedef int pdo_sqlite_create_collation_callback(void*, int, const void*, int, const void*); diff --git a/ext/pdo_sqlite/sqlite_driver.c b/ext/pdo_sqlite/sqlite_driver.c index d06d255c14cd8..2c907a34f489b 100644 --- a/ext/pdo_sqlite/sqlite_driver.c +++ b/ext/pdo_sqlite/sqlite_driver.c @@ -492,14 +492,10 @@ static int php_sqlite3_collation_callback(void *context, int string1_len, const zend_type_error("%s(): Return value of the collation callback must be of type int, %s returned", ZSTR_VAL(func_name), zend_zval_value_name(&retval)); zend_string_release(func_name); - ret = FAILURE; - } - if (Z_LVAL(retval) > 0) { - ret = 1; - } else if (Z_LVAL(retval) < 0) { - ret = -1; + zval_ptr_dtor(&retval); + return FAILURE; } - zval_ptr_dtor(&retval); + ret = ZEND_NORMALIZE_BOOL(Z_LVAL(retval)); } return ret; diff --git a/ext/pdo_sqlite/sqlite_statement.c b/ext/pdo_sqlite/sqlite_statement.c index c0e327450232f..64c8c8a86dd9a 100644 --- a/ext/pdo_sqlite/sqlite_statement.c +++ b/ext/pdo_sqlite/sqlite_statement.c @@ -375,13 +375,18 @@ static int pdo_sqlite_stmt_get_attribute(pdo_stmt_t *stmt, zend_long attr, zval case PDO_SQLITE_ATTR_READONLY_STATEMENT: ZVAL_FALSE(val); -#if SQLITE_VERSION_NUMBER >= 3007004 - if (sqlite3_stmt_readonly(S->stmt)) { - ZVAL_TRUE(val); - } -#endif + if (sqlite3_stmt_readonly(S->stmt)) { + ZVAL_TRUE(val); + } break; + case PDO_SQLITE_ATTR_BUSY_STATEMENT: + ZVAL_FALSE(val); + + if (sqlite3_stmt_busy(S->stmt)) { + ZVAL_TRUE(val); + } + break; default: return 0; } diff --git a/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_constants.phpt b/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_constants.phpt index 243240bef4662..d1db58b1323eb 100644 --- a/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_constants.phpt +++ b/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_constants.phpt @@ -13,6 +13,7 @@ var_dump(Pdo\Sqlite::OPEN_READWRITE); var_dump(Pdo\Sqlite::OPEN_CREATE); var_dump(Pdo\Sqlite::ATTR_READONLY_STATEMENT); var_dump(Pdo\Sqlite::ATTR_EXTENDED_RESULT_CODES); +var_dump(Pdo\Sqlite::ATTR_BUSY_STATEMENT); ?> --EXPECTF-- @@ -24,3 +25,4 @@ int(%d) int(%d) int(%d) int(%d) +int(%d) diff --git a/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_getattr_busy.phpt b/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_getattr_busy.phpt new file mode 100644 index 0000000000000..230fb7390ae50 --- /dev/null +++ b/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_getattr_busy.phpt @@ -0,0 +1,19 @@ +--TEST-- +Pdo\Sqlite::ATTR_BUSY_STATEMENT usage +--EXTENSIONS-- +pdo_sqlite +--FILE-- +query('CREATE TABLE test_busy (a string);'); +$db->query('INSERT INTO test_busy VALUES ("interleaved"), ("statements")'); +$st = $db->prepare('SELECT a FROM test_busy'); +var_dump($st->getAttribute(Pdo\Sqlite::ATTR_BUSY_STATEMENT)); +$st->execute(); +var_dump($st->getAttribute(Pdo\Sqlite::ATTR_BUSY_STATEMENT)); +?> +--EXPECTF-- +bool(false) +bool(true) From 931ee4bf6292e99b9342d2b171253167cbe51453 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Mon, 9 Jun 2025 20:04:24 +0200 Subject: [PATCH 060/682] [ci skip] Re-add accidentally removed NEWS entry --- NEWS | 1 + 1 file changed, 1 insertion(+) diff --git a/NEWS b/NEWS index 1c28ce97b7bd6..9fe60cbb875cc 100644 --- a/NEWS +++ b/NEWS @@ -53,6 +53,7 @@ PHP NEWS evaluation) and GH-18464 (Recursion protection for deprecation constants not released on bailout). (DanielEScherzer and ilutov) . Fixed AST printing for immediately invoked Closure. (Dmitrii Derepko) + . Properly handle __debugInfo() returning an array reference. (nielsdos) - Curl: . Added curl_multi_get_handles(). (timwolla) From e1181475e1102ae5e6e7cf711381420e0cbc6ab6 Mon Sep 17 00:00:00 2001 From: DanielEScherzer Date: Mon, 9 Jun 2025 12:22:07 -0700 Subject: [PATCH 061/682] release-process: update pre-release cycle docs (#18805) https://wiki.php.net/rfc/release_cycle_update --- docs/release-process.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/release-process.md b/docs/release-process.md index 5d603a22f078e..6eec8ff9f6435 100644 --- a/docs/release-process.md +++ b/docs/release-process.md @@ -18,14 +18,14 @@ PHP on the fourth Thursday of November each year. Following the GA release, we publish patch-level releases every four weeks, with at least one release candidate (RC) published two weeks before each patch-level release. -Each major and minor version undergoes a 24-week pre-release cycle before GA -release. The pre-release cycle begins on the second Thursday of June with the -first alpha release of the new major/minor version. The pre-release cycle -consists of at least: +Each major and minor version undergoes a 20-week pre-release cycle before GA +release. The pre-release cycle begins on the second Thursday of July with the +first alpha release of the new major/minor version (usually; count back from the +GA release date). The pre-release cycle consists of at least: - 3 alpha releases - 3 beta releases -- 6 release candidates +- 4 release candidates Feature freeze for the next major/minor occurs with the first beta release. From fe3bea090e598cc7d543dc6086c7a65f6e6787f1 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Mon, 9 Jun 2025 21:17:33 +0200 Subject: [PATCH 062/682] Fix technically incorrect sizeof This doesn't actually matter because both `*sal` and `**sal` are pointer sized, but this makes analysers happy. Fixes bug #68866. Closes GH-18816. --- main/network.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/main/network.c b/main/network.c index d4938a4a08c1e..8de81a6271a2f 100644 --- a/main/network.c +++ b/main/network.c @@ -227,7 +227,7 @@ PHPAPI int php_network_getaddresses(const char *host, int socktype, struct socka for (n = 1; (sai = sai->ai_next) != NULL; n++) ; - *sal = safe_emalloc((n + 1), sizeof(*sal), 0); + *sal = safe_emalloc((n + 1), sizeof(**sal), 0); sai = res; sap = *sal; @@ -266,7 +266,7 @@ PHPAPI int php_network_getaddresses(const char *host, int socktype, struct socka in = *((struct in_addr *) host_info->h_addr); } - *sal = safe_emalloc(2, sizeof(*sal), 0); + *sal = safe_emalloc(2, sizeof(**sal), 0); sap = *sal; *sap = emalloc(sizeof(struct sockaddr_in)); (*sap)->sa_family = AF_INET; From 3e37bcedf4df26efed5db89626d06b1897eb168f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Tue, 10 Jun 2025 09:05:37 +0200 Subject: [PATCH 063/682] [skip ci] Trim trailing whitespace in zend_compile.c Introduced in 5544be7018fe64584945c49fffcda20402bece73. Trimming to simplify the diff of the pipe operator RFC implementation. --- Zend/zend_compile.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 0669d106f15e9..379a97733d0ed 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -8361,10 +8361,10 @@ static zend_op_array *zend_compile_func_decl_ex( "nodiscard", sizeof("nodiscard")-1 ); - + if (nodiscard_attribute) { op_array->fn_flags |= ZEND_ACC_NODISCARD; - } + } } /* Do not leak the class scope into free standing functions, even if they are dynamically From 2036c7158d8f3d2bc57d105dcddf647ebca0c5db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Tue, 10 Jun 2025 09:12:32 +0200 Subject: [PATCH 064/682] [skip ci] Add T_VOID_CAST constant to UPGRADING see 8779e2a6031b78bb0e3cac980410eb038b9932c4 --- UPGRADING | 3 +++ 1 file changed, 3 insertions(+) diff --git a/UPGRADING b/UPGRADING index b38fd2c7a2deb..f642166de5150 100644 --- a/UPGRADING +++ b/UPGRADING @@ -474,6 +474,9 @@ PHP 8.5 UPGRADE NOTES - SHUT_WR. - SHUT_RDWR. +- Tokenizer: + . T_VOID_CAST. + ======================================== 11. Changes to INI File Handling ======================================== From 1c09c0c8323342aaaecbe444b3afa45efbd72ced Mon Sep 17 00:00:00 2001 From: Larry Garfield Date: Tue, 10 Jun 2025 02:59:43 -0500 Subject: [PATCH 065/682] RFC: Pipe operator (#17118) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Gina Peter Banyard Co-authored-by: Arnaud Le Blanc Co-authored-by: Tim Düsterhus --- NEWS | 1 + UPGRADING | 3 + Zend/tests/pipe_operator/ast.phpt | 109 ++++++++++++++++++ Zend/tests/pipe_operator/call_by_ref.phpt | 37 ++++++ .../pipe_operator/call_prefer_by_ref.phpt | 17 +++ .../tests/pipe_operator/complex_ordering.phpt | 20 ++++ .../compound_userland_calls.phpt | 19 +++ .../pipe_operator/exception_interruption.phpt | 37 ++++++ .../pipe_operator/function_not_found.phpt | 15 +++ Zend/tests/pipe_operator/generators.phpt | 30 +++++ .../pipe_operator/mixed_callable_call.phpt | 80 +++++++++++++ .../pipe_operator/namespaced_functions.phpt | 22 ++++ Zend/tests/pipe_operator/optimizations.phpt | 89 ++++++++++++++ .../pipe_operator/optional_parameters.phpt | 15 +++ .../pipe_operator/precedence_addition.phpt | 16 +++ .../pipe_operator/precedence_coalesce.phpt | 17 +++ .../pipe_operator/precedence_comparison.phpt | 16 +++ .../pipe_operator/precedence_ternary.phpt | 36 ++++++ .../pipe_operator/simple_builtin_call.phpt | 11 ++ .../pipe_operator/simple_userland_call.phpt | 15 +++ .../pipe_operator/too_many_parameters.phpt | 21 ++++ Zend/tests/pipe_operator/type_mismatch.phpt | 20 ++++ Zend/tests/pipe_operator/void_return.phpt | 21 ++++ Zend/tests/pipe_operator/wrapped_chains.phpt | 21 ++++ Zend/zend_ast.c | 1 + Zend/zend_ast.h | 1 + Zend/zend_compile.c | 54 +++++++++ Zend/zend_language_parser.y | 4 + Zend/zend_language_scanner.l | 4 + ext/tokenizer/tokenizer_data.c | 1 + ext/tokenizer/tokenizer_data.stub.php | 5 + ext/tokenizer/tokenizer_data_arginfo.h | 3 +- 32 files changed, 760 insertions(+), 1 deletion(-) create mode 100644 Zend/tests/pipe_operator/ast.phpt create mode 100644 Zend/tests/pipe_operator/call_by_ref.phpt create mode 100644 Zend/tests/pipe_operator/call_prefer_by_ref.phpt create mode 100644 Zend/tests/pipe_operator/complex_ordering.phpt create mode 100644 Zend/tests/pipe_operator/compound_userland_calls.phpt create mode 100644 Zend/tests/pipe_operator/exception_interruption.phpt create mode 100644 Zend/tests/pipe_operator/function_not_found.phpt create mode 100644 Zend/tests/pipe_operator/generators.phpt create mode 100644 Zend/tests/pipe_operator/mixed_callable_call.phpt create mode 100644 Zend/tests/pipe_operator/namespaced_functions.phpt create mode 100644 Zend/tests/pipe_operator/optimizations.phpt create mode 100644 Zend/tests/pipe_operator/optional_parameters.phpt create mode 100644 Zend/tests/pipe_operator/precedence_addition.phpt create mode 100644 Zend/tests/pipe_operator/precedence_coalesce.phpt create mode 100644 Zend/tests/pipe_operator/precedence_comparison.phpt create mode 100644 Zend/tests/pipe_operator/precedence_ternary.phpt create mode 100644 Zend/tests/pipe_operator/simple_builtin_call.phpt create mode 100644 Zend/tests/pipe_operator/simple_userland_call.phpt create mode 100644 Zend/tests/pipe_operator/too_many_parameters.phpt create mode 100644 Zend/tests/pipe_operator/type_mismatch.phpt create mode 100644 Zend/tests/pipe_operator/void_return.phpt create mode 100644 Zend/tests/pipe_operator/wrapped_chains.phpt diff --git a/NEWS b/NEWS index 9fe60cbb875cc..b1100f7672226 100644 --- a/NEWS +++ b/NEWS @@ -54,6 +54,7 @@ PHP NEWS released on bailout). (DanielEScherzer and ilutov) . Fixed AST printing for immediately invoked Closure. (Dmitrii Derepko) . Properly handle __debugInfo() returning an array reference. (nielsdos) + . Added the pipe (|>) operator. (crell) - Curl: . Added curl_multi_get_handles(). (timwolla) diff --git a/UPGRADING b/UPGRADING index f642166de5150..7025c9778e7ac 100644 --- a/UPGRADING +++ b/UPGRADING @@ -144,6 +144,8 @@ PHP 8.5 UPGRADE NOTES RFC: https://wiki.php.net/rfc/attributes-on-constants . The #[\Deprecated] attribute can now be used on constants. RFC: https://wiki.php.net/rfc/attributes-on-constants + . Added the pipe (|>) operator. + RFC: https://wiki.php.net/rfc/pipe-operator-v3 - Curl: . Added support for share handles that are persisted across multiple PHP @@ -476,6 +478,7 @@ PHP 8.5 UPGRADE NOTES - Tokenizer: . T_VOID_CAST. + . T_PIPE. ======================================== 11. Changes to INI File Handling diff --git a/Zend/tests/pipe_operator/ast.phpt b/Zend/tests/pipe_operator/ast.phpt new file mode 100644 index 0000000000000..e8c088dabfdbd --- /dev/null +++ b/Zend/tests/pipe_operator/ast.phpt @@ -0,0 +1,109 @@ +--TEST-- +A pipe operator displays as a pipe operator when outputting syntax, with correct parens. +--FILE-- + baz() . quux()); +} catch (AssertionError $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + assert(false && (foo() . bar()) |> baz() . quux()); +} catch (AssertionError $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + assert(false && foo() . (bar() |> baz()) . quux()); +} catch (AssertionError $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + assert(false && foo() . bar() |> (baz() . quux())); +} catch (AssertionError $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + assert(false && (foo() . bar() |> baz()) . quux()); +} catch (AssertionError $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + assert(false && foo() . (bar() |> baz() . quux())); +} catch (AssertionError $e) { + echo $e->getMessage(), PHP_EOL; +} + +print "<, which binds lower\n"; + +try { + assert(false && foo() < bar() |> baz()); +} catch (AssertionError $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + assert(false && (foo() < bar()) |> baz()); +} catch (AssertionError $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + assert(false && foo() < (bar() |> baz())); +} catch (AssertionError $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + assert(false && foo() |> bar() < baz()); +} catch (AssertionError $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + assert(false && (foo() |> bar()) < baz()); +} catch (AssertionError $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + assert(false && foo() |> (bar() < baz())); +} catch (AssertionError $e) { + echo $e->getMessage(), PHP_EOL; +} + + + +print "misc examples\n"; + +try { + assert(false && foo() |> (bar() |> baz(...))); +} catch (AssertionError $e) { + echo $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +Concat, which binds higher +assert(false && foo() . bar() |> baz() . quux()) +assert(false && foo() . bar() |> baz() . quux()) +assert(false && foo() . (bar() |> baz()) . quux()) +assert(false && foo() . bar() |> baz() . quux()) +assert(false && (foo() . bar() |> baz()) . quux()) +assert(false && foo() . (bar() |> baz() . quux())) +<, which binds lower +assert(false && foo() < bar() |> baz()) +assert(false && (foo() < bar()) |> baz()) +assert(false && foo() < bar() |> baz()) +assert(false && foo() |> bar() < baz()) +assert(false && foo() |> bar() < baz()) +assert(false && foo() |> (bar() < baz())) +misc examples +assert(false && foo() |> (bar() |> baz(...))) diff --git a/Zend/tests/pipe_operator/call_by_ref.phpt b/Zend/tests/pipe_operator/call_by_ref.phpt new file mode 100644 index 0000000000000..026089b0a6547 --- /dev/null +++ b/Zend/tests/pipe_operator/call_by_ref.phpt @@ -0,0 +1,37 @@ +--TEST-- +Pipe operator rejects by-reference functions. +--FILE-- + _modify(...); + var_dump($res1); +} catch (\Error $e) { + echo $e->getMessage(), PHP_EOL; +} + +// Complex variables. +try { + $a = ['foo' => 'beep']; + $res2 = $a |> _append(...); + var_dump($res2); +} catch (\Error $e) { + echo $e->getMessage(), PHP_EOL; +} + + +?> +--EXPECTF-- +_modify(): Argument #1 ($a) could not be passed by reference +_append(): Argument #1 ($a) could not be passed by reference diff --git a/Zend/tests/pipe_operator/call_prefer_by_ref.phpt b/Zend/tests/pipe_operator/call_prefer_by_ref.phpt new file mode 100644 index 0000000000000..31750ac280d71 --- /dev/null +++ b/Zend/tests/pipe_operator/call_prefer_by_ref.phpt @@ -0,0 +1,17 @@ +--TEST-- +Pipe operator accepts prefer-by-reference functions. +--FILE-- + array_multisort(...); + var_dump($r); +} catch (\Error $e) { + echo $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +bool(true) diff --git a/Zend/tests/pipe_operator/complex_ordering.phpt b/Zend/tests/pipe_operator/complex_ordering.phpt new file mode 100644 index 0000000000000..49f3d47ade4b9 --- /dev/null +++ b/Zend/tests/pipe_operator/complex_ordering.phpt @@ -0,0 +1,20 @@ +--TEST-- +Functions are executed in the expected order +--FILE-- + (bar() ? baz(...) : quux(...)) + |> var_dump(...); + +?> +--EXPECT-- +foo +bar +quux +int(1) diff --git a/Zend/tests/pipe_operator/compound_userland_calls.phpt b/Zend/tests/pipe_operator/compound_userland_calls.phpt new file mode 100644 index 0000000000000..922c3c5ac23d8 --- /dev/null +++ b/Zend/tests/pipe_operator/compound_userland_calls.phpt @@ -0,0 +1,19 @@ +--TEST-- +Pipe operator chains +--FILE-- + '_test1' |> '_test2'; + +var_dump($res1); +?> +--EXPECT-- +int(12) diff --git a/Zend/tests/pipe_operator/exception_interruption.phpt b/Zend/tests/pipe_operator/exception_interruption.phpt new file mode 100644 index 0000000000000..6711b652e7c6b --- /dev/null +++ b/Zend/tests/pipe_operator/exception_interruption.phpt @@ -0,0 +1,37 @@ +--TEST-- +A pipe interrupted by an exception, to demonstrate correct order of execution. +--FILE-- + (bar() ? baz(...) : quux(...)) + |> var_dump(...); +} +catch (Throwable $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} + +try { + $result = foo() + |> (throw new Exception('Break')) + |> (bar() ? baz(...) : quux(...)) + |> var_dump(...); +} +catch (Throwable $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} + +?> +--EXPECTF-- +foo +bar +quux +Exception: Oops +foo +Exception: Break diff --git a/Zend/tests/pipe_operator/function_not_found.phpt b/Zend/tests/pipe_operator/function_not_found.phpt new file mode 100644 index 0000000000000..747f53ce3e3d1 --- /dev/null +++ b/Zend/tests/pipe_operator/function_not_found.phpt @@ -0,0 +1,15 @@ +--TEST-- +Pipe operator throws normally on missing function +--FILE-- + '_test'; +} +catch (Throwable $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +Error: Call to undefined function _test() diff --git a/Zend/tests/pipe_operator/generators.phpt b/Zend/tests/pipe_operator/generators.phpt new file mode 100644 index 0000000000000..9607af581fcdc --- /dev/null +++ b/Zend/tests/pipe_operator/generators.phpt @@ -0,0 +1,30 @@ +--TEST-- +Generators +--FILE-- + map_incr(...) |> iterator_to_array(...); + +var_dump($result); +?> +--EXPECT-- +array(3) { + [0]=> + int(2) + [1]=> + int(3) + [2]=> + int(4) +} diff --git a/Zend/tests/pipe_operator/mixed_callable_call.phpt b/Zend/tests/pipe_operator/mixed_callable_call.phpt new file mode 100644 index 0000000000000..55bae626f1890 --- /dev/null +++ b/Zend/tests/pipe_operator/mixed_callable_call.phpt @@ -0,0 +1,80 @@ +--TEST-- +Pipe operator handles all callable styles +--FILE-- + times3(...) + |> 'times5' + |> $test->times7(...) + |> [$test, 'times11'] + |> StaticTest::times13(...) + |> [StaticTest::class, 'times17'] + |> new Times23() + |> $times29 + |> fn($x) => times2($x) +; + +var_dump($res1); +?> +--EXPECT-- +int(340510170) diff --git a/Zend/tests/pipe_operator/namespaced_functions.phpt b/Zend/tests/pipe_operator/namespaced_functions.phpt new file mode 100644 index 0000000000000..70ce85c0a25c6 --- /dev/null +++ b/Zend/tests/pipe_operator/namespaced_functions.phpt @@ -0,0 +1,22 @@ +--TEST-- +Pipe operator handles namespaces +--FILE-- + test(...); + + 5 |> \Beep\test(...); +} +?> +--EXPECT-- +5 +5 diff --git a/Zend/tests/pipe_operator/optimizations.phpt b/Zend/tests/pipe_operator/optimizations.phpt new file mode 100644 index 0000000000000..afdc528337c13 --- /dev/null +++ b/Zend/tests/pipe_operator/optimizations.phpt @@ -0,0 +1,89 @@ +--TEST-- +Pipe operator optimizes away most callables +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.opt_debug_level=0x20000 +--EXTENSIONS-- +opcache +--FILE-- + _test1(...) + |> $o->foo(...) + |> Other::bar(...) +; + +var_dump($res1); +?> +--EXPECTF-- +$_main: + ; (lines=18, args=0, vars=2, tmps=2) + ; (after optimizer) + ; %s:1-27 +0000 V2 = NEW 0 string("Other") +0001 DO_FCALL +0002 ASSIGN CV0($o) V2 +0003 INIT_FCALL 1 %d string("_test1") +0004 SEND_VAL int(5) 1 +0005 T2 = DO_UCALL +0006 INIT_METHOD_CALL 1 CV0($o) string("foo") +0007 SEND_VAL_EX T2 1 +0008 V3 = DO_FCALL +0009 T2 = QM_ASSIGN V3 +0010 INIT_STATIC_METHOD_CALL 1 string("Other") string("bar") +0011 SEND_VAL T2 1 +0012 V2 = DO_UCALL +0013 ASSIGN CV1($res1) V2 +0014 INIT_FCALL 1 %d string("var_dump") +0015 SEND_VAR CV1($res1) 1 +0016 DO_ICALL +0017 RETURN int(1) +LIVE RANGES: + 2: 0001 - 0002 (new) + 2: 0010 - 0011 (tmp/var) + +_test1: + ; (lines=4, args=1, vars=1, tmps=1) + ; (after optimizer) + ; %s:3-5 +0000 CV0($a) = RECV 1 +0001 T1 = ADD CV0($a) int(1) +0002 VERIFY_RETURN_TYPE T1 +0003 RETURN T1 + +Other::foo: + ; (lines=4, args=1, vars=1, tmps=1) + ; (after optimizer) + ; %s:8-10 +0000 CV0($a) = RECV 1 +0001 T1 = ADD CV0($a) CV0($a) +0002 VERIFY_RETURN_TYPE T1 +0003 RETURN T1 + +Other::bar: + ; (lines=4, args=1, vars=1, tmps=1) + ; (after optimizer) + ; %s:12-14 +0000 CV0($a) = RECV 1 +0001 T1 = SUB CV0($a) int(1) +0002 VERIFY_RETURN_TYPE T1 +0003 RETURN T1 +int(11) diff --git a/Zend/tests/pipe_operator/optional_parameters.phpt b/Zend/tests/pipe_operator/optional_parameters.phpt new file mode 100644 index 0000000000000..53cce2e9972e8 --- /dev/null +++ b/Zend/tests/pipe_operator/optional_parameters.phpt @@ -0,0 +1,15 @@ +--TEST-- +Pipe operator accepts optional-parameter functions +--FILE-- + '_test'; + +var_dump($res1); +?> +--EXPECT-- +int(8) diff --git a/Zend/tests/pipe_operator/precedence_addition.phpt b/Zend/tests/pipe_operator/precedence_addition.phpt new file mode 100644 index 0000000000000..4fd64639b4518 --- /dev/null +++ b/Zend/tests/pipe_operator/precedence_addition.phpt @@ -0,0 +1,16 @@ +--TEST-- +Pipe binds lower than addition +--FILE-- + '_test1'; + +var_dump($res1); +?> +--EXPECT-- +int(14) diff --git a/Zend/tests/pipe_operator/precedence_coalesce.phpt b/Zend/tests/pipe_operator/precedence_coalesce.phpt new file mode 100644 index 0000000000000..daf699d9fe85d --- /dev/null +++ b/Zend/tests/pipe_operator/precedence_coalesce.phpt @@ -0,0 +1,17 @@ +--TEST-- +Pipe binds higher than coalesce +--FILE-- + get_username(...) + ?? 'default'; + +var_dump($user); +?> +--EXPECT-- +string(1) "5" diff --git a/Zend/tests/pipe_operator/precedence_comparison.phpt b/Zend/tests/pipe_operator/precedence_comparison.phpt new file mode 100644 index 0000000000000..84f71b74058a0 --- /dev/null +++ b/Zend/tests/pipe_operator/precedence_comparison.phpt @@ -0,0 +1,16 @@ +--TEST-- +Pipe binds higher than comparison +--FILE-- + _test1(...) == 10 ; +var_dump($res1); + +?> +--EXPECTF-- +bool(true) diff --git a/Zend/tests/pipe_operator/precedence_ternary.phpt b/Zend/tests/pipe_operator/precedence_ternary.phpt new file mode 100644 index 0000000000000..207d15dab9d65 --- /dev/null +++ b/Zend/tests/pipe_operator/precedence_ternary.phpt @@ -0,0 +1,36 @@ +--TEST-- +Pipe binds higher than ternary +--FILE-- + is_odd(...) ? 'odd' : 'even'; +var_dump($res1); + +// The pipe binds first, resulting in bool ? int : string, which is well-understood. +$x = true; +$y = 'beep'; +$z = 'default'; +$ret3 = $x ? $y |> strlen(...) : $z; +var_dump($ret3); + + +?> +--EXPECT-- +string(3) "odd" +int(4) diff --git a/Zend/tests/pipe_operator/simple_builtin_call.phpt b/Zend/tests/pipe_operator/simple_builtin_call.phpt new file mode 100644 index 0000000000000..72f5968dd0b65 --- /dev/null +++ b/Zend/tests/pipe_operator/simple_builtin_call.phpt @@ -0,0 +1,11 @@ +--TEST-- +Pipe operator supports built-in functions +--FILE-- + 'strlen'; + +var_dump($res1); +?> +--EXPECT-- +int(5) diff --git a/Zend/tests/pipe_operator/simple_userland_call.phpt b/Zend/tests/pipe_operator/simple_userland_call.phpt new file mode 100644 index 0000000000000..7f311f9a10474 --- /dev/null +++ b/Zend/tests/pipe_operator/simple_userland_call.phpt @@ -0,0 +1,15 @@ +--TEST-- +Pipe operator supports user-defined functions +--FILE-- + '_test'; + +var_dump($res1); +?> +--EXPECT-- +int(6) diff --git a/Zend/tests/pipe_operator/too_many_parameters.phpt b/Zend/tests/pipe_operator/too_many_parameters.phpt new file mode 100644 index 0000000000000..b36046bde05c2 --- /dev/null +++ b/Zend/tests/pipe_operator/too_many_parameters.phpt @@ -0,0 +1,21 @@ +--TEST-- +Pipe operator fails on multi-parameter functions +--FILE-- + '_test'; +} +catch (Throwable $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} + + +?> +--EXPECTF-- +ArgumentCountError: Too few arguments to function %s, 1 passed in %s on line %s and exactly 2 expected diff --git a/Zend/tests/pipe_operator/type_mismatch.phpt b/Zend/tests/pipe_operator/type_mismatch.phpt new file mode 100644 index 0000000000000..2cee15bb47a0d --- /dev/null +++ b/Zend/tests/pipe_operator/type_mismatch.phpt @@ -0,0 +1,20 @@ +--TEST-- +Pipe operator respects types +--FILE-- + '_test'; + var_dump($res1); +} +catch (Throwable $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} + +?> +--EXPECTF-- +TypeError: _test(): Argument #1 ($a) must be of type int, string given, called in %s on line %d diff --git a/Zend/tests/pipe_operator/void_return.phpt b/Zend/tests/pipe_operator/void_return.phpt new file mode 100644 index 0000000000000..0dbba519297cd --- /dev/null +++ b/Zend/tests/pipe_operator/void_return.phpt @@ -0,0 +1,21 @@ +--TEST-- +Pipe operator fails void return chaining in strict mode +--FILE-- + 'nonReturnFunction' + |> 'strlen'; + var_dump($result); +} +catch (Throwable $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +TypeError: strlen(): Argument #1 ($string) must be of type string, null given diff --git a/Zend/tests/pipe_operator/wrapped_chains.phpt b/Zend/tests/pipe_operator/wrapped_chains.phpt new file mode 100644 index 0000000000000..e2b6f39f7f342 --- /dev/null +++ b/Zend/tests/pipe_operator/wrapped_chains.phpt @@ -0,0 +1,21 @@ +--TEST-- +Pipe operator chains saved as a closure +--FILE-- + $x |> '_test1' |> '_test2'; + +$res1 = $func(5); + +var_dump($res1); +?> +--EXPECT-- +int(12) diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index 6a2826160e9c0..beecf51216a94 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -2518,6 +2518,7 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio case ZEND_AST_GREATER_EQUAL: BINARY_OP(" >= ", 180, 181, 181); case ZEND_AST_AND: BINARY_OP(" && ", 130, 130, 131); case ZEND_AST_OR: BINARY_OP(" || ", 120, 120, 121); + case ZEND_AST_PIPE: BINARY_OP(" |> ", 183, 183, 184); case ZEND_AST_ARRAY_ELEM: if (ast->child[1]) { zend_ast_export_ex(str, ast->child[1], 80, indent); diff --git a/Zend/zend_ast.h b/Zend/zend_ast.h index 9348c35f6cc07..c82ca66c9f573 100644 --- a/Zend/zend_ast.h +++ b/Zend/zend_ast.h @@ -154,6 +154,7 @@ enum _zend_ast_kind { ZEND_AST_MATCH_ARM, ZEND_AST_NAMED_ARG, ZEND_AST_PARENT_PROPERTY_HOOK_CALL, + ZEND_AST_PIPE, /* 3 child nodes */ ZEND_AST_METHOD_CALL = 3 << ZEND_AST_NUM_CHILDREN_SHIFT, diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 379a97733d0ed..2bc0cf7b703d9 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -6427,6 +6427,57 @@ static bool can_match_use_jumptable(zend_ast_list *arms) { return 1; } +static void zend_compile_pipe(znode *result, zend_ast *ast) +{ + zend_ast *operand_ast = ast->child[0]; + zend_ast *callable_ast = ast->child[1]; + + /* Compile the left hand side down to a value first. */ + znode operand_result; + zend_compile_expr(&operand_result, operand_ast); + + /* Wrap simple values in a ZEND_QM_ASSIGN opcode to ensure references + * always fail. They will already fail in complex cases like arrays, + * so those don't need a wrapper. */ + znode wrapped_operand_result; + if (operand_result.op_type & (IS_CV|IS_VAR)) { + zend_emit_op_tmp(&wrapped_operand_result, ZEND_QM_ASSIGN, &operand_result, NULL); + } else { + wrapped_operand_result = operand_result; + } + + /* Turn the operand into a function parameter list. */ + zend_ast *arg_list_ast = zend_ast_create_list(1, ZEND_AST_ARG_LIST, zend_ast_create_znode(&wrapped_operand_result)); + + zend_ast *fcall_ast; + znode callable_result; + + /* Turn $foo |> bar(...) into bar($foo). */ + if (callable_ast->kind == ZEND_AST_CALL + && callable_ast->child[1]->kind == ZEND_AST_CALLABLE_CONVERT) { + fcall_ast = zend_ast_create(ZEND_AST_CALL, + callable_ast->child[0], arg_list_ast); + /* Turn $foo |> bar::baz(...) into bar::baz($foo). */ + } else if (callable_ast->kind == ZEND_AST_STATIC_CALL + && callable_ast->child[2]->kind == ZEND_AST_CALLABLE_CONVERT) { + fcall_ast = zend_ast_create(ZEND_AST_STATIC_CALL, + callable_ast->child[0], callable_ast->child[1], arg_list_ast); + /* Turn $foo |> $bar->baz(...) into $bar->baz($foo). */ + } else if (callable_ast->kind == ZEND_AST_METHOD_CALL + && callable_ast->child[2]->kind == ZEND_AST_CALLABLE_CONVERT) { + fcall_ast = zend_ast_create(ZEND_AST_METHOD_CALL, + callable_ast->child[0], callable_ast->child[1], arg_list_ast); + /* Turn $foo |> $expr into ($expr)($foo) */ + } else { + zend_compile_expr(&callable_result, callable_ast); + callable_ast = zend_ast_create_znode(&callable_result); + fcall_ast = zend_ast_create(ZEND_AST_CALL, + callable_ast, arg_list_ast); + } + + zend_compile_expr(result, fcall_ast); +} + static void zend_compile_match(znode *result, zend_ast *ast) { zend_ast *expr_ast = ast->child[0]; @@ -11769,6 +11820,9 @@ static void zend_compile_expr_inner(znode *result, zend_ast *ast) /* {{{ */ case ZEND_AST_MATCH: zend_compile_match(result, ast); return; + case ZEND_AST_PIPE: + zend_compile_pipe(result, ast); + return; default: ZEND_ASSERT(0 /* not supported */); } diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index 08b2ac6b3f39b..816b8126cbf25 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -71,6 +71,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*); %left T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG %nonassoc T_IS_EQUAL T_IS_NOT_EQUAL T_IS_IDENTICAL T_IS_NOT_IDENTICAL T_SPACESHIP %nonassoc '<' T_IS_SMALLER_OR_EQUAL '>' T_IS_GREATER_OR_EQUAL +%left T_PIPE %left '.' %left T_SL T_SR %left '+' '-' @@ -237,6 +238,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*); %token T_COALESCE "'??'" %token T_POW "'**'" %token T_POW_EQUAL "'**='" +%token T_PIPE "'|>'" /* We need to split the & token in two to avoid a shift/reduce conflict. For T1&$v and T1&T2, * with only one token lookahead, bison does not know whether to reduce T1 as a complete type, * or shift to continue parsing an intersection type. */ @@ -1292,6 +1294,8 @@ expr: { $$ = zend_ast_create_binary_op(ZEND_IS_EQUAL, $1, $3); } | expr T_IS_NOT_EQUAL expr { $$ = zend_ast_create_binary_op(ZEND_IS_NOT_EQUAL, $1, $3); } + | expr T_PIPE expr + { $$ = zend_ast_create(ZEND_AST_PIPE, $1, $3); } | expr '<' expr { $$ = zend_ast_create_binary_op(ZEND_IS_SMALLER, $1, $3); } | expr T_IS_SMALLER_OR_EQUAL expr diff --git a/Zend/zend_language_scanner.l b/Zend/zend_language_scanner.l index 4c883b81c5f7d..5e377249422a5 100644 --- a/Zend/zend_language_scanner.l +++ b/Zend/zend_language_scanner.l @@ -1861,6 +1861,10 @@ OPTIONAL_WHITESPACE_OR_COMMENTS ({WHITESPACE}|{MULTI_LINE_COMMENT}|{SINGLE_LINE_ RETURN_TOKEN(T_COALESCE_EQUAL); } +"|>" { + RETURN_TOKEN(T_PIPE); +} + "||" { RETURN_TOKEN(T_BOOLEAN_OR); } diff --git a/ext/tokenizer/tokenizer_data.c b/ext/tokenizer/tokenizer_data.c index a1e131032bcfb..0900c51d3d95a 100644 --- a/ext/tokenizer/tokenizer_data.c +++ b/ext/tokenizer/tokenizer_data.c @@ -173,6 +173,7 @@ char *get_token_type_name(int token_type) case T_COALESCE: return "T_COALESCE"; case T_POW: return "T_POW"; case T_POW_EQUAL: return "T_POW_EQUAL"; + case T_PIPE: return "T_PIPE"; case T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG: return "T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG"; case T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG: return "T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG"; case T_BAD_CHARACTER: return "T_BAD_CHARACTER"; diff --git a/ext/tokenizer/tokenizer_data.stub.php b/ext/tokenizer/tokenizer_data.stub.php index c1e1fd254dfaa..57c8edad8acb6 100644 --- a/ext/tokenizer/tokenizer_data.stub.php +++ b/ext/tokenizer/tokenizer_data.stub.php @@ -742,6 +742,11 @@ * @cvalue T_POW_EQUAL */ const T_POW_EQUAL = UNKNOWN; +/** + * @var int + * @cvalue T_PIPE + */ +const T_PIPE = UNKNOWN; /** * @var int * @cvalue T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG diff --git a/ext/tokenizer/tokenizer_data_arginfo.h b/ext/tokenizer/tokenizer_data_arginfo.h index 9c488d19f1890..3a3cdaa468133 100644 --- a/ext/tokenizer/tokenizer_data_arginfo.h +++ b/ext/tokenizer/tokenizer_data_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 19d25d22098f46283b517352cbb302db962b50fd */ + * Stub hash: c5235344b7c651d27c2c33c90696a418a9c96837 */ static void register_tokenizer_data_symbols(int module_number) { @@ -151,6 +151,7 @@ static void register_tokenizer_data_symbols(int module_number) REGISTER_LONG_CONSTANT("T_COALESCE", T_COALESCE, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("T_POW", T_POW, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("T_POW_EQUAL", T_POW_EQUAL, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("T_PIPE", T_PIPE, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG", T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG", T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("T_BAD_CHARACTER", T_BAD_CHARACTER, CONST_PERSISTENT); From 7d24cce78abed57a9be9771dd4bc78d3f4cec3e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Tue, 10 Jun 2025 10:07:11 +0200 Subject: [PATCH 066/682] Update Lexbor Cherry-pick https://github.com/lexbor/lexbor/commit/b2dbadcf16ce08588fe7a0f624d64c190b1e83a5 Adding support for IDNA URL serialization. --- ext/lexbor/lexbor/url/url.c | 27 +++++++++++++++++++++++---- ext/lexbor/lexbor/url/url.h | 4 ++++ 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/ext/lexbor/lexbor/url/url.c b/ext/lexbor/lexbor/url/url.c index dcea861f7cb66..bbb3b5bbd3cb7 100644 --- a/ext/lexbor/lexbor/url/url.c +++ b/ext/lexbor/lexbor/url/url.c @@ -4442,9 +4442,9 @@ lxb_url_api_hash_set(lxb_url_t *url, lxb_url_parser_t *parser, return status; } -lxb_status_t -lxb_url_serialize(const lxb_url_t *url, lexbor_serialize_cb_f cb, void *ctx, - bool exclude_fragment) +static lxb_status_t +lxb_url_serialize_body(lxb_unicode_idna_t *idna, const lxb_url_t *url, lexbor_serialize_cb_f cb, + void *ctx, bool exclude_fragment) { lxb_status_t status; const lexbor_str_t *str; @@ -4484,7 +4484,12 @@ lxb_url_serialize(const lxb_url_t *url, lexbor_serialize_cb_f cb, void *ctx, lexbor_serialize_write(cb, at_str.data, at_str.length, ctx, status); } - status = lxb_url_serialize_host(&url->host, cb, ctx); + if (idna != NULL) { + status = lxb_url_serialize_host_unicode(idna, &url->host, cb, ctx); + } else { + status = lxb_url_serialize_host(&url->host, cb, ctx); + } + if (status != LXB_STATUS_OK) { return status; } @@ -4529,6 +4534,20 @@ lxb_url_serialize(const lxb_url_t *url, lexbor_serialize_cb_f cb, void *ctx, return LXB_STATUS_OK; } +lxb_status_t +lxb_url_serialize(const lxb_url_t *url, lexbor_serialize_cb_f cb, void *ctx, + bool exclude_fragment) +{ + return lxb_url_serialize_body(NULL, url, cb, ctx, exclude_fragment); +} + +lxb_status_t +lxb_url_serialize_idna(lxb_unicode_idna_t *idna, const lxb_url_t *url, lexbor_serialize_cb_f cb, + void *ctx, bool exclude_fragment) +{ + return lxb_url_serialize_body(idna, url, cb, ctx, exclude_fragment); +} + lxb_status_t lxb_url_serialize_scheme(const lxb_url_t *url, lexbor_serialize_cb_f cb, void *ctx) diff --git a/ext/lexbor/lexbor/url/url.h b/ext/lexbor/lexbor/url/url.h index 2bf2d7c3f5918..58952a0f910b6 100644 --- a/ext/lexbor/lexbor/url/url.h +++ b/ext/lexbor/lexbor/url/url.h @@ -403,6 +403,10 @@ LXB_API lxb_status_t lxb_url_serialize(const lxb_url_t *url, lexbor_serialize_cb_f cb, void *ctx, bool exclude_fragment); +LXB_API lxb_status_t +lxb_url_serialize_idna(lxb_unicode_idna_t *idna, const lxb_url_t *url, lexbor_serialize_cb_f cb, + void *ctx, bool exclude_fragment); + LXB_API lxb_status_t lxb_url_serialize_scheme(const lxb_url_t *url, lexbor_serialize_cb_f cb, void *ctx); From 3399235bec275516f6b87714087526d36d6f6976 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Tue, 10 Jun 2025 10:18:22 +0200 Subject: [PATCH 067/682] Add Uri\WhatWg classes to ext/uri (#18672) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Relates to #14461 and https://wiki.php.net/rfc/url_parsing_api Co-authored-by: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Co-authored-by: Tim Düsterhus --- UPGRADING.INTERNALS | 3 + Zend/zend_exceptions.c | 61 ++-- Zend/zend_exceptions.h | 2 + Zend/zend_string.h | 2 + build/gen_stub.php | 2 + ext/uri/config.m4 | 4 +- ext/uri/config.w32 | 4 +- ext/uri/php_lexbor.c | 639 ++++++++++++++++++++++++++++++++++++++ ext/uri/php_lexbor.h | 30 ++ ext/uri/php_uri.c | 614 +++++++++++++++++++++++++++++++++++- ext/uri/php_uri.h | 3 + ext/uri/php_uri.stub.php | 110 +++++++ ext/uri/php_uri_arginfo.h | 290 ++++++++++++++++- ext/uri/php_uri_common.c | 169 ++++++++++ ext/uri/php_uri_common.h | 138 ++++++++ ext/uri/tests/003.phpt | 32 ++ ext/uri/tests/004.phpt | 25 ++ ext/uri/tests/005.phpt | 38 +++ ext/uri/tests/006.phpt | 30 ++ ext/uri/tests/007.phpt | 63 ++++ ext/uri/tests/008.phpt | 34 ++ ext/uri/tests/009.phpt | 29 ++ ext/uri/tests/010.phpt | 48 +++ ext/uri/tests/011.phpt | 22 ++ ext/uri/tests/012.phpt | 49 +++ ext/uri/tests/013.phpt | 87 ++++++ ext/uri/tests/014.phpt | 12 + ext/uri/tests/015.phpt | 18 ++ ext/uri/tests/018.phpt | 50 +++ ext/uri/tests/019.phpt | 55 ++++ ext/uri/tests/022.phpt | 14 + ext/uri/tests/023.phpt | 31 ++ ext/uri/tests/024.phpt | 29 ++ ext/uri/tests/025.phpt | 29 ++ ext/uri/tests/026.phpt | 67 ++++ ext/uri/tests/027.phpt | 36 +++ ext/uri/tests/028.phpt | 37 +++ ext/uri/tests/029.phpt | 40 +++ ext/uri/tests/030.phpt | 31 ++ ext/uri/tests/031.phpt | 98 ++++++ ext/uri/tests/032.phpt | 13 + ext/uri/tests/033.phpt | 15 + ext/uri/tests/034.phpt | 14 + ext/uri/tests/035.phpt | 24 ++ ext/uri/tests/036.phpt | 24 ++ ext/uri/tests/038.phpt | 26 ++ ext/uri/tests/039.phpt | 34 ++ ext/uri/tests/040.phpt | 32 ++ ext/uri/tests/041.phpt | 26 ++ ext/uri/tests/042.phpt | 29 ++ ext/uri/tests/043.phpt | 71 +++++ ext/uri/tests/045.phpt | 16 + ext/uri/tests/046.phpt | 30 ++ ext/uri/tests/047.phpt | 27 ++ ext/uri/tests/049.phpt | 13 + ext/uri/tests/050.phpt | 16 + ext/uri/tests/051.phpt | 35 +++ ext/uri/tests/052.phpt | 28 ++ ext/uri/tests/053.phpt | 63 ++++ 59 files changed, 3565 insertions(+), 46 deletions(-) create mode 100644 ext/uri/php_lexbor.c create mode 100644 ext/uri/php_lexbor.h create mode 100644 ext/uri/php_uri_common.c create mode 100644 ext/uri/php_uri_common.h create mode 100644 ext/uri/tests/003.phpt create mode 100644 ext/uri/tests/004.phpt create mode 100644 ext/uri/tests/005.phpt create mode 100644 ext/uri/tests/006.phpt create mode 100644 ext/uri/tests/007.phpt create mode 100644 ext/uri/tests/008.phpt create mode 100644 ext/uri/tests/009.phpt create mode 100644 ext/uri/tests/010.phpt create mode 100644 ext/uri/tests/011.phpt create mode 100644 ext/uri/tests/012.phpt create mode 100644 ext/uri/tests/013.phpt create mode 100644 ext/uri/tests/014.phpt create mode 100644 ext/uri/tests/015.phpt create mode 100644 ext/uri/tests/018.phpt create mode 100644 ext/uri/tests/019.phpt create mode 100644 ext/uri/tests/022.phpt create mode 100644 ext/uri/tests/023.phpt create mode 100644 ext/uri/tests/024.phpt create mode 100644 ext/uri/tests/025.phpt create mode 100644 ext/uri/tests/026.phpt create mode 100644 ext/uri/tests/027.phpt create mode 100644 ext/uri/tests/028.phpt create mode 100644 ext/uri/tests/029.phpt create mode 100644 ext/uri/tests/030.phpt create mode 100644 ext/uri/tests/031.phpt create mode 100644 ext/uri/tests/032.phpt create mode 100644 ext/uri/tests/033.phpt create mode 100644 ext/uri/tests/034.phpt create mode 100644 ext/uri/tests/035.phpt create mode 100644 ext/uri/tests/036.phpt create mode 100644 ext/uri/tests/038.phpt create mode 100644 ext/uri/tests/039.phpt create mode 100644 ext/uri/tests/040.phpt create mode 100644 ext/uri/tests/041.phpt create mode 100644 ext/uri/tests/042.phpt create mode 100644 ext/uri/tests/043.phpt create mode 100644 ext/uri/tests/045.phpt create mode 100644 ext/uri/tests/046.phpt create mode 100644 ext/uri/tests/047.phpt create mode 100644 ext/uri/tests/049.phpt create mode 100644 ext/uri/tests/050.phpt create mode 100644 ext/uri/tests/051.phpt create mode 100644 ext/uri/tests/052.phpt create mode 100644 ext/uri/tests/053.phpt diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS index 574d5f0827ff7..e4cf9e9c94b0d 100644 --- a/UPGRADING.INTERNALS +++ b/UPGRADING.INTERNALS @@ -34,6 +34,9 @@ PHP 8.5 INTERNALS UPGRADE NOTES . Added ZEND_NONSTRING attribute macro for character arrays that do not represent strings. This allows to silence the GCC 15.x `-Wunterminated-string-initialization` warning. + . Added the zend_update_exception_properties() function for instantiating + Exception child classes. It updates the $message, $code, and $previous + properties. ======================== 2. Build system changes diff --git a/Zend/zend_exceptions.c b/Zend/zend_exceptions.c index 7777c5fa62e48..ab9c815718a0d 100644 --- a/Zend/zend_exceptions.c +++ b/Zend/zend_exceptions.c @@ -329,24 +329,15 @@ ZEND_COLD ZEND_METHOD(Exception, __clone) } /* }}} */ -/* {{{ Exception constructor */ -ZEND_METHOD(Exception, __construct) +ZEND_API zend_result zend_update_exception_properties(INTERNAL_FUNCTION_PARAMETERS, zend_string *message, zend_long code, zval *previous) { - zend_string *message = NULL; - zend_long code = 0; - zval tmp, *object, *previous = NULL; - - object = ZEND_THIS; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "|SlO!", &message, &code, &previous, zend_ce_throwable) == FAILURE) { - RETURN_THROWS(); - } + zval tmp, *object = ZEND_THIS; if (message) { ZVAL_STR_COPY(&tmp, message); zend_update_property_num_checked(NULL, Z_OBJ_P(object), ZEND_EXCEPTION_MESSAGE_OFF, ZSTR_KNOWN(ZEND_STR_MESSAGE), &tmp); if (UNEXPECTED(EG(exception))) { - RETURN_THROWS(); + return FAILURE; } } @@ -354,7 +345,7 @@ ZEND_METHOD(Exception, __construct) ZVAL_LONG(&tmp, code); zend_update_property_num_checked(NULL, Z_OBJ_P(object), ZEND_EXCEPTION_CODE_OFF, ZSTR_KNOWN(ZEND_STR_CODE), &tmp); if (UNEXPECTED(EG(exception))) { - RETURN_THROWS(); + return FAILURE; } } @@ -362,9 +353,27 @@ ZEND_METHOD(Exception, __construct) Z_ADDREF_P(previous); zend_update_property_num_checked(zend_ce_exception, Z_OBJ_P(object), ZEND_EXCEPTION_PREVIOUS_OFF, ZSTR_KNOWN(ZEND_STR_PREVIOUS), previous); if (UNEXPECTED(EG(exception))) { - RETURN_THROWS(); + return FAILURE; } } + + return SUCCESS; +} + +/* {{{ Exception constructor */ +ZEND_METHOD(Exception, __construct) +{ + zend_string *message = NULL; + zend_long code = 0; + zval *previous = NULL; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "|SlO!", &message, &code, &previous, zend_ce_throwable) == FAILURE) { + RETURN_THROWS(); + } + + if (zend_update_exception_properties(INTERNAL_FUNCTION_PARAM_PASSTHRU, message, code, previous) == FAILURE) { + RETURN_THROWS(); + } } /* }}} */ @@ -401,28 +410,8 @@ ZEND_METHOD(ErrorException, __construct) object = ZEND_THIS; - if (message) { - ZVAL_STR_COPY(&tmp, message); - zend_update_property_num_checked(NULL, Z_OBJ_P(object), ZEND_EXCEPTION_MESSAGE_OFF, ZSTR_KNOWN(ZEND_STR_MESSAGE), &tmp); - if (UNEXPECTED(EG(exception))) { - RETURN_THROWS(); - } - } - - if (code) { - ZVAL_LONG(&tmp, code); - zend_update_property_num_checked(NULL, Z_OBJ_P(object), ZEND_EXCEPTION_CODE_OFF, ZSTR_KNOWN(ZEND_STR_CODE), &tmp); - if (UNEXPECTED(EG(exception))) { - RETURN_THROWS(); - } - } - - if (previous) { - Z_ADDREF_P(previous); - zend_update_property_num_checked(zend_ce_exception, Z_OBJ_P(object), ZEND_EXCEPTION_PREVIOUS_OFF, ZSTR_KNOWN(ZEND_STR_PREVIOUS), previous); - if (UNEXPECTED(EG(exception))) { - RETURN_THROWS(); - } + if (zend_update_exception_properties(INTERNAL_FUNCTION_PARAM_PASSTHRU, message, code, previous) == FAILURE) { + RETURN_THROWS(); } ZVAL_LONG(&tmp, severity); diff --git a/Zend/zend_exceptions.h b/Zend/zend_exceptions.h index d0138021d1ea3..86dc379cce871 100644 --- a/Zend/zend_exceptions.h +++ b/Zend/zend_exceptions.h @@ -69,6 +69,8 @@ ZEND_API zend_object *zend_throw_error_exception(zend_class_entry *exception_ce, extern ZEND_API void (*zend_throw_exception_hook)(zend_object *ex); +ZEND_API zend_result zend_update_exception_properties(INTERNAL_FUNCTION_PARAMETERS, zend_string *message, zend_long code, zval *previous); + /* show an exception using zend_error(severity,...), severity should be E_ERROR */ ZEND_API ZEND_COLD zend_result zend_exception_error(zend_object *exception, int severity); ZEND_NORETURN void zend_exception_uncaught_error(const char *prefix, ...) ZEND_ATTRIBUTE_FORMAT(printf, 1, 2); diff --git a/Zend/zend_string.h b/Zend/zend_string.h index e9e2b947a6c91..0b2a484016ec3 100644 --- a/Zend/zend_string.h +++ b/Zend/zend_string.h @@ -597,7 +597,9 @@ EMPTY_SWITCH_DEFAULT_CASE() _(ZEND_STR_HOST, "host") \ _(ZEND_STR_PORT, "port") \ _(ZEND_STR_USER, "user") \ + _(ZEND_STR_USERNAME, "username") \ _(ZEND_STR_PASS, "pass") \ + _(ZEND_STR_PASSWORD, "password") \ _(ZEND_STR_PATH, "path") \ _(ZEND_STR_QUERY, "query") \ _(ZEND_STR_FRAGMENT, "fragment") \ diff --git a/build/gen_stub.php b/build/gen_stub.php index 13ef9e60f334d..0e87cdd9a0b40 100755 --- a/build/gen_stub.php +++ b/build/gen_stub.php @@ -3055,6 +3055,8 @@ class PropertyInfo extends VariableLike private const PHP_85_KNOWN = [ "self" => "ZEND_STR_SELF", "parent" => "ZEND_STR_PARENT", + "username" => "ZEND_STR_USERNAME", + "password" => "ZEND_STR_PASSWORD", ]; /** diff --git a/ext/uri/config.m4 b/ext/uri/config.m4 index f29bbe58bd32e..08dc044d8d29f 100644 --- a/ext/uri/config.m4 +++ b/ext/uri/config.m4 @@ -2,7 +2,9 @@ dnl Configure options dnl PHP_INSTALL_HEADERS([ext/uri], m4_normalize([ + php_lexbor.h php_uri.h + php_uri_common.h ])) AC_DEFINE([URI_ENABLE_ANSI], [1], [Define to 1 for enabling ANSI support of uriparser.]) @@ -15,6 +17,6 @@ $URIPARSER_DIR/src/UriMemory.c $URIPARSER_DIR/src/UriNormalize.c $URIPARSER_DIR/ $URIPARSER_DIR/src/UriParse.c $URIPARSER_DIR/src/UriParseBase.c $URIPARSER_DIR/src/UriQuery.c \ $URIPARSER_DIR/src/UriRecompose.c $URIPARSER_DIR/src/UriResolve.c $URIPARSER_DIR/src/UriShorten.c" -PHP_NEW_EXTENSION(uri, [php_uri.c $URIPARSER_SOURCES], [no],,[-I$ext_srcdir/$URIPARSER_DIR/include -DURI_STATIC_BUILD -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1]) +PHP_NEW_EXTENSION(uri, [php_lexbor.c php_uri.c php_uri_common.c $URIPARSER_SOURCES], [no],,[-I$ext_srcdir/$URIPARSER_DIR/include -DURI_STATIC_BUILD -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1]) PHP_ADD_EXTENSION_DEP(uri, lexbor) PHP_ADD_BUILD_DIR($ext_builddir/$URIPARSER_DIR/src $ext_builddir/$URIPARSER_DIR/include) diff --git a/ext/uri/config.w32 b/ext/uri/config.w32 index 962f7bee0660e..9c6af0cc5fa7b 100644 --- a/ext/uri/config.w32 +++ b/ext/uri/config.w32 @@ -1,4 +1,4 @@ -EXTENSION("uri", "php_uri.c", false /* never shared */, "/I ext/lexbor /I ext/uri/uriparser/include /DZEND_ENABLE_STATIC_TSRMLS_CACHE=1"); +EXTENSION("uri", "php_lexbor.c php_uri.c php_uri_common.c", false /* never shared */, "/I ext/lexbor /I ext/uri/uriparser/include /DZEND_ENABLE_STATIC_TSRMLS_CACHE=1"); AC_DEFINE("URI_ENABLE_ANSI", 1, "Define to 1 for enabling ANSI support of uriparser.") AC_DEFINE("URI_NO_UNICODE", 1, "Define to 1 for disabling unicode support of uriparser.") @@ -6,4 +6,4 @@ ADD_FLAG("CFLAGS_URI", "/D URI_STATIC_BUILD"); ADD_EXTENSION_DEP('uri', 'lexbor'); ADD_SOURCES("ext/uri/uriparser/src", "UriCommon.c UriCompare.c UriEscape.c UriFile.c UriIp4.c UriIp4Base.c UriMemory.c UriNormalize.c UriNormalizeBase.c UriParse.c UriParseBase.c UriQuery.c UriRecompose.c UriShorten.c", "uri"); -PHP_INSTALL_HEADERS("ext/uri", "php_uri.h uriparser/src uriparser/include"); +PHP_INSTALL_HEADERS("ext/uri", "php_lexbor.h php_uri.h php_uri_common.h uriparser/src uriparser/include"); diff --git a/ext/uri/php_lexbor.c b/ext/uri/php_lexbor.c new file mode 100644 index 0000000000000..82f3919bb6a97 --- /dev/null +++ b/ext/uri/php_lexbor.c @@ -0,0 +1,639 @@ +/* + +----------------------------------------------------------------------+ + | Copyright (c) The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | https://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Máté Kocsis | + +----------------------------------------------------------------------+ +*/ + +#include "php.h" +#include "php_lexbor.h" +#include "php_uri_common.h" +#include "Zend/zend_enum.h" +#include "Zend/zend_smart_str.h" +#include "Zend/zend_exceptions.h" +#ifdef HAVE_ARPA_INET_H +#include +#endif + +ZEND_TLS lxb_url_parser_t lexbor_parser; +ZEND_TLS unsigned short int lexbor_urls; + +#define LEXBOR_MAX_URL_COUNT 500 +#define LEXBOR_MRAW_BYTE_SIZE 8192 + +static zend_always_inline void zval_string_or_null_to_lexbor_str(zval *value, lexbor_str_t *lexbor_str) +{ + if (Z_TYPE_P(value) == IS_STRING && Z_STRLEN_P(value) > 0) { + lexbor_str->data = (lxb_char_t *) Z_STRVAL_P(value); + lexbor_str->length = Z_STRLEN_P(value); + } else { + ZEND_ASSERT(Z_ISNULL_P(value) || (Z_TYPE_P(value) == IS_STRING && Z_STRLEN_P(value) == 0)); + lexbor_str->data = (lxb_char_t *) ""; + lexbor_str->length = 0; + } +} + +static zend_always_inline void zval_long_or_null_to_lexbor_str(zval *value, lexbor_str_t *lexbor_str) +{ + if (Z_TYPE_P(value) == IS_LONG) { + ZVAL_STR(value, zend_long_to_str(Z_LVAL_P(value))); + lexbor_str_init_append(lexbor_str, lexbor_parser.mraw, (const lxb_char_t *) Z_STRVAL_P(value), Z_STRLEN_P(value)); + zval_ptr_dtor_str(value); + } else { + ZEND_ASSERT(Z_ISNULL_P(value)); + lexbor_str->data = (lxb_char_t *) ""; + lexbor_str->length = 0; + } +} + +static void lexbor_cleanup_parser(void) +{ + if (++lexbor_urls % LEXBOR_MAX_URL_COUNT == 0) { + lexbor_mraw_clean(lexbor_parser.mraw); + lexbor_urls = 0; + } + + lxb_url_parser_clean(&lexbor_parser); +} + +/** + * Creates a Uri\WhatWg\UrlValidationError class by mapping error codes listed in + * https://url.spec.whatwg.org/#writing to a Uri\WhatWg\UrlValidationErrorType enum. + * The result is passed by reference to the errors parameter. + * + * When errors is NULL, the caller is not interested in the additional error information, + * so the function does nothing. + */ +static void fill_errors(zval *errors) +{ + if (errors == NULL) { + return; + } + + ZEND_ASSERT(Z_ISUNDEF_P(errors)); + + array_init(errors); + + if (lexbor_parser.log == NULL) { + return; + } + + lexbor_plog_entry_t *lxb_error; + while ((lxb_error = lexbor_array_obj_pop(&lexbor_parser.log->list)) != NULL) { + zval error; + object_init_ex(&error, uri_whatwg_url_validation_error_ce); + zend_update_property_string(uri_whatwg_url_validation_error_ce, Z_OBJ(error), ZEND_STRL("context"), (const char *) lxb_error->data); + + zend_string *error_str; + zval failure; + switch (lxb_error->id) { + case LXB_URL_ERROR_TYPE_DOMAIN_TO_ASCII: + error_str = ZSTR_INIT_LITERAL("DomainToAscii", false); + ZVAL_TRUE(&failure); + break; + case LXB_URL_ERROR_TYPE_DOMAIN_TO_UNICODE: + error_str = ZSTR_INIT_LITERAL("DomainToUnicode", false); + ZVAL_FALSE(&failure); + break; + case LXB_URL_ERROR_TYPE_DOMAIN_INVALID_CODE_POINT: + error_str = ZSTR_INIT_LITERAL("DomainInvalidCodePoint", false); + ZVAL_TRUE(&failure); + break; + case LXB_URL_ERROR_TYPE_HOST_INVALID_CODE_POINT: + error_str = ZSTR_INIT_LITERAL("HostInvalidCodePoint", false); + ZVAL_TRUE(&failure); + break; + case LXB_URL_ERROR_TYPE_IPV4_EMPTY_PART: + error_str = ZSTR_INIT_LITERAL("Ipv4EmptyPart", false); + ZVAL_FALSE(&failure); + break; + case LXB_URL_ERROR_TYPE_IPV4_TOO_MANY_PARTS: + error_str = ZSTR_INIT_LITERAL("Ipv4TooManyParts", false); + ZVAL_TRUE(&failure); + break; + case LXB_URL_ERROR_TYPE_IPV4_NON_NUMERIC_PART: + error_str = ZSTR_INIT_LITERAL("Ipv4NonNumericPart", false); + ZVAL_TRUE(&failure); + break; + case LXB_URL_ERROR_TYPE_IPV4_NON_DECIMAL_PART: + error_str = ZSTR_INIT_LITERAL("Ipv4NonDecimalPart", false); + ZVAL_FALSE(&failure); + break; + case LXB_URL_ERROR_TYPE_IPV4_OUT_OF_RANGE_PART: + error_str = ZSTR_INIT_LITERAL("Ipv4OutOfRangePart", false); + ZVAL_TRUE(&failure); + break; + case LXB_URL_ERROR_TYPE_IPV6_UNCLOSED: + error_str = ZSTR_INIT_LITERAL("Ipv6Unclosed", false); + ZVAL_TRUE(&failure); + break; + case LXB_URL_ERROR_TYPE_IPV6_INVALID_COMPRESSION: + error_str = ZSTR_INIT_LITERAL("Ipv6InvalidCompression", false); + ZVAL_TRUE(&failure); + break; + case LXB_URL_ERROR_TYPE_IPV6_TOO_MANY_PIECES: + error_str = ZSTR_INIT_LITERAL("Ipv6TooManyPieces", false); + ZVAL_TRUE(&failure); + break; + case LXB_URL_ERROR_TYPE_IPV6_MULTIPLE_COMPRESSION: + error_str = ZSTR_INIT_LITERAL("Ipv6MultipleCompression", false); + ZVAL_TRUE(&failure); + break; + case LXB_URL_ERROR_TYPE_IPV6_INVALID_CODE_POINT: + error_str = ZSTR_INIT_LITERAL("Ipv6InvalidCodePoint", false); + ZVAL_TRUE(&failure); + break; + case LXB_URL_ERROR_TYPE_IPV6_TOO_FEW_PIECES: + error_str = ZSTR_INIT_LITERAL("Ipv6TooFewPieces", false); + ZVAL_TRUE(&failure); + break; + case LXB_URL_ERROR_TYPE_IPV4_IN_IPV6_TOO_MANY_PIECES: + error_str = ZSTR_INIT_LITERAL("Ipv4InIpv6TooManyPieces", false); + ZVAL_TRUE(&failure); + break; + case LXB_URL_ERROR_TYPE_IPV4_IN_IPV6_INVALID_CODE_POINT: + error_str = ZSTR_INIT_LITERAL("Ipv4InIpv6InvalidCodePoint", false); + ZVAL_TRUE(&failure); + break; + case LXB_URL_ERROR_TYPE_IPV4_IN_IPV6_OUT_OF_RANGE_PART: + error_str = ZSTR_INIT_LITERAL("Ipv4InIpv6OutOfRangePart", false); + ZVAL_TRUE(&failure); + break; + case LXB_URL_ERROR_TYPE_IPV4_IN_IPV6_TOO_FEW_PARTS: + error_str = ZSTR_INIT_LITERAL("Ipv4InIpv6TooFewParts", false); + ZVAL_TRUE(&failure); + break; + case LXB_URL_ERROR_TYPE_INVALID_URL_UNIT: + error_str = ZSTR_INIT_LITERAL("InvalidUrlUnit", false); + ZVAL_FALSE(&failure); + break; + case LXB_URL_ERROR_TYPE_SPECIAL_SCHEME_MISSING_FOLLOWING_SOLIDUS: + error_str = ZSTR_INIT_LITERAL("SpecialSchemeMissingFollowingSolidus", false); + ZVAL_FALSE(&failure); + break; + case LXB_URL_ERROR_TYPE_MISSING_SCHEME_NON_RELATIVE_URL: + error_str = ZSTR_INIT_LITERAL("MissingSchemeNonRelativeUrl", false); + ZVAL_TRUE(&failure); + break; + case LXB_URL_ERROR_TYPE_INVALID_REVERSE_SOLIDUS: + error_str = ZSTR_INIT_LITERAL("InvalidReverseSoldius", false); + ZVAL_FALSE(&failure); + break; + case LXB_URL_ERROR_TYPE_INVALID_CREDENTIALS: + error_str = ZSTR_INIT_LITERAL("InvalidCredentials", false); + ZVAL_FALSE(&failure); + break; + case LXB_URL_ERROR_TYPE_HOST_MISSING: + error_str = ZSTR_INIT_LITERAL("HostMissing", false); + ZVAL_TRUE(&failure); + break; + case LXB_URL_ERROR_TYPE_PORT_OUT_OF_RANGE: + error_str = ZSTR_INIT_LITERAL("PortOutOfRange", false); + ZVAL_TRUE(&failure); + break; + case LXB_URL_ERROR_TYPE_PORT_INVALID: + error_str = ZSTR_INIT_LITERAL("PortInvalid", false); + ZVAL_TRUE(&failure); + break; + case LXB_URL_ERROR_TYPE_FILE_INVALID_WINDOWS_DRIVE_LETTER: + error_str = ZSTR_INIT_LITERAL("FileInvalidWindowsDriveLetter", false); + ZVAL_FALSE(&failure); + break; + case LXB_URL_ERROR_TYPE_FILE_INVALID_WINDOWS_DRIVE_LETTER_HOST: + error_str = ZSTR_INIT_LITERAL("FileInvalidWindowsDriveLetterHost", false); + ZVAL_FALSE(&failure); + break; + EMPTY_SWITCH_DEFAULT_CASE() + } + + zval error_type; + zend_enum_new(&error_type, uri_whatwg_url_validation_error_type_ce, error_str, NULL); + zend_update_property_ex(uri_whatwg_url_validation_error_ce, Z_OBJ(error), ZSTR_KNOWN(ZEND_STR_TYPE), &error_type); + zend_string_release_ex(error_str, false); + zval_ptr_dtor(&error_type); + + zend_update_property(uri_whatwg_url_validation_error_ce, Z_OBJ(error), ZEND_STRL("failure"), &failure); + + add_next_index_zval(errors, &error); + } +} + +static void throw_invalid_url_exception(zval *errors) +{ + ZEND_ASSERT(errors != NULL && Z_TYPE_P(errors) == IS_ARRAY); + + zval exception; + + object_init_ex(&exception, uri_whatwg_invalid_url_exception_ce); + + zval value; + ZVAL_STRING(&value, "URL parsing failed"); + zend_update_property_ex(uri_whatwg_invalid_url_exception_ce, Z_OBJ(exception), ZSTR_KNOWN(ZEND_STR_MESSAGE), &value); + zval_ptr_dtor_str(&value); + + zend_update_property(uri_whatwg_invalid_url_exception_ce, Z_OBJ(exception), ZEND_STRL("errors"), errors); + + zend_throw_exception_object(&exception); +} + +static void throw_invalid_url_exception_during_write(zval *errors) +{ + fill_errors(errors); + throw_invalid_url_exception(errors); +} + +static lxb_status_t lexbor_serialize_callback(const lxb_char_t *data, size_t length, void *ctx) +{ + smart_str *uri_str = ctx; + + if (data != NULL && length > 0) { + smart_str_appendl(uri_str, (const char *) data, length); + } + + return LXB_STATUS_OK; +} + +static zend_result lexbor_read_scheme(const struct uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval) +{ + lxb_url_t *lexbor_uri = internal_uri->uri; + + ZEND_ASSERT(lexbor_uri->scheme.type != LXB_URL_SCHEMEL_TYPE__UNDEF); + + ZVAL_STRINGL(retval, (const char *) lexbor_uri->scheme.name.data, lexbor_uri->scheme.name.length); + + return SUCCESS; +} + +static zend_result lexbor_write_scheme(struct uri_internal_t *internal_uri, zval *value, zval *errors) +{ + lxb_url_t *lexbor_uri = internal_uri->uri; + lexbor_str_t str = {0}; + + zval_string_or_null_to_lexbor_str(value, &str); + + if (lxb_url_api_protocol_set(lexbor_uri, &lexbor_parser, str.data, str.length) != LXB_STATUS_OK) { + throw_invalid_url_exception_during_write(errors); + + return FAILURE; + } + + return SUCCESS; +} + +static zend_result lexbor_read_username(const struct uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval) +{ + lxb_url_t *lexbor_uri = internal_uri->uri; + + if (lexbor_uri->username.length) { + ZVAL_STRINGL(retval, (const char *) lexbor_uri->username.data, lexbor_uri->username.length); + } else { + ZVAL_NULL(retval); + } + + return SUCCESS; +} + +static zend_result lexbor_write_username(uri_internal_t *internal_uri, zval *value, zval *errors) +{ + lxb_url_t *lexbor_uri = internal_uri->uri; + lexbor_str_t str = {0}; + + zval_string_or_null_to_lexbor_str(value, &str); + + if (lxb_url_api_username_set(lexbor_uri, str.data, str.length) != LXB_STATUS_OK) { + throw_invalid_url_exception_during_write(errors); + + return FAILURE; + } + + return SUCCESS; +} + +static zend_result lexbor_read_password(const struct uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval) +{ + lxb_url_t *lexbor_uri = internal_uri->uri; + + if (lexbor_uri->password.length > 0) { + ZVAL_STRINGL(retval, (const char *) lexbor_uri->password.data, lexbor_uri->password.length); + } else { + ZVAL_NULL(retval); + } + + return SUCCESS; +} + +static zend_result lexbor_write_password(struct uri_internal_t *internal_uri, zval *value, zval *errors) +{ + lxb_url_t *lexbor_uri = internal_uri->uri; + lexbor_str_t str = {0}; + + zval_string_or_null_to_lexbor_str(value, &str); + + if (lxb_url_api_password_set(lexbor_uri, str.data, str.length) != LXB_STATUS_OK) { + throw_invalid_url_exception_during_write(errors); + + return FAILURE; + } + + return SUCCESS; +} + +static zend_result init_idna(void) +{ + if (lexbor_parser.idna != NULL) { + return SUCCESS; + } + + lexbor_parser.idna = lxb_unicode_idna_create(); + + return lxb_unicode_idna_init(lexbor_parser.idna) == LXB_STATUS_OK ? SUCCESS : FAILURE; +} + +static zend_result lexbor_read_host(const struct uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval) +{ + lxb_url_t *lexbor_uri = internal_uri->uri; + + if (lexbor_uri->host.type == LXB_URL_HOST_TYPE_IPV4) { + smart_str host_str = {0}; + + lxb_url_serialize_host_ipv4(lexbor_uri->host.u.ipv4, lexbor_serialize_callback, &host_str); + + ZVAL_NEW_STR(retval, smart_str_extract(&host_str)); + } else if (lexbor_uri->host.type == LXB_URL_HOST_TYPE_IPV6) { + smart_str host_str = {0}; + + smart_str_appendc(&host_str, '['); + lxb_url_serialize_host_ipv6(lexbor_uri->host.u.ipv6, lexbor_serialize_callback, &host_str); + smart_str_appendc(&host_str, ']'); + + ZVAL_NEW_STR(retval, smart_str_extract(&host_str)); + } else if (lexbor_uri->host.type != LXB_URL_HOST_TYPE_EMPTY && lexbor_uri->host.type != LXB_URL_HOST_TYPE__UNDEF) { + switch (read_mode) { + case URI_COMPONENT_READ_NORMALIZED_UNICODE: { + smart_str host_str = {0}; + if (init_idna() == FAILURE) { + return FAILURE; + } + lxb_url_serialize_host_unicode(lexbor_parser.idna, &lexbor_uri->host, lexbor_serialize_callback, &host_str); + lxb_unicode_idna_clean(lexbor_parser.idna); + + ZVAL_NEW_STR(retval, smart_str_extract(&host_str)); + break; + } + case URI_COMPONENT_READ_NORMALIZED_ASCII: + ZEND_FALLTHROUGH; + case URI_COMPONENT_READ_RAW: + ZVAL_STRINGL(retval, (const char *) lexbor_uri->host.u.domain.data, lexbor_uri->host.u.domain.length); + break; + EMPTY_SWITCH_DEFAULT_CASE() + } + } else { + ZVAL_NULL(retval); + } + + return SUCCESS; +} + +static zend_result lexbor_write_host(struct uri_internal_t *internal_uri, zval *value, zval *errors) +{ + lxb_url_t *lexbor_uri = internal_uri->uri; + lexbor_str_t str = {0}; + + zval_string_or_null_to_lexbor_str(value, &str); + + if (lxb_url_api_hostname_set(lexbor_uri, &lexbor_parser, str.data, str.length) != LXB_STATUS_OK) { + throw_invalid_url_exception_during_write(errors); + + return FAILURE; + } + + return SUCCESS; +} + +static zend_result lexbor_read_port(const struct uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval) +{ + lxb_url_t *lexbor_uri = internal_uri->uri; + + if (lexbor_uri->has_port) { + ZVAL_LONG(retval, lexbor_uri->port); + } else { + ZVAL_NULL(retval); + } + + return SUCCESS; +} + +static zend_result lexbor_write_port(struct uri_internal_t *internal_uri, zval *value, zval *errors) +{ + lxb_url_t *lexbor_uri = internal_uri->uri; + lexbor_str_t str = {0}; + + zval_long_or_null_to_lexbor_str(value, &str); + + if (lxb_url_api_port_set(lexbor_uri, &lexbor_parser, str.data, str.length) != LXB_STATUS_OK) { + throw_invalid_url_exception_during_write(errors); + + return FAILURE; + } + + return SUCCESS; +} + +static zend_result lexbor_read_path(const struct uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval) +{ + lxb_url_t *lexbor_uri = internal_uri->uri; + + if (lexbor_uri->path.str.length) { + ZVAL_STRINGL(retval, (const char *) lexbor_uri->path.str.data, lexbor_uri->path.str.length); + } else { + ZVAL_EMPTY_STRING(retval); + } + + return SUCCESS; +} + +static zend_result lexbor_write_path(struct uri_internal_t *internal_uri, zval *value, zval *errors) +{ + lxb_url_t *lexbor_uri = internal_uri->uri; + lexbor_str_t str = {0}; + + zval_string_or_null_to_lexbor_str(value, &str); + + if (lxb_url_api_pathname_set(lexbor_uri, &lexbor_parser, str.data, str.length) != LXB_STATUS_OK) { + throw_invalid_url_exception_during_write(errors); + + return FAILURE; + } + + return SUCCESS; +} + +static zend_result lexbor_read_query(const struct uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval) +{ + lxb_url_t *lexbor_uri = internal_uri->uri; + + if (lexbor_uri->query.length) { + ZVAL_STRINGL(retval, (const char *) lexbor_uri->query.data, lexbor_uri->query.length); + } else { + ZVAL_NULL(retval); + } + + return SUCCESS; +} + +static zend_result lexbor_write_query(struct uri_internal_t *internal_uri, zval *value, zval *errors) +{ + lxb_url_t *lexbor_uri = internal_uri->uri; + lexbor_str_t str = {0}; + + zval_string_or_null_to_lexbor_str(value, &str); + + if (lxb_url_api_search_set(lexbor_uri, &lexbor_parser, str.data, str.length) != LXB_STATUS_OK) { + throw_invalid_url_exception_during_write(errors); + + return FAILURE; + } + + return SUCCESS; +} + +static zend_result lexbor_read_fragment(const struct uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval) +{ + lxb_url_t *lexbor_uri = internal_uri->uri; + + if (lexbor_uri->fragment.length) { + ZVAL_STRINGL(retval, (const char *) lexbor_uri->fragment.data, lexbor_uri->fragment.length); + } else { + ZVAL_NULL(retval); + } + + return SUCCESS; +} + +static zend_result lexbor_write_fragment(struct uri_internal_t *internal_uri, zval *value, zval *errors) +{ + lxb_url_t *lexbor_uri = internal_uri->uri; + lexbor_str_t str = {0}; + + zval_string_or_null_to_lexbor_str(value, &str); + + if (lxb_url_api_hash_set(lexbor_uri, &lexbor_parser, str.data, str.length) != LXB_STATUS_OK) { + throw_invalid_url_exception_during_write(errors); + + return FAILURE; + } + + return SUCCESS; +} + +zend_result lexbor_request_init(void) +{ + lexbor_mraw_t *mraw = lexbor_mraw_create(); + lxb_status_t status = lexbor_mraw_init(mraw, LEXBOR_MRAW_BYTE_SIZE); + if (status != LXB_STATUS_OK) { + lexbor_mraw_destroy(mraw, true); + return FAILURE; + } + + status = lxb_url_parser_init(&lexbor_parser, mraw); + if (status != LXB_STATUS_OK) { + lxb_url_parser_destroy(&lexbor_parser, false); + lexbor_mraw_destroy(mraw, true); + return FAILURE; + } + + lexbor_urls = 0; + + return SUCCESS; +} + +void lexbor_request_shutdown(void) +{ + lxb_url_parser_memory_destroy(&lexbor_parser); + lxb_url_parser_destroy(&lexbor_parser, false); + + lexbor_urls = 0; +} + +lxb_url_t *lexbor_parse_uri_ex(const zend_string *uri_str, const lxb_url_t *lexbor_base_url, zval *errors, bool silent) +{ + lexbor_cleanup_parser(); + + lxb_url_t *url = lxb_url_parse(&lexbor_parser, lexbor_base_url, (unsigned char *) ZSTR_VAL(uri_str), ZSTR_LEN(uri_str)); + fill_errors(errors); + + if (url == NULL && !silent) { + throw_invalid_url_exception(errors); + } + + return url; +} + +static void *lexbor_parse_uri(const zend_string *uri_str, const void *base_url, zval *errors, bool silent) +{ + return lexbor_parse_uri_ex(uri_str, base_url, errors, silent); +} + +static void *lexbor_clone_uri(void *uri) +{ + lxb_url_t *lexbor_uri = (lxb_url_t *) uri; + + return lxb_url_clone(lexbor_parser.mraw, lexbor_uri); +} + +static zend_string *lexbor_uri_to_string(void *uri, uri_recomposition_mode_t recomposition_mode, bool exclude_fragment) +{ + lxb_url_t *lexbor_uri = (lxb_url_t *) uri; + smart_str uri_str = {0}; + + switch (recomposition_mode) { + case URI_RECOMPOSITION_RAW_UNICODE: + ZEND_FALLTHROUGH; + case URI_RECOMPOSITION_NORMALIZED_UNICODE: + if (init_idna() == FAILURE) { + return NULL; + } + lxb_url_serialize_idna(lexbor_parser.idna, lexbor_uri, lexbor_serialize_callback, &uri_str, exclude_fragment); + lxb_unicode_idna_clean(lexbor_parser.idna); + break; + case URI_RECOMPOSITION_RAW_ASCII: + ZEND_FALLTHROUGH; + case URI_RECOMPOSITION_NORMALIZED_ASCII: + lxb_url_serialize(lexbor_uri, lexbor_serialize_callback, &uri_str, exclude_fragment); + break; + EMPTY_SWITCH_DEFAULT_CASE() + } + + return smart_str_extract(&uri_str); +} + +static void lexbor_free_uri(void *uri) +{ +} + +const uri_handler_t lexbor_uri_handler = { + .name = URI_PARSER_WHATWG, + .parse_uri = lexbor_parse_uri, + .clone_uri = lexbor_clone_uri, + .uri_to_string = lexbor_uri_to_string, + .free_uri = lexbor_free_uri, + { + .scheme = {.read_func = lexbor_read_scheme, .write_func = lexbor_write_scheme}, + .username = {.read_func = lexbor_read_username, .write_func = lexbor_write_username}, + .password = {.read_func = lexbor_read_password, .write_func = lexbor_write_password}, + .host = {.read_func = lexbor_read_host, .write_func = lexbor_write_host}, + .port = {.read_func = lexbor_read_port, .write_func = lexbor_write_port}, + .path = {.read_func = lexbor_read_path, .write_func = lexbor_write_path}, + .query = {.read_func = lexbor_read_query, .write_func = lexbor_write_query}, + .fragment = {.read_func = lexbor_read_fragment, .write_func = lexbor_write_fragment}, + } +}; diff --git a/ext/uri/php_lexbor.h b/ext/uri/php_lexbor.h new file mode 100644 index 0000000000000..30d68b5cdf681 --- /dev/null +++ b/ext/uri/php_lexbor.h @@ -0,0 +1,30 @@ +/* + +----------------------------------------------------------------------+ + | Copyright (c) The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | https://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Máté Kocsis | + +----------------------------------------------------------------------+ +*/ + +#ifndef PHP_LEXBOR_H +#define PHP_LEXBOR_H + +#include "php_uri_common.h" +#include "lexbor/url/url.h" + +extern const uri_handler_t lexbor_uri_handler; + +lxb_url_t *lexbor_parse_uri_ex(const zend_string *uri_str, const lxb_url_t *lexbor_base_url, zval *errors, bool silent); + +zend_result lexbor_request_init(void); +void lexbor_request_shutdown(void); + +#endif diff --git a/ext/uri/php_uri.c b/ext/uri/php_uri.c index bd5c622ed3765..5b2e21b1625a3 100644 --- a/ext/uri/php_uri.c +++ b/ext/uri/php_uri.c @@ -22,16 +22,23 @@ #include "Zend/zend_interfaces.h" #include "Zend/zend_exceptions.h" #include "Zend/zend_attributes.h" -#include "main/php_ini.h" +#include "Zend/zend_enum.h" #include "ext/standard/info.h" #include "php_uri.h" +#include "php_uri_common.h" +#include "php_lexbor.h" #include "php_uri_arginfo.h" #include "uriparser/src/UriConfig.h" +zend_class_entry *uri_whatwg_url_ce; +zend_object_handlers uri_whatwg_uri_object_handlers; +zend_class_entry *uri_comparison_mode_ce; zend_class_entry *uri_exception_ce; -zend_class_entry *invalid_uri_exception_ce; -zend_class_entry *whatwg_invalid_url_exception_ce; +zend_class_entry *uri_invalid_uri_exception_ce; +zend_class_entry *uri_whatwg_invalid_url_exception_ce; +zend_class_entry *uri_whatwg_url_validation_error_type_ce; +zend_class_entry *uri_whatwg_url_validation_error_ce; #define URIPARSER_VERSION PACKAGE_VERSION @@ -40,12 +47,603 @@ static const zend_module_dep uri_deps[] = { ZEND_MOD_END }; +static zend_array uri_handlers; + +static uri_handler_t *uri_handler_by_name(const char *handler_name, size_t handler_name_len) +{ + return zend_hash_str_find_ptr(&uri_handlers, handler_name, handler_name_len); +} + +static HashTable *uri_get_debug_properties(zend_object *object) +{ + uri_internal_t *internal_uri = uri_internal_from_obj(object); + ZEND_ASSERT(internal_uri != NULL); + + HashTable *std_properties = zend_std_get_properties(object); + HashTable *result = zend_array_dup(std_properties); + + if (UNEXPECTED(internal_uri->uri == NULL)) { + return result; + } + + const uri_property_handlers_t property_handlers = internal_uri->handler->property_handlers; + + zval tmp; + if (property_handlers.scheme.read_func(internal_uri, URI_COMPONENT_READ_RAW, &tmp) == SUCCESS) { + zend_hash_update(result, ZSTR_KNOWN(ZEND_STR_SCHEME), &tmp); + } + + if (property_handlers.username.read_func(internal_uri, URI_COMPONENT_READ_RAW, &tmp) == SUCCESS) { + zend_hash_update(result, ZSTR_KNOWN(ZEND_STR_USERNAME), &tmp); + } + + if (property_handlers.password.read_func(internal_uri, URI_COMPONENT_READ_RAW, &tmp) == SUCCESS) { + zend_hash_update(result, ZSTR_KNOWN(ZEND_STR_PASSWORD), &tmp); + } + + if (property_handlers.host.read_func(internal_uri, URI_COMPONENT_READ_RAW, &tmp) == SUCCESS) { + zend_hash_update(result, ZSTR_KNOWN(ZEND_STR_HOST), &tmp); + } + + if (property_handlers.port.read_func(internal_uri, URI_COMPONENT_READ_RAW, &tmp) == SUCCESS) { + zend_hash_update(result, ZSTR_KNOWN(ZEND_STR_PORT), &tmp); + } + + if (property_handlers.path.read_func(internal_uri, URI_COMPONENT_READ_RAW, &tmp) == SUCCESS) { + zend_hash_update(result, ZSTR_KNOWN(ZEND_STR_PATH), &tmp); + } + + if (property_handlers.query.read_func(internal_uri, URI_COMPONENT_READ_RAW, &tmp) == SUCCESS) { + zend_hash_update(result, ZSTR_KNOWN(ZEND_STR_QUERY), &tmp); + } + + if (property_handlers.fragment.read_func(internal_uri, URI_COMPONENT_READ_RAW, &tmp) == SUCCESS) { + zend_hash_update(result, ZSTR_KNOWN(ZEND_STR_FRAGMENT), &tmp); + } + + return result; +} + +PHP_METHOD(Uri_WhatWg_InvalidUrlException, __construct) +{ + zend_string *message = NULL; + zval *errors = NULL; + zend_long code = 0; + zval *previous = NULL; + + ZEND_PARSE_PARAMETERS_START(0, 4) + Z_PARAM_OPTIONAL + Z_PARAM_STR(message) + Z_PARAM_ARRAY(errors) + Z_PARAM_LONG(code) + Z_PARAM_OBJECT_OF_CLASS_OR_NULL(previous, zend_ce_throwable) + ZEND_PARSE_PARAMETERS_END(); + + if (zend_update_exception_properties(INTERNAL_FUNCTION_PARAM_PASSTHRU, message, code, previous) == FAILURE) { + RETURN_THROWS(); + } + + if (errors == NULL) { + zval tmp; + ZVAL_EMPTY_ARRAY(&tmp); + zend_update_property(uri_whatwg_invalid_url_exception_ce, Z_OBJ_P(ZEND_THIS), ZEND_STRL("errors"), &tmp); + } else { + zend_update_property(uri_whatwg_invalid_url_exception_ce, Z_OBJ_P(ZEND_THIS), ZEND_STRL("errors"), errors); + } + if (EG(exception)) { + RETURN_THROWS(); + } +} + +PHP_METHOD(Uri_WhatWg_UrlValidationError, __construct) +{ + zend_string *context; + zval *type; + bool failure; + + ZEND_PARSE_PARAMETERS_START(3, 3) + Z_PARAM_STR(context) + Z_PARAM_OBJECT_OF_CLASS(type, uri_whatwg_url_validation_error_type_ce) + Z_PARAM_BOOL(failure) + ZEND_PARSE_PARAMETERS_END(); + + zend_update_property_str(uri_whatwg_url_validation_error_ce, Z_OBJ_P(ZEND_THIS), ZEND_STRL("context"), context); + if (EG(exception)) { + RETURN_THROWS(); + } + + zend_update_property_ex(uri_whatwg_url_validation_error_ce, Z_OBJ_P(ZEND_THIS), ZSTR_KNOWN(ZEND_STR_TYPE), type); + if (EG(exception)) { + RETURN_THROWS(); + } + + zval failure_zv; + ZVAL_BOOL(&failure_zv, failure); + zend_update_property(uri_whatwg_url_validation_error_ce, Z_OBJ_P(ZEND_THIS), ZEND_STRL("failure"), &failure_zv); + if (EG(exception)) { + RETURN_THROWS(); + } +} + +/** + * Pass the errors parameter by ref to errors_zv for userland, and frees it if + * it is not not needed anymore. + */ +static zend_result pass_errors_by_ref_and_free(zval *errors_zv, zval *errors) +{ + ZEND_ASSERT(Z_TYPE_P(errors) == IS_UNDEF || Z_TYPE_P(errors) == IS_ARRAY); + + /* There was no error during parsing */ + if (Z_ISUNDEF_P(errors)) { + return SUCCESS; + } + + /* The errors parameter is an array, but the pass-by ref argument stored by + * errors_zv was not passed - the URI implementation either doesn't support + * returning additional error information, or the caller is not interested in it */ + if (errors_zv == NULL) { + zval_ptr_dtor(errors); + return SUCCESS; + } + + ZEND_TRY_ASSIGN_REF_ARR(errors_zv, Z_ARRVAL_P(errors)); + if (EG(exception)) { + zval_ptr_dtor(errors); + return FAILURE; + } + + return SUCCESS; +} + +PHPAPI void php_uri_instantiate_uri( + INTERNAL_FUNCTION_PARAMETERS, const uri_handler_t *handler, const zend_string *uri_str, const zend_object *base_url_object, + bool should_throw, bool should_update_this_object, zval *errors_zv +) { + zval errors; + ZVAL_UNDEF(&errors); + + void *base_url = NULL; + if (base_url_object != NULL) { + uri_internal_t *internal_base_url = uri_internal_from_obj(base_url_object); + URI_ASSERT_INITIALIZATION(internal_base_url); + base_url = internal_base_url->uri; + } + + void *uri = handler->parse_uri(uri_str, base_url, should_throw || errors_zv != NULL ? &errors : NULL, !should_throw); + if (UNEXPECTED(uri == NULL)) { + if (should_throw) { + zval_ptr_dtor(&errors); + RETURN_THROWS(); + } else { + if (pass_errors_by_ref_and_free(errors_zv, &errors) == FAILURE) { + RETURN_THROWS(); + } + RETURN_NULL(); + } + } + + if (pass_errors_by_ref_and_free(errors_zv, &errors) == FAILURE) { + RETURN_THROWS(); + } + + uri_object_t *uri_object; + if (should_update_this_object) { + uri_object = Z_URI_OBJECT_P(ZEND_THIS); + } else { + if (EX(func)->common.fn_flags & ZEND_ACC_STATIC) { + object_init_ex(return_value, Z_CE_P(ZEND_THIS)); + } else { + object_init_ex(return_value, Z_OBJCE_P(ZEND_THIS)); + } + uri_object = Z_URI_OBJECT_P(return_value); + } + + uri_object->internal.handler = handler; + uri_object->internal.uri = uri; +} + +static void create_whatwg_uri(INTERNAL_FUNCTION_PARAMETERS, bool is_constructor) +{ + zend_string *uri_str; + zend_object *base_url_object = NULL; + zval *errors = NULL; + + ZEND_PARSE_PARAMETERS_START(1, 3) + Z_PARAM_PATH_STR(uri_str) + Z_PARAM_OPTIONAL + Z_PARAM_OBJ_OF_CLASS_OR_NULL(base_url_object, uri_whatwg_url_ce) + Z_PARAM_ZVAL(errors) + ZEND_PARSE_PARAMETERS_END(); + + php_uri_instantiate_uri(INTERNAL_FUNCTION_PARAM_PASSTHRU, &lexbor_uri_handler, uri_str, base_url_object, is_constructor, is_constructor, errors); +} + +PHP_METHOD(Uri_WhatWg_Url, parse) +{ + create_whatwg_uri(INTERNAL_FUNCTION_PARAM_PASSTHRU, false); +} + +PHP_METHOD(Uri_WhatWg_Url, __construct) +{ + create_whatwg_uri(INTERNAL_FUNCTION_PARAM_PASSTHRU, true); +} + +static void uri_equals(INTERNAL_FUNCTION_PARAMETERS, zend_object *that_object, zend_object *comparison_mode) +{ + zend_object *this_object = Z_OBJ_P(ZEND_THIS); + uri_internal_t *this_internal_uri = uri_internal_from_obj(this_object); + URI_ASSERT_INITIALIZATION(this_internal_uri); + + uri_internal_t *that_internal_uri = uri_internal_from_obj(that_object); + URI_ASSERT_INITIALIZATION(that_internal_uri); + + if (this_object->ce != that_object->ce && + !instanceof_function(this_object->ce, that_object->ce) && + !instanceof_function(that_object->ce, this_object->ce) + ) { + RETURN_FALSE; + } + + bool exclude_fragment = true; + if (comparison_mode) { + zval *case_name = zend_enum_fetch_case_name(comparison_mode); + exclude_fragment = zend_string_equals_literal(Z_STR_P(case_name), "ExcludeFragment"); + } + + zend_string *this_str = this_internal_uri->handler->uri_to_string( + this_internal_uri->uri, URI_RECOMPOSITION_NORMALIZED_ASCII, exclude_fragment); + if (this_str == NULL) { + zend_throw_exception_ex(NULL, 0, "Cannot recompose %s to string", ZSTR_VAL(this_object->ce->name)); + RETURN_THROWS(); + } + + zend_string *that_str = that_internal_uri->handler->uri_to_string( + that_internal_uri->uri, URI_RECOMPOSITION_NORMALIZED_ASCII, exclude_fragment); + if (that_str == NULL) { + zend_string_release(this_str); + zend_throw_exception_ex(NULL, 0, "Cannot recompose %s to string", ZSTR_VAL(that_object->ce->name)); + RETURN_THROWS(); + } + + RETVAL_BOOL(zend_string_equals(this_str, that_str)); + + zend_string_release(this_str); + zend_string_release(that_str); +} + +static void uri_unserialize(INTERNAL_FUNCTION_PARAMETERS, const char *handler_name) +{ + HashTable *data; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ARRAY_HT(data) + ZEND_PARSE_PARAMETERS_END(); + + zend_object *object = Z_OBJ_P(ZEND_THIS); + + /* Verify the expected number of elements, this implicitly ensures that no additional elements are present. */ + if (zend_hash_num_elements(data) != 2) { + zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(object->ce->name)); + RETURN_THROWS(); + } + + /* Unserialize state: "uri" key in the first array */ + zval *arr = zend_hash_index_find(data, 0); + if (arr == NULL || Z_TYPE_P(arr) != IS_ARRAY) { + zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(object->ce->name)); + RETURN_THROWS(); + } + + /* Verify the expected number of elements inside the first array, this implicitly ensures that no additional elements are present. */ + if (zend_hash_num_elements(Z_ARRVAL_P(arr)) != 1) { + zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(object->ce->name)); + RETURN_THROWS(); + } + + zval *uri_zv = zend_hash_str_find_ind(Z_ARRVAL_P(arr), ZEND_STRL(URI_SERIALIZED_PROPERTY_NAME)); + if (uri_zv == NULL || Z_TYPE_P(uri_zv) != IS_STRING) { + zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(object->ce->name)); + RETURN_THROWS(); + } + + uri_internal_t *internal_uri = uri_internal_from_obj(object); + internal_uri->handler = uri_handler_by_name(handler_name, strlen(handler_name)); + if (internal_uri->uri != NULL) { + internal_uri->handler->free_uri(internal_uri->uri); + } + internal_uri->uri = internal_uri->handler->parse_uri(Z_STR_P(uri_zv), NULL, NULL, true); + if (internal_uri->uri == NULL) { + zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(object->ce->name)); + RETURN_THROWS(); + } + + /* Unserialize regular properties: second array */ + arr = zend_hash_index_find(data, 1); + if (arr == NULL || Z_TYPE_P(arr) != IS_ARRAY) { + zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(object->ce->name)); + RETURN_THROWS(); + } + + /* Verify that there is no regular property in the second array, because the URI classes have no properties and they are final. */ + if (zend_hash_num_elements(Z_ARRVAL_P(arr)) > 0) { + zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(object->ce->name)); + RETURN_THROWS(); + } +} + +PHP_METHOD(Uri_WhatWg_Url, getScheme) +{ + uri_read_component(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_SCHEME, URI_COMPONENT_READ_NORMALIZED_ASCII); +} + +PHP_METHOD(Uri_WhatWg_Url, withScheme) +{ + uri_write_component_str(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_SCHEME); +} + +PHP_METHOD(Uri_WhatWg_Url, getUsername) +{ + uri_read_component(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_USERNAME, URI_COMPONENT_READ_NORMALIZED_ASCII); +} + +PHP_METHOD(Uri_WhatWg_Url, withUsername) +{ + uri_write_component_str_or_null(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_USERNAME); +} + +PHP_METHOD(Uri_WhatWg_Url, getPassword) +{ + uri_read_component(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_PASSWORD, URI_COMPONENT_READ_NORMALIZED_ASCII); +} + +PHP_METHOD(Uri_WhatWg_Url, withPassword) +{ + uri_write_component_str_or_null(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_PASSWORD); +} + +PHP_METHOD(Uri_WhatWg_Url, getAsciiHost) +{ + uri_read_component(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_HOST, URI_COMPONENT_READ_NORMALIZED_ASCII); +} + +PHP_METHOD(Uri_WhatWg_Url, getUnicodeHost) +{ + uri_read_component(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_HOST, URI_COMPONENT_READ_NORMALIZED_UNICODE); +} + +PHP_METHOD(Uri_WhatWg_Url, withHost) +{ + uri_write_component_str_or_null(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_HOST); +} + +PHP_METHOD(Uri_WhatWg_Url, getPort) +{ + uri_read_component(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_PORT, URI_COMPONENT_READ_NORMALIZED_ASCII); +} + +PHP_METHOD(Uri_WhatWg_Url, withPort) +{ + uri_write_component_long_or_null(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_PORT); +} + +PHP_METHOD(Uri_WhatWg_Url, getPath) +{ + uri_read_component(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_PATH, URI_COMPONENT_READ_NORMALIZED_ASCII); +} + +PHP_METHOD(Uri_WhatWg_Url, withPath) +{ + uri_write_component_str(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_PATH); +} + +PHP_METHOD(Uri_WhatWg_Url, getQuery) +{ + uri_read_component(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_QUERY, URI_COMPONENT_READ_NORMALIZED_ASCII); +} + +PHP_METHOD(Uri_WhatWg_Url, withQuery) +{ + uri_write_component_str_or_null(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_QUERY); +} + +PHP_METHOD(Uri_WhatWg_Url, getFragment) +{ + uri_read_component(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_FRAGMENT, URI_COMPONENT_READ_NORMALIZED_ASCII); +} + +PHP_METHOD(Uri_WhatWg_Url, withFragment) +{ + uri_write_component_str_or_null(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_FRAGMENT); +} + +PHP_METHOD(Uri_WhatWg_Url, equals) +{ + zend_object *that_object; + zend_object *comparison_mode = NULL; + + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_OBJ_OF_CLASS(that_object, uri_whatwg_url_ce) + Z_PARAM_OPTIONAL + Z_PARAM_OBJ_OF_CLASS(comparison_mode, uri_comparison_mode_ce) + ZEND_PARSE_PARAMETERS_END(); + + uri_equals(INTERNAL_FUNCTION_PARAM_PASSTHRU, that_object, comparison_mode); +} + +PHP_METHOD(Uri_WhatWg_Url, toUnicodeString) +{ + ZEND_PARSE_PARAMETERS_NONE(); + + zend_object *this_object = Z_OBJ_P(ZEND_THIS); + uri_internal_t *internal_uri = uri_internal_from_obj(this_object); + URI_ASSERT_INITIALIZATION(internal_uri); + + RETURN_STR(internal_uri->handler->uri_to_string(internal_uri->uri, URI_RECOMPOSITION_RAW_UNICODE, false)); +} + +PHP_METHOD(Uri_WhatWg_Url, toAsciiString) +{ + ZEND_PARSE_PARAMETERS_NONE(); + + zend_object *this_object = Z_OBJ_P(ZEND_THIS); + uri_internal_t *internal_uri = uri_internal_from_obj(this_object); + URI_ASSERT_INITIALIZATION(internal_uri); + + RETURN_STR(internal_uri->handler->uri_to_string(internal_uri->uri, URI_RECOMPOSITION_RAW_ASCII, false)); +} + +PHP_METHOD(Uri_WhatWg_Url, resolve) +{ + zend_string *uri_str; + zval *errors = NULL; + + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_PATH_STR(uri_str) + Z_PARAM_OPTIONAL + Z_PARAM_ZVAL(errors) + ZEND_PARSE_PARAMETERS_END(); + + zend_object *this_object = Z_OBJ_P(ZEND_THIS); + uri_internal_t *internal_uri = uri_internal_from_obj(this_object); + URI_ASSERT_INITIALIZATION(internal_uri); + + php_uri_instantiate_uri(INTERNAL_FUNCTION_PARAM_PASSTHRU, internal_uri->handler, uri_str, this_object, true, false, errors); +} + +PHP_METHOD(Uri_WhatWg_Url, __serialize) +{ + ZEND_PARSE_PARAMETERS_NONE(); + + zend_object *this_object = Z_OBJ_P(ZEND_THIS); + uri_internal_t *internal_uri = uri_internal_from_obj(this_object); + URI_ASSERT_INITIALIZATION(internal_uri); + + /* Serialize state: "uri" key in the first array */ + zend_string *uri_str = internal_uri->handler->uri_to_string(internal_uri->uri, URI_RECOMPOSITION_RAW_ASCII, false); + if (uri_str == NULL) { + zend_throw_exception_ex(NULL, 0, "Cannot recompose %s to string", ZSTR_VAL(this_object->ce->name)); + RETURN_THROWS(); + } + zval tmp; + ZVAL_STR(&tmp, uri_str); + + array_init(return_value); + + zval arr; + array_init(&arr); + zend_hash_str_add_new(Z_ARRVAL(arr), ZEND_STRL(URI_SERIALIZED_PROPERTY_NAME), &tmp); + zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &arr); + + /* Serialize regular properties: second array */ + ZVAL_ARR(&arr, this_object->handlers->get_properties(this_object)); + Z_ADDREF(arr); + zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &arr); +} + +PHP_METHOD(Uri_WhatWg_Url, __unserialize) +{ + uri_unserialize(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PARSER_WHATWG); +} + +PHP_METHOD(Uri_WhatWg_Url, __debugInfo) +{ + ZEND_PARSE_PARAMETERS_NONE(); + + zend_object *object = Z_OBJ_P(ZEND_THIS); + + RETURN_ARR(uri_get_debug_properties(object)); +} + +static zend_object *uri_create_object_handler(zend_class_entry *class_type) +{ + uri_object_t *uri_object = zend_object_alloc(sizeof(uri_object_t), class_type); + + zend_object_std_init(&uri_object->std, class_type); + object_properties_init(&uri_object->std, class_type); + + return &uri_object->std; +} + +static void uri_free_obj_handler(zend_object *object) +{ + uri_object_t *uri_object = uri_object_from_obj(object); + + if (UNEXPECTED(uri_object->internal.uri != NULL)) { + uri_object->internal.handler->free_uri(uri_object->internal.uri); + uri_object->internal.handler = NULL; + uri_object->internal.uri = NULL; + } + + zend_object_std_dtor(&uri_object->std); +} + +zend_object *uri_clone_obj_handler(zend_object *object) +{ + uri_object_t *uri_object = uri_object_from_obj(object); + uri_internal_t *internal_uri = uri_internal_from_obj(object); + + URI_ASSERT_INITIALIZATION(internal_uri); + + zend_object *new_object = uri_create_object_handler(object->ce); + ZEND_ASSERT(new_object != NULL); + uri_object_t *new_uri_object = uri_object_from_obj(new_object); + + new_uri_object->internal.handler = internal_uri->handler; + + void *uri = internal_uri->handler->clone_uri(internal_uri->uri); + ZEND_ASSERT(uri != NULL); + + new_uri_object->internal.uri = uri; + + zend_objects_clone_members(&new_uri_object->std, &uri_object->std); + + return &new_uri_object->std; +} + +PHPAPI void php_uri_implementation_set_object_handlers(zend_class_entry *ce, zend_object_handlers *object_handlers) +{ + ce->create_object = uri_create_object_handler; + ce->default_object_handlers = object_handlers; + memcpy(object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); + object_handlers->offset = XtOffsetOf(uri_object_t, std); + object_handlers->free_obj = uri_free_obj_handler; + object_handlers->clone_obj = uri_clone_obj_handler; +} + +zend_result uri_handler_register(const uri_handler_t *uri_handler) +{ + zend_string *key = zend_string_init_interned(uri_handler->name, strlen(uri_handler->name), 1); + + ZEND_ASSERT(uri_handler->name != NULL); + ZEND_ASSERT(uri_handler->parse_uri != NULL); + ZEND_ASSERT(uri_handler->clone_uri != NULL); + ZEND_ASSERT(uri_handler->uri_to_string != NULL); + ZEND_ASSERT(uri_handler->free_uri != NULL); + + zend_result result = zend_hash_add_ptr(&uri_handlers, key, (void *) uri_handler) != NULL ? SUCCESS : FAILURE; + + zend_string_release_ex(key, true); + + return result; +} static PHP_MINIT_FUNCTION(uri) { + uri_whatwg_url_ce = register_class_Uri_WhatWg_Url(); + php_uri_implementation_set_object_handlers(uri_whatwg_url_ce, &uri_whatwg_uri_object_handlers); + + uri_comparison_mode_ce = register_class_Uri_UriComparisonMode(); uri_exception_ce = register_class_Uri_UriException(zend_ce_exception); - invalid_uri_exception_ce = register_class_Uri_InvalidUriException(uri_exception_ce); - whatwg_invalid_url_exception_ce = register_class_Uri_WhatWg_InvalidUrlException(invalid_uri_exception_ce); + uri_invalid_uri_exception_ce = register_class_Uri_InvalidUriException(uri_exception_ce); + uri_whatwg_invalid_url_exception_ce = register_class_Uri_WhatWg_InvalidUrlException(uri_invalid_uri_exception_ce); + uri_whatwg_url_validation_error_ce = register_class_Uri_WhatWg_UrlValidationError(); + uri_whatwg_url_validation_error_type_ce = register_class_Uri_WhatWg_UrlValidationErrorType(); + + zend_hash_init(&uri_handlers, 4, NULL, NULL, true); + + if (uri_handler_register(&lexbor_uri_handler) == FAILURE) { + return FAILURE; + } return SUCCESS; } @@ -60,18 +658,24 @@ static PHP_MINFO_FUNCTION(uri) static PHP_MSHUTDOWN_FUNCTION(uri) { + zend_hash_destroy(&uri_handlers); return SUCCESS; } PHP_RINIT_FUNCTION(uri) { + if (lexbor_request_init() == FAILURE) { + return FAILURE; + } return SUCCESS; } PHP_RSHUTDOWN_FUNCTION(uri) { + lexbor_request_shutdown(); + return SUCCESS; } diff --git a/ext/uri/php_uri.h b/ext/uri/php_uri.h index 79dfced4a721a..9e22c227cbf83 100644 --- a/ext/uri/php_uri.h +++ b/ext/uri/php_uri.h @@ -17,8 +17,11 @@ #ifndef PHP_URI_H #define PHP_URI_H +#include "php_uri_common.h" + extern zend_module_entry uri_module_entry; #define phpext_uri_ptr &uri_module_entry +PHPAPI void php_uri_implementation_set_object_handlers(zend_class_entry *ce, zend_object_handlers *object_handlers); #endif diff --git a/ext/uri/php_uri.stub.php b/ext/uri/php_uri.stub.php index 926be1fbb8267..ef49e4ba6f968 100644 --- a/ext/uri/php_uri.stub.php +++ b/ext/uri/php_uri.stub.php @@ -12,6 +12,12 @@ class UriException extends \Exception class InvalidUriException extends \Uri\UriException { } + + enum UriComparisonMode + { + case IncludeFragment; + case ExcludeFragment; + } } namespace Uri\WhatWg { @@ -19,5 +25,109 @@ class InvalidUriException extends \Uri\UriException class InvalidUrlException extends \Uri\InvalidUriException { public readonly array $errors; + + public function __construct(string $message = "", array $errors = [], int $code = 0, ?\Throwable $previous = null) {} + } + + enum UrlValidationErrorType + { + case DomainToAscii; + case DomainToUnicode; + case DomainInvalidCodePoint; + case HostInvalidCodePoint; + case Ipv4EmptyPart; + case Ipv4TooManyParts; + case Ipv4NonNumericPart; + case Ipv4NonDecimalPart; + case Ipv4OutOfRangePart; + case Ipv6Unclosed; + case Ipv6InvalidCompression; + case Ipv6TooManyPieces; + case Ipv6MultipleCompression; + case Ipv6InvalidCodePoint; + case Ipv6TooFewPieces; + case Ipv4InIpv6TooManyPieces; + case Ipv4InIpv6InvalidCodePoint; + case Ipv4InIpv6OutOfRangePart; + case Ipv4InIpv6TooFewParts; + case InvalidUrlUnit; + case SpecialSchemeMissingFollowingSolidus; + case MissingSchemeNonRelativeUrl; + case InvalidReverseSoldius; + case InvalidCredentials; + case HostMissing; + case PortOutOfRange; + case PortInvalid; + case FileInvalidWindowsDriveLetter; + case FileInvalidWindowsDriveLetterHost; + } + + /** @strict-properties */ + final readonly class UrlValidationError + { + public string $context; + public \Uri\WhatWg\UrlValidationErrorType $type; + public bool $failure; + + public function __construct(string $context, \Uri\WhatWg\UrlValidationErrorType $type, bool $failure) {} + } + + /** @strict-properties */ + final readonly class Url + { + /** @param array $errors */ + public static function parse(string $uri, ?\Uri\WhatWg\Url $baseUrl = null, &$errors = null): ?static {} + + /** @param array $softErrors */ + public function __construct(string $uri, ?\Uri\WhatWg\Url $baseUrl = null, &$softErrors = null) {} + + public function getScheme(): string {} + + public function withScheme(string $scheme): static {} + + public function getUsername(): ?string {} + + public function withUsername(?string $username): static {} + + public function getPassword(): ?string {} + + public function withPassword(#[\SensitiveParameter] ?string $password): static {} + + public function getAsciiHost(): ?string {} + + public function getUnicodeHost(): ?string {} + + public function withHost(?string $host): static {} + + public function getPort(): ?int {} + + public function withPort(?int $port): static {} + + public function getPath(): string {} + + public function withPath(string $path): static {} + + public function getQuery(): ?string {} + + public function withQuery(?string $query): static {} + + public function getFragment(): ?string {} + + public function withFragment(?string $fragment): static {} + + public function equals(\Uri\WhatWg\Url $url, \Uri\UriComparisonMode $comparisonMode = \Uri\UriComparisonMode::ExcludeFragment): bool {} + + public function toAsciiString(): string {} + + public function toUnicodeString(): string {} + + /** @param array $softErrors */ + public function resolve(string $uri, &$softErrors = null): static {} + + public function __serialize(): array {} + + public function __unserialize(array $data): void {} + + public function __debugInfo(): array {} } } diff --git a/ext/uri/php_uri_arginfo.h b/ext/uri/php_uri_arginfo.h index 124a6b3719849..0ae755a9f70dc 100644 --- a/ext/uri/php_uri_arginfo.h +++ b/ext/uri/php_uri_arginfo.h @@ -1,5 +1,175 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 35460b24cf237585dabdc9813212c343034cf591 */ + * Stub hash: 1945c28deef13c2af552b18c2a5a6c7798d4aeec */ + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Uri_WhatWg_InvalidUrlException___construct, 0, 0, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, message, IS_STRING, 0, "\"\"") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, errors, IS_ARRAY, 0, "[]") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, code, IS_LONG, 0, "0") + ZEND_ARG_OBJ_INFO_WITH_DEFAULT_VALUE(0, previous, Throwable, 1, "null") +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Uri_WhatWg_UrlValidationError___construct, 0, 0, 3) + ZEND_ARG_TYPE_INFO(0, context, IS_STRING, 0) + ZEND_ARG_OBJ_INFO(0, type, Uri\\WhatWg\\\125rlValidationErrorType, 0) + ZEND_ARG_TYPE_INFO(0, failure, _IS_BOOL, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_WhatWg_Url_parse, 0, 1, IS_STATIC, 1) + ZEND_ARG_TYPE_INFO(0, uri, IS_STRING, 0) + ZEND_ARG_OBJ_INFO_WITH_DEFAULT_VALUE(0, baseUrl, Uri\\WhatWg\\\125rl, 1, "null") + ZEND_ARG_INFO_WITH_DEFAULT_VALUE(1, errors, "null") +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Uri_WhatWg_Url___construct, 0, 0, 1) + ZEND_ARG_TYPE_INFO(0, uri, IS_STRING, 0) + ZEND_ARG_OBJ_INFO_WITH_DEFAULT_VALUE(0, baseUrl, Uri\\WhatWg\\\125rl, 1, "null") + ZEND_ARG_INFO_WITH_DEFAULT_VALUE(1, softErrors, "null") +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_WhatWg_Url_getScheme, 0, 0, IS_STRING, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_WhatWg_Url_withScheme, 0, 1, IS_STATIC, 0) + ZEND_ARG_TYPE_INFO(0, scheme, IS_STRING, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_WhatWg_Url_getUsername, 0, 0, IS_STRING, 1) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_WhatWg_Url_withUsername, 0, 1, IS_STATIC, 0) + ZEND_ARG_TYPE_INFO(0, username, IS_STRING, 1) +ZEND_END_ARG_INFO() + +#define arginfo_class_Uri_WhatWg_Url_getPassword arginfo_class_Uri_WhatWg_Url_getUsername + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_WhatWg_Url_withPassword, 0, 1, IS_STATIC, 0) + ZEND_ARG_TYPE_INFO(0, password, IS_STRING, 1) +ZEND_END_ARG_INFO() + +#define arginfo_class_Uri_WhatWg_Url_getAsciiHost arginfo_class_Uri_WhatWg_Url_getUsername + +#define arginfo_class_Uri_WhatWg_Url_getUnicodeHost arginfo_class_Uri_WhatWg_Url_getUsername + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_WhatWg_Url_withHost, 0, 1, IS_STATIC, 0) + ZEND_ARG_TYPE_INFO(0, host, IS_STRING, 1) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_WhatWg_Url_getPort, 0, 0, IS_LONG, 1) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_WhatWg_Url_withPort, 0, 1, IS_STATIC, 0) + ZEND_ARG_TYPE_INFO(0, port, IS_LONG, 1) +ZEND_END_ARG_INFO() + +#define arginfo_class_Uri_WhatWg_Url_getPath arginfo_class_Uri_WhatWg_Url_getScheme + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_WhatWg_Url_withPath, 0, 1, IS_STATIC, 0) + ZEND_ARG_TYPE_INFO(0, path, IS_STRING, 0) +ZEND_END_ARG_INFO() + +#define arginfo_class_Uri_WhatWg_Url_getQuery arginfo_class_Uri_WhatWg_Url_getUsername + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_WhatWg_Url_withQuery, 0, 1, IS_STATIC, 0) + ZEND_ARG_TYPE_INFO(0, query, IS_STRING, 1) +ZEND_END_ARG_INFO() + +#define arginfo_class_Uri_WhatWg_Url_getFragment arginfo_class_Uri_WhatWg_Url_getUsername + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_WhatWg_Url_withFragment, 0, 1, IS_STATIC, 0) + ZEND_ARG_TYPE_INFO(0, fragment, IS_STRING, 1) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_WhatWg_Url_equals, 0, 1, _IS_BOOL, 0) + ZEND_ARG_OBJ_INFO(0, url, Uri\\WhatWg\\\125rl, 0) + ZEND_ARG_OBJ_INFO_WITH_DEFAULT_VALUE(0, comparisonMode, Uri\\\125riComparisonMode, 0, "Uri\\UriComparisonMode::ExcludeFragment") +ZEND_END_ARG_INFO() + +#define arginfo_class_Uri_WhatWg_Url_toAsciiString arginfo_class_Uri_WhatWg_Url_getScheme + +#define arginfo_class_Uri_WhatWg_Url_toUnicodeString arginfo_class_Uri_WhatWg_Url_getScheme + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_WhatWg_Url_resolve, 0, 1, IS_STATIC, 0) + ZEND_ARG_TYPE_INFO(0, uri, IS_STRING, 0) + ZEND_ARG_INFO_WITH_DEFAULT_VALUE(1, softErrors, "null") +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_WhatWg_Url___serialize, 0, 0, IS_ARRAY, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_WhatWg_Url___unserialize, 0, 1, IS_VOID, 0) + ZEND_ARG_TYPE_INFO(0, data, IS_ARRAY, 0) +ZEND_END_ARG_INFO() + +#define arginfo_class_Uri_WhatWg_Url___debugInfo arginfo_class_Uri_WhatWg_Url___serialize + +ZEND_METHOD(Uri_WhatWg_InvalidUrlException, __construct); +ZEND_METHOD(Uri_WhatWg_UrlValidationError, __construct); +ZEND_METHOD(Uri_WhatWg_Url, parse); +ZEND_METHOD(Uri_WhatWg_Url, __construct); +ZEND_METHOD(Uri_WhatWg_Url, getScheme); +ZEND_METHOD(Uri_WhatWg_Url, withScheme); +ZEND_METHOD(Uri_WhatWg_Url, getUsername); +ZEND_METHOD(Uri_WhatWg_Url, withUsername); +ZEND_METHOD(Uri_WhatWg_Url, getPassword); +ZEND_METHOD(Uri_WhatWg_Url, withPassword); +ZEND_METHOD(Uri_WhatWg_Url, getAsciiHost); +ZEND_METHOD(Uri_WhatWg_Url, getUnicodeHost); +ZEND_METHOD(Uri_WhatWg_Url, withHost); +ZEND_METHOD(Uri_WhatWg_Url, getPort); +ZEND_METHOD(Uri_WhatWg_Url, withPort); +ZEND_METHOD(Uri_WhatWg_Url, getPath); +ZEND_METHOD(Uri_WhatWg_Url, withPath); +ZEND_METHOD(Uri_WhatWg_Url, getQuery); +ZEND_METHOD(Uri_WhatWg_Url, withQuery); +ZEND_METHOD(Uri_WhatWg_Url, getFragment); +ZEND_METHOD(Uri_WhatWg_Url, withFragment); +ZEND_METHOD(Uri_WhatWg_Url, equals); +ZEND_METHOD(Uri_WhatWg_Url, toAsciiString); +ZEND_METHOD(Uri_WhatWg_Url, toUnicodeString); +ZEND_METHOD(Uri_WhatWg_Url, resolve); +ZEND_METHOD(Uri_WhatWg_Url, __serialize); +ZEND_METHOD(Uri_WhatWg_Url, __unserialize); +ZEND_METHOD(Uri_WhatWg_Url, __debugInfo); + +static const zend_function_entry class_Uri_WhatWg_InvalidUrlException_methods[] = { + ZEND_ME(Uri_WhatWg_InvalidUrlException, __construct, arginfo_class_Uri_WhatWg_InvalidUrlException___construct, ZEND_ACC_PUBLIC) + ZEND_FE_END +}; + +static const zend_function_entry class_Uri_WhatWg_UrlValidationError_methods[] = { + ZEND_ME(Uri_WhatWg_UrlValidationError, __construct, arginfo_class_Uri_WhatWg_UrlValidationError___construct, ZEND_ACC_PUBLIC) + ZEND_FE_END +}; + +static const zend_function_entry class_Uri_WhatWg_Url_methods[] = { + ZEND_ME(Uri_WhatWg_Url, parse, arginfo_class_Uri_WhatWg_Url_parse, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) + ZEND_ME(Uri_WhatWg_Url, __construct, arginfo_class_Uri_WhatWg_Url___construct, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_WhatWg_Url, getScheme, arginfo_class_Uri_WhatWg_Url_getScheme, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_WhatWg_Url, withScheme, arginfo_class_Uri_WhatWg_Url_withScheme, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_WhatWg_Url, getUsername, arginfo_class_Uri_WhatWg_Url_getUsername, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_WhatWg_Url, withUsername, arginfo_class_Uri_WhatWg_Url_withUsername, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_WhatWg_Url, getPassword, arginfo_class_Uri_WhatWg_Url_getPassword, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_WhatWg_Url, withPassword, arginfo_class_Uri_WhatWg_Url_withPassword, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_WhatWg_Url, getAsciiHost, arginfo_class_Uri_WhatWg_Url_getAsciiHost, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_WhatWg_Url, getUnicodeHost, arginfo_class_Uri_WhatWg_Url_getUnicodeHost, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_WhatWg_Url, withHost, arginfo_class_Uri_WhatWg_Url_withHost, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_WhatWg_Url, getPort, arginfo_class_Uri_WhatWg_Url_getPort, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_WhatWg_Url, withPort, arginfo_class_Uri_WhatWg_Url_withPort, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_WhatWg_Url, getPath, arginfo_class_Uri_WhatWg_Url_getPath, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_WhatWg_Url, withPath, arginfo_class_Uri_WhatWg_Url_withPath, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_WhatWg_Url, getQuery, arginfo_class_Uri_WhatWg_Url_getQuery, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_WhatWg_Url, withQuery, arginfo_class_Uri_WhatWg_Url_withQuery, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_WhatWg_Url, getFragment, arginfo_class_Uri_WhatWg_Url_getFragment, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_WhatWg_Url, withFragment, arginfo_class_Uri_WhatWg_Url_withFragment, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_WhatWg_Url, equals, arginfo_class_Uri_WhatWg_Url_equals, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_WhatWg_Url, toAsciiString, arginfo_class_Uri_WhatWg_Url_toAsciiString, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_WhatWg_Url, toUnicodeString, arginfo_class_Uri_WhatWg_Url_toUnicodeString, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_WhatWg_Url, resolve, arginfo_class_Uri_WhatWg_Url_resolve, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_WhatWg_Url, __serialize, arginfo_class_Uri_WhatWg_Url___serialize, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_WhatWg_Url, __unserialize, arginfo_class_Uri_WhatWg_Url___unserialize, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_WhatWg_Url, __debugInfo, arginfo_class_Uri_WhatWg_Url___debugInfo, ZEND_ACC_PUBLIC) + ZEND_FE_END +}; static zend_class_entry *register_class_Uri_UriException(zend_class_entry *class_entry_Exception) { @@ -21,11 +191,22 @@ static zend_class_entry *register_class_Uri_InvalidUriException(zend_class_entry return class_entry; } +static zend_class_entry *register_class_Uri_UriComparisonMode(void) +{ + zend_class_entry *class_entry = zend_register_internal_enum("Uri\\UriComparisonMode", IS_UNDEF, NULL); + + zend_enum_add_case_cstr(class_entry, "IncludeFragment", NULL); + + zend_enum_add_case_cstr(class_entry, "ExcludeFragment", NULL); + + return class_entry; +} + static zend_class_entry *register_class_Uri_WhatWg_InvalidUrlException(zend_class_entry *class_entry_Uri_InvalidUriException) { zend_class_entry ce, *class_entry; - INIT_NS_CLASS_ENTRY(ce, "Uri\\WhatWg", "InvalidUrlException", NULL); + INIT_NS_CLASS_ENTRY(ce, "Uri\\WhatWg", "InvalidUrlException", class_Uri_WhatWg_InvalidUrlException_methods); class_entry = zend_register_internal_class_with_flags(&ce, class_entry_Uri_InvalidUriException, ZEND_ACC_NO_DYNAMIC_PROPERTIES); zval property_errors_default_value; @@ -36,3 +217,108 @@ static zend_class_entry *register_class_Uri_WhatWg_InvalidUrlException(zend_clas return class_entry; } + +static zend_class_entry *register_class_Uri_WhatWg_UrlValidationErrorType(void) +{ + zend_class_entry *class_entry = zend_register_internal_enum("Uri\\WhatWg\\UrlValidationErrorType", IS_UNDEF, NULL); + + zend_enum_add_case_cstr(class_entry, "DomainToAscii", NULL); + + zend_enum_add_case_cstr(class_entry, "DomainToUnicode", NULL); + + zend_enum_add_case_cstr(class_entry, "DomainInvalidCodePoint", NULL); + + zend_enum_add_case_cstr(class_entry, "HostInvalidCodePoint", NULL); + + zend_enum_add_case_cstr(class_entry, "Ipv4EmptyPart", NULL); + + zend_enum_add_case_cstr(class_entry, "Ipv4TooManyParts", NULL); + + zend_enum_add_case_cstr(class_entry, "Ipv4NonNumericPart", NULL); + + zend_enum_add_case_cstr(class_entry, "Ipv4NonDecimalPart", NULL); + + zend_enum_add_case_cstr(class_entry, "Ipv4OutOfRangePart", NULL); + + zend_enum_add_case_cstr(class_entry, "Ipv6Unclosed", NULL); + + zend_enum_add_case_cstr(class_entry, "Ipv6InvalidCompression", NULL); + + zend_enum_add_case_cstr(class_entry, "Ipv6TooManyPieces", NULL); + + zend_enum_add_case_cstr(class_entry, "Ipv6MultipleCompression", NULL); + + zend_enum_add_case_cstr(class_entry, "Ipv6InvalidCodePoint", NULL); + + zend_enum_add_case_cstr(class_entry, "Ipv6TooFewPieces", NULL); + + zend_enum_add_case_cstr(class_entry, "Ipv4InIpv6TooManyPieces", NULL); + + zend_enum_add_case_cstr(class_entry, "Ipv4InIpv6InvalidCodePoint", NULL); + + zend_enum_add_case_cstr(class_entry, "Ipv4InIpv6OutOfRangePart", NULL); + + zend_enum_add_case_cstr(class_entry, "Ipv4InIpv6TooFewParts", NULL); + + zend_enum_add_case_cstr(class_entry, "InvalidUrlUnit", NULL); + + zend_enum_add_case_cstr(class_entry, "SpecialSchemeMissingFollowingSolidus", NULL); + + zend_enum_add_case_cstr(class_entry, "MissingSchemeNonRelativeUrl", NULL); + + zend_enum_add_case_cstr(class_entry, "InvalidReverseSoldius", NULL); + + zend_enum_add_case_cstr(class_entry, "InvalidCredentials", NULL); + + zend_enum_add_case_cstr(class_entry, "HostMissing", NULL); + + zend_enum_add_case_cstr(class_entry, "PortOutOfRange", NULL); + + zend_enum_add_case_cstr(class_entry, "PortInvalid", NULL); + + zend_enum_add_case_cstr(class_entry, "FileInvalidWindowsDriveLetter", NULL); + + zend_enum_add_case_cstr(class_entry, "FileInvalidWindowsDriveLetterHost", NULL); + + return class_entry; +} + +static zend_class_entry *register_class_Uri_WhatWg_UrlValidationError(void) +{ + zend_class_entry ce, *class_entry; + + INIT_NS_CLASS_ENTRY(ce, "Uri\\WhatWg", "UrlValidationError", class_Uri_WhatWg_UrlValidationError_methods); + class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_READONLY_CLASS); + + zval property_context_default_value; + ZVAL_UNDEF(&property_context_default_value); + zend_string *property_context_name = zend_string_init("context", sizeof("context") - 1, 1); + zend_declare_typed_property(class_entry, property_context_name, &property_context_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); + zend_string_release(property_context_name); + + zval property_type_default_value; + ZVAL_UNDEF(&property_type_default_value); + zend_string *property_type_class_Uri_WhatWg_UrlValidationErrorType = zend_string_init("Uri\\WhatWg\\\125rlValidationErrorType", sizeof("Uri\\WhatWg\\\125rlValidationErrorType")-1, 1); + zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_TYPE), &property_type_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_type_class_Uri_WhatWg_UrlValidationErrorType, 0, 0)); + + zval property_failure_default_value; + ZVAL_UNDEF(&property_failure_default_value); + zend_string *property_failure_name = zend_string_init("failure", sizeof("failure") - 1, 1); + zend_declare_typed_property(class_entry, property_failure_name, &property_failure_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_BOOL)); + zend_string_release(property_failure_name); + + return class_entry; +} + +static zend_class_entry *register_class_Uri_WhatWg_Url(void) +{ + zend_class_entry ce, *class_entry; + + INIT_NS_CLASS_ENTRY(ce, "Uri\\WhatWg", "Url", class_Uri_WhatWg_Url_methods); + class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_READONLY_CLASS); + + + zend_add_parameter_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "withpassword", sizeof("withpassword") - 1), 0, ZSTR_KNOWN(ZEND_STR_SENSITIVEPARAMETER), 0); + + return class_entry; +} diff --git a/ext/uri/php_uri_common.c b/ext/uri/php_uri_common.c new file mode 100644 index 0000000000000..9128b942e57b8 --- /dev/null +++ b/ext/uri/php_uri_common.c @@ -0,0 +1,169 @@ +/* + +----------------------------------------------------------------------+ + | Copyright (c) The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | https://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Máté Kocsis | + +----------------------------------------------------------------------+ +*/ + +#include "php.h" +#include "Zend/zend_interfaces.h" +#include "Zend/zend_exceptions.h" +#include "php_uri_common.h" + +const uri_property_handler_t *uri_property_handler_from_internal_uri(const uri_internal_t *internal_uri, uri_property_name_t property_name) +{ + switch (property_name) { + case URI_PROPERTY_NAME_SCHEME: + return &internal_uri->handler->property_handlers.scheme; + case URI_PROPERTY_NAME_USERNAME: + return &internal_uri->handler->property_handlers.username; + case URI_PROPERTY_NAME_PASSWORD: + return &internal_uri->handler->property_handlers.password; + case URI_PROPERTY_NAME_HOST: + return &internal_uri->handler->property_handlers.host; + case URI_PROPERTY_NAME_PORT: + return &internal_uri->handler->property_handlers.port; + case URI_PROPERTY_NAME_PATH: + return &internal_uri->handler->property_handlers.path; + case URI_PROPERTY_NAME_QUERY: + return &internal_uri->handler->property_handlers.query; + case URI_PROPERTY_NAME_FRAGMENT: + return &internal_uri->handler->property_handlers.fragment; + EMPTY_SWITCH_DEFAULT_CASE() + } +} + +static zend_string *get_known_string_by_property_name(uri_property_name_t property_name) +{ + switch (property_name) { + case URI_PROPERTY_NAME_SCHEME: + return ZSTR_KNOWN(ZEND_STR_SCHEME); + case URI_PROPERTY_NAME_USERNAME: + return ZSTR_KNOWN(ZEND_STR_USERNAME); + case URI_PROPERTY_NAME_PASSWORD: + return ZSTR_KNOWN(ZEND_STR_PASSWORD); + case URI_PROPERTY_NAME_HOST: + return ZSTR_KNOWN(ZEND_STR_HOST); + case URI_PROPERTY_NAME_PORT: + return ZSTR_KNOWN(ZEND_STR_PORT); + case URI_PROPERTY_NAME_PATH: + return ZSTR_KNOWN(ZEND_STR_PATH); + case URI_PROPERTY_NAME_QUERY: + return ZSTR_KNOWN(ZEND_STR_QUERY); + case URI_PROPERTY_NAME_FRAGMENT: + return ZSTR_KNOWN(ZEND_STR_FRAGMENT); + EMPTY_SWITCH_DEFAULT_CASE() + } +} + +void uri_read_component(INTERNAL_FUNCTION_PARAMETERS, uri_property_name_t property_name, uri_component_read_mode_t component_read_mode) +{ + ZEND_PARSE_PARAMETERS_NONE(); + + uri_internal_t *internal_uri = Z_URI_INTERNAL_P(ZEND_THIS); + URI_ASSERT_INITIALIZATION(internal_uri); + + const uri_property_handler_t *property_handler = uri_property_handler_from_internal_uri(internal_uri, property_name); + ZEND_ASSERT(property_handler != NULL); + + if (UNEXPECTED(property_handler->read_func(internal_uri, component_read_mode, return_value) == FAILURE)) { + zend_throw_error(NULL, "%s::$%s property cannot be retrieved", ZSTR_VAL(Z_OBJ_P(ZEND_THIS)->ce->name), + ZSTR_VAL(get_known_string_by_property_name(property_name))); + RETURN_THROWS(); + } +} + +static void uri_write_component_ex(INTERNAL_FUNCTION_PARAMETERS, uri_property_name_t property_name, zval *property_zv) +{ + uri_internal_t *internal_uri = Z_URI_INTERNAL_P(ZEND_THIS); + URI_ASSERT_INITIALIZATION(internal_uri); + + const uri_property_handler_t *property_handler = uri_property_handler_from_internal_uri(internal_uri, property_name); + ZEND_ASSERT(property_handler != NULL); + + zend_object *new_object = uri_clone_obj_handler(Z_OBJ_P(ZEND_THIS)); + if (UNEXPECTED(EG(exception) != NULL)) { + zend_object_release(new_object); + RETURN_THROWS(); + } + + uri_internal_t *new_internal_uri = uri_internal_from_obj(new_object); + URI_ASSERT_INITIALIZATION(new_internal_uri); + if (property_handler->write_func == NULL) { + zend_readonly_property_modification_error_ex(ZSTR_VAL(Z_OBJ_P(ZEND_THIS)->ce->name), + ZSTR_VAL(get_known_string_by_property_name(property_name))); + zend_object_release(new_object); + RETURN_THROWS(); + } + + zval errors; + ZVAL_UNDEF(&errors); + if (property_handler->write_func(new_internal_uri, property_zv, &errors) == FAILURE) { + zval_ptr_dtor(&errors); + zend_object_release(new_object); + RETURN_THROWS(); + } + + ZEND_ASSERT(Z_ISUNDEF(errors)); + RETVAL_OBJ(new_object); +} + +void uri_write_component_str(INTERNAL_FUNCTION_PARAMETERS, uri_property_name_t property_name) +{ + zend_string *value; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_PATH_STR(value) + ZEND_PARSE_PARAMETERS_END(); + + zval zv; + ZVAL_STR(&zv, value); + + uri_write_component_ex(INTERNAL_FUNCTION_PARAM_PASSTHRU, property_name, &zv); +} + +void uri_write_component_str_or_null(INTERNAL_FUNCTION_PARAMETERS, uri_property_name_t property_name) +{ + zend_string *value; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_PATH_STR_OR_NULL(value) + ZEND_PARSE_PARAMETERS_END(); + + zval zv; + if (value == NULL) { + ZVAL_NULL(&zv); + } else { + ZVAL_STR(&zv, value); + } + + uri_write_component_ex(INTERNAL_FUNCTION_PARAM_PASSTHRU, property_name, &zv); +} + +void uri_write_component_long_or_null(INTERNAL_FUNCTION_PARAMETERS, uri_property_name_t property_name) +{ + zend_long value; + bool value_is_null; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG_OR_NULL(value, value_is_null) + ZEND_PARSE_PARAMETERS_END(); + + zval zv; + if (value_is_null) { + ZVAL_NULL(&zv); + } else { + ZVAL_LONG(&zv, value); + } + + uri_write_component_ex(INTERNAL_FUNCTION_PARAM_PASSTHRU, property_name, &zv); +} diff --git a/ext/uri/php_uri_common.h b/ext/uri/php_uri_common.h new file mode 100644 index 0000000000000..1aee1cd512472 --- /dev/null +++ b/ext/uri/php_uri_common.h @@ -0,0 +1,138 @@ +/* + +----------------------------------------------------------------------+ + | Copyright (c) The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | https://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Máté Kocsis | + +----------------------------------------------------------------------+ +*/ + +#ifndef PHP_URI_COMMON_H +#define PHP_URI_COMMON_H + +extern zend_class_entry *uri_whatwg_url_ce; +extern zend_object_handlers uri_whatwg_uri_object_handlers; +extern zend_class_entry *uri_comparison_mode_ce; +extern zend_class_entry *uri_exception_ce; +extern zend_class_entry *uri_invalid_uri_exception_ce; +extern zend_class_entry *uri_whatwg_invalid_url_exception_ce; +extern zend_class_entry *uri_whatwg_url_validation_error_type_ce; +extern zend_class_entry *uri_whatwg_url_validation_error_ce; +extern zend_object *uri_clone_obj_handler(zend_object *object); + +typedef enum { + URI_RECOMPOSITION_RAW_ASCII, + URI_RECOMPOSITION_RAW_UNICODE, + URI_RECOMPOSITION_NORMALIZED_ASCII, + URI_RECOMPOSITION_NORMALIZED_UNICODE, +} uri_recomposition_mode_t; + +typedef enum { + URI_COMPONENT_READ_RAW, + URI_COMPONENT_READ_NORMALIZED_ASCII, + URI_COMPONENT_READ_NORMALIZED_UNICODE, +} uri_component_read_mode_t; + +struct uri_internal_t; + +typedef zend_result (*uri_read_t)(const struct uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval); + +typedef zend_result (*uri_write_t)(struct uri_internal_t *internal_uri, zval *value, zval *errors); + +typedef enum { + URI_PROPERTY_NAME_SCHEME, + URI_PROPERTY_NAME_USERNAME, + URI_PROPERTY_NAME_PASSWORD, + URI_PROPERTY_NAME_HOST, + URI_PROPERTY_NAME_PORT, + URI_PROPERTY_NAME_PATH, + URI_PROPERTY_NAME_QUERY, + URI_PROPERTY_NAME_FRAGMENT, +} uri_property_name_t; + +typedef struct uri_property_handler_t { + uri_read_t read_func; + uri_write_t write_func; +} uri_property_handler_t; + +typedef struct uri_property_handlers_t { + uri_property_handler_t scheme; + uri_property_handler_t username; + uri_property_handler_t password; + uri_property_handler_t host; + uri_property_handler_t port; + uri_property_handler_t path; + uri_property_handler_t query; + uri_property_handler_t fragment; +} uri_property_handlers_t; + +typedef struct uri_handler_t { + const char *name; + + /** + * Parse a URI string into a URI. + * + * If the URI string is valid, a URI is returned. In case of failure, NULL is + * returned. + * + * The errors by-ref parameter can contain errors that occurred during parsing. + * If the input value is NULL, or there were no errors, the errors parameter should + * not be modified. + * + * If the URI string is valid and the base_url URI is not NULL, the URI object + * is resolved against the base_url. + * + * If the silent parameter is true, a Uri\InvalidUriException instance must be thrown. + * If the parameter is false, the possible errors should be handled by the caller. + */ + void *(*parse_uri)(const zend_string *uri_str, const void *base_url, zval *errors, bool silent); + void *(*clone_uri)(void *uri); + zend_string *(*uri_to_string)(void *uri, uri_recomposition_mode_t recomposition_mode, bool exclude_fragment); + void (*free_uri)(void *uri); + + const uri_property_handlers_t property_handlers; +} uri_handler_t; + +typedef struct uri_internal_t { + const uri_handler_t *handler; + void *uri; +} uri_internal_t; + +typedef struct uri_object_t { + uri_internal_t internal; + zend_object std; +} uri_object_t; + +static inline uri_object_t *uri_object_from_obj(const zend_object *object) { + return (uri_object_t*)((char*)(object) - XtOffsetOf(uri_object_t, std)); +} + +static inline uri_internal_t *uri_internal_from_obj(const zend_object *object) { + return &(uri_object_from_obj(object)->internal); +} + +#define Z_URI_OBJECT_P(zv) uri_object_from_obj(Z_OBJ_P((zv))) +#define Z_URI_INTERNAL_P(zv) uri_internal_from_obj(Z_OBJ_P((zv))) + +#define URI_PARSER_WHATWG "Uri\\WhatWg\\Url" +#define URI_SERIALIZED_PROPERTY_NAME "uri" + +zend_result uri_handler_register(const uri_handler_t *uri_handler); +const uri_property_handler_t *uri_property_handler_from_internal_uri(const uri_internal_t *internal_uri, uri_property_name_t property_name); +void uri_read_component(INTERNAL_FUNCTION_PARAMETERS, uri_property_name_t property_name, uri_component_read_mode_t component_read_mode); +void uri_write_component_str(INTERNAL_FUNCTION_PARAMETERS, uri_property_name_t property_name); +void uri_write_component_str_or_null(INTERNAL_FUNCTION_PARAMETERS, uri_property_name_t property_name); +void uri_write_component_long_or_null(INTERNAL_FUNCTION_PARAMETERS, uri_property_name_t property_name); + +#define URI_ASSERT_INITIALIZATION(internal_uri) do { \ + ZEND_ASSERT(internal_uri != NULL && internal_uri->uri != NULL); \ +} while (0) + +#endif diff --git a/ext/uri/tests/003.phpt b/ext/uri/tests/003.phpt new file mode 100644 index 0000000000000..bcd6e417441c2 --- /dev/null +++ b/ext/uri/tests/003.phpt @@ -0,0 +1,32 @@ +--TEST-- +Parse URL exotic URLs +--EXTENSIONS-- +uri +--FILE-- + +--EXPECTF-- +object(Uri\WhatWg\Url)#%d (%d) { + ["scheme"]=> + string(4) "http" + ["username"]=> + string(8) "username" + ["password"]=> + string(8) "password" + ["host"]=> + string(18) "xn--hostname-b1aaa" + ["port"]=> + int(9090) + ["path"]=> + string(5) "/path" + ["query"]=> + string(14) "arg=va%C3%A9ue" + ["fragment"]=> + string(6) "anchor" +} +NULL diff --git a/ext/uri/tests/004.phpt b/ext/uri/tests/004.phpt new file mode 100644 index 0000000000000..04127a7ded0d3 --- /dev/null +++ b/ext/uri/tests/004.phpt @@ -0,0 +1,25 @@ +--TEST-- +Parse invalid URLs +--EXTENSIONS-- +uri +--FILE-- +getMessage() . "\n"; +} + +var_dump(Uri\WhatWg\Url::parse("")); + +var_dump(Uri\WhatWg\Url::parse("192.168/contact.html", null)); + +var_dump(Uri\WhatWg\Url::parse("http://RuPaul's Drag Race All Stars 7 Winners Cast on This Season's", null)); + +?> +--EXPECTF-- +URL parsing failed +NULL +NULL +NULL diff --git a/ext/uri/tests/005.phpt b/ext/uri/tests/005.phpt new file mode 100644 index 0000000000000..262d43a75406b --- /dev/null +++ b/ext/uri/tests/005.phpt @@ -0,0 +1,38 @@ +--TEST-- +Parse multibyte URLs +--EXTENSIONS-- +uri +--FILE-- +getAsciiHost()); +var_dump($url->getUnicodeHost()); +var_dump($url->toAsciiString()); +var_dump($url->toUnicodeString()); + +?> +--EXPECTF-- +object(Uri\WhatWg\Url)#%d (%d) { + ["scheme"]=> + string(4) "http" + ["username"]=> + string(8) "username" + ["password"]=> + string(8) "password" + ["host"]=> + string(18) "xn--hostname-b1aaa" + ["port"]=> + int(9090) + ["path"]=> + string(5) "/path" + ["query"]=> + string(14) "arg=va%C3%A9ue" + ["fragment"]=> + string(6) "anchor" +} +string(18) "xn--hostname-b1aaa" +string(14) "héééostname" +string(75) "http://username:password@xn--hostname-b1aaa:9090/path?arg=va%C3%A9ue#anchor" +string(71) "http://username:password@héééostname:9090/path?arg=va%C3%A9ue#anchor" diff --git a/ext/uri/tests/006.phpt b/ext/uri/tests/006.phpt new file mode 100644 index 0000000000000..0aba3e9e46b5e --- /dev/null +++ b/ext/uri/tests/006.phpt @@ -0,0 +1,30 @@ +--TEST-- +Test successful manual Uri child instance creation +--EXTENSIONS-- +uri +--FILE-- + +--EXPECTF-- +object(Uri\WhatWg\Url)#%d (%d) { + ["scheme"]=> + string(5) "https" + ["username"]=> + string(8) "username" + ["password"]=> + string(8) "password" + ["host"]=> + string(11) "example.com" + ["port"]=> + int(8080) + ["path"]=> + string(5) "/path" + ["query"]=> + string(3) "q=r" + ["fragment"]=> + string(8) "fragment" +} diff --git a/ext/uri/tests/007.phpt b/ext/uri/tests/007.phpt new file mode 100644 index 0000000000000..e60e69fc113a3 --- /dev/null +++ b/ext/uri/tests/007.phpt @@ -0,0 +1,63 @@ +--TEST-- +Test URI creation errors +--EXTENSIONS-- +uri +--FILE-- +getMessage() . "\n"; + var_dump($e->errors); +} + +$failures = []; +$url = new Uri\WhatWg\Url(" https://example.org ", null, $failures); +var_dump($url->toAsciiString()); +var_dump($failures); + +?> +--EXPECTF-- +URL parsing failed +array(%d) { + [0]=> + object(Uri\WhatWg\UrlValidationError)#%d (%d) { + ["context"]=> + string(26) "password/path?q=r#fragment" + ["type"]=> + enum(Uri\WhatWg\UrlValidationErrorType::PortInvalid) + ["failure"]=> + bool(true) + } + [1]=> + object(Uri\WhatWg\UrlValidationError)#%d (%d) { + ["context"]=> + string(36) "@username:password/path?q=r#fragment" + ["type"]=> + enum(Uri\WhatWg\UrlValidationErrorType::InvalidCredentials) + ["failure"]=> + bool(false) + } +} +string(20) "https://example.org/" +array(2) { + [0]=> + object(Uri\WhatWg\UrlValidationError)#%d (%d) { + ["context"]=> + string(1) " " + ["type"]=> + enum(Uri\WhatWg\UrlValidationErrorType::InvalidUrlUnit) + ["failure"]=> + bool(false) + } + [1]=> + object(Uri\WhatWg\UrlValidationError)#%d (%d) { + ["context"]=> + string(21) " https://example.org " + ["type"]=> + enum(Uri\WhatWg\UrlValidationErrorType::InvalidUrlUnit) + ["failure"]=> + bool(false) + } +} diff --git a/ext/uri/tests/008.phpt b/ext/uri/tests/008.phpt new file mode 100644 index 0000000000000..f4fddcd8eb777 --- /dev/null +++ b/ext/uri/tests/008.phpt @@ -0,0 +1,34 @@ +--TEST-- +Test Uri getters +--EXTENSIONS-- +uri +--FILE-- +getScheme()); + var_dump($url->getUsername()); + var_dump($url->getPassword()); + var_dump($url->getAsciiHost()); + var_dump($url->getUnicodeHost()); + var_dump($url->getPort()); + var_dump($url->getPath()); + var_dump($url->getQuery()); + var_dump($url->getFragment()); +} + +$url = Uri\WhatWg\Url::parse("https://username:password@www.google.com:8080/pathname1/pathname2/pathname3?query=true#hash-exists"); +callWhatWgGetters($url); + +?> +--EXPECT-- +string(5) "https" +string(8) "username" +string(8) "password" +string(14) "www.google.com" +string(14) "www.google.com" +int(8080) +string(30) "/pathname1/pathname2/pathname3" +string(10) "query=true" +string(11) "hash-exists" diff --git a/ext/uri/tests/009.phpt b/ext/uri/tests/009.phpt new file mode 100644 index 0000000000000..1b279588c0167 --- /dev/null +++ b/ext/uri/tests/009.phpt @@ -0,0 +1,29 @@ +--TEST-- +Test parsing with IANA schemes +--EXTENSIONS-- +uri +--FILE-- + +--EXPECTF-- +object(Uri\WhatWg\Url)#%d (%d) { + ["scheme"]=> + string(16) "chrome-extension" + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + string(11) "example.com" + ["port"]=> + NULL + ["path"]=> + string(0) "" + ["query"]=> + NULL + ["fragment"]=> + NULL +} diff --git a/ext/uri/tests/010.phpt b/ext/uri/tests/010.phpt new file mode 100644 index 0000000000000..4ec13f652f60c --- /dev/null +++ b/ext/uri/tests/010.phpt @@ -0,0 +1,48 @@ +--TEST-- +Test parsing URIs when a base URI is present +--EXTENSIONS-- +uri +--FILE-- + +--EXPECTF-- +object(Uri\WhatWg\Url)#%d (%d) { + ["scheme"]=> + string(4) "http" + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + string(11) "example.com" + ["port"]=> + NULL + ["path"]=> + string(14) "/path/to/file1" + ["query"]=> + NULL + ["fragment"]=> + NULL +} +object(Uri\WhatWg\Url)#%d (%d) { + ["scheme"]=> + string(5) "https" + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + string(8) "test.com" + ["port"]=> + NULL + ["path"]=> + string(14) "/path/to/file1" + ["query"]=> + NULL + ["fragment"]=> + NULL +} diff --git a/ext/uri/tests/011.phpt b/ext/uri/tests/011.phpt new file mode 100644 index 0000000000000..283886fb34fbb --- /dev/null +++ b/ext/uri/tests/011.phpt @@ -0,0 +1,22 @@ +--TEST-- +Test encoding and normalization +--EXTENSIONS-- +uri +--FILE-- +toAsciiString()); +var_dump(Uri\WhatWg\Url::parse("https://www.example.com:443/dir1/../dir2")->toAsciiString()); +var_dump(Uri\WhatWg\Url::parse("https://你好你好")->toAsciiString()); +var_dump(Uri\WhatWg\Url::parse("https://你好你好")->toUnicodeString()); +var_dump(Uri\WhatWg\Url::parse("https://ï¼ï¼¸ï½ƒï¼ï¼Žï¼ï¼’5ï¼ï¼Žï¼ï¼‘")->toAsciiString()); +var_dump(Uri\WhatWg\Url::parse("HttPs://0300.0250.0000.0001/path?query=foo%20bar")->toAsciiString()); + +?> +--EXPECT-- +string(23) "http://www.example.com/" +string(28) "https://www.example.com/dir2" +string(23) "https://xn--6qqa088eba/" +string(21) "https://你好你好/" +string(20) "https://192.168.0.1/" +string(40) "https://192.168.0.1/path?query=foo%20bar" diff --git a/ext/uri/tests/012.phpt b/ext/uri/tests/012.phpt new file mode 100644 index 0000000000000..0784a74e625f0 --- /dev/null +++ b/ext/uri/tests/012.phpt @@ -0,0 +1,49 @@ +--TEST-- +Test parsing of various schemes +--EXTENSIONS-- +uri +--FILE-- + +--EXPECTF-- +object(Uri\WhatWg\Url)#%d (%d) { + ["scheme"]=> + string(6) "mailto" + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + NULL + ["port"]=> + NULL + ["path"]=> + string(15) "Joe@Example.COM" + ["query"]=> + NULL + ["fragment"]=> + NULL +} +object(Uri\WhatWg\Url)#%d (%d) { + ["scheme"]=> + string(4) "file" + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + NULL + ["port"]=> + NULL + ["path"]=> + string(30) "/E:/Documents%20and%20Settings" + ["query"]=> + NULL + ["fragment"]=> + NULL +} diff --git a/ext/uri/tests/013.phpt b/ext/uri/tests/013.phpt new file mode 100644 index 0000000000000..016fe6632782c --- /dev/null +++ b/ext/uri/tests/013.phpt @@ -0,0 +1,87 @@ +--TEST-- +Test parsing of query strings +--EXTENSIONS-- +uri +--FILE-- + + @")); + +?> +--EXPECTF-- +object(Uri\WhatWg\Url)#%d (%d) { + ["scheme"]=> + string(4) "http" + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + string(11) "example.com" + ["port"]=> + NULL + ["path"]=> + string(1) "/" + ["query"]=> + string(25) "foo=Hell%C3%B3+W%C3%B6rld" + ["fragment"]=> + NULL +} +object(Uri\WhatWg\Url)#%d (%d) { + ["scheme"]=> + string(4) "http" + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + string(11) "example.com" + ["port"]=> + NULL + ["path"]=> + string(1) "/" + ["query"]=> + string(27) "foo=Hell%C3%B3%20W%C3%B6rld" + ["fragment"]=> + NULL +} +object(Uri\WhatWg\Url)#%d (%d) { + ["scheme"]=> + string(4) "http" + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + string(11) "example.com" + ["port"]=> + NULL + ["path"]=> + string(1) "/" + ["query"]=> + string(30) "foobar=%27%3Cscript%3E+%2B+%40" + ["fragment"]=> + NULL +} +object(Uri\WhatWg\Url)#%d (%d) { + ["scheme"]=> + string(4) "http" + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + string(11) "example.com" + ["port"]=> + NULL + ["path"]=> + string(1) "/" + ["query"]=> + string(30) "foobar=%27%3Cscript%3E%20+%20@" + ["fragment"]=> + NULL +} diff --git a/ext/uri/tests/014.phpt b/ext/uri/tests/014.phpt new file mode 100644 index 0000000000000..224b0c9b64fb4 --- /dev/null +++ b/ext/uri/tests/014.phpt @@ -0,0 +1,12 @@ +--TEST-- +Test recomposition of URIs +--EXTENSIONS-- +uri +--FILE-- +toAsciiString()); + +?> +--EXPECT-- +string(45) "http://example.com/?foo=Hell%C3%B3+W%C3%B6rld" diff --git a/ext/uri/tests/015.phpt b/ext/uri/tests/015.phpt new file mode 100644 index 0000000000000..4df353e942186 --- /dev/null +++ b/ext/uri/tests/015.phpt @@ -0,0 +1,18 @@ +--TEST-- +Test instantiation without calling constructor +--EXTENSIONS-- +reflection +uri +--FILE-- +newInstanceWithoutConstructor(); +} catch (ReflectionException $e) { + echo $e->getMessage() . "\n"; +} + +?> +--EXPECT-- +Class Uri\WhatWg\Url is an internal class marked as final that cannot be instantiated without invoking its constructor diff --git a/ext/uri/tests/018.phpt b/ext/uri/tests/018.phpt new file mode 100644 index 0000000000000..bf8caffb5e7ec --- /dev/null +++ b/ext/uri/tests/018.phpt @@ -0,0 +1,50 @@ +--TEST-- +Test property mutation +--EXTENSIONS-- +uri +--FILE-- + +--EXPECTF-- +object(Uri\WhatWg\Url)#1 (%d) { + ["scheme"]=> + string(5) "https" + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + string(11) "example.com" + ["port"]=> + NULL + ["path"]=> + string(1) "/" + ["query"]=> + NULL + ["fragment"]=> + NULL +} +object(Uri\WhatWg\Url)#2 (%d) { + ["scheme"]=> + string(5) "https" + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + string(11) "example.com" + ["port"]=> + NULL + ["path"]=> + string(1) "/" + ["query"]=> + NULL + ["fragment"]=> + NULL +} diff --git a/ext/uri/tests/019.phpt b/ext/uri/tests/019.phpt new file mode 100644 index 0000000000000..df19fb14196ac --- /dev/null +++ b/ext/uri/tests/019.phpt @@ -0,0 +1,55 @@ +--TEST-- +Test IDNA support +--EXTENSIONS-- +uri +--FILE-- +getAsciiHost()); +var_dump($url->getUnicodeHost()); +var_dump($url->toAsciiString()); +var_dump($url->toUnicodeString()); + +?> +--EXPECTF-- +NULL +array(1) { + [0]=> + object(Uri\WhatWg\UrlValidationError)#%d (%d) { + ["context"]=> + string(4) "ðŸ˜" + ["type"]=> + enum(Uri\WhatWg\UrlValidationErrorType::MissingSchemeNonRelativeUrl) + ["failure"]=> + bool(true) + } +} +object(Uri\WhatWg\Url)#%d (%d) { + ["scheme"]=> + string(5) "https" + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + string(12) "xn--go8h.com" + ["port"]=> + NULL + ["path"]=> + string(13) "/%F0%9F%90%98" + ["query"]=> + string(25) "%F0%9F%90%98=%F0%9F%90%98" + ["fragment"]=> + NULL +} +string(12) "xn--go8h.com" +string(8) "ðŸ˜.com" +string(59) "https://xn--go8h.com/%F0%9F%90%98?%F0%9F%90%98=%F0%9F%90%98" +string(55) "https://ðŸ˜.com/%F0%9F%90%98?%F0%9F%90%98=%F0%9F%90%98" diff --git a/ext/uri/tests/022.phpt b/ext/uri/tests/022.phpt new file mode 100644 index 0000000000000..1e920c5055f81 --- /dev/null +++ b/ext/uri/tests/022.phpt @@ -0,0 +1,14 @@ +--TEST-- +Test extension of Uri\WhatWg\Url +--EXTENSIONS-- +uri +--FILE-- + +--EXPECTF-- +Fatal error: Class MyWhatWgUri cannot extend final class Uri\WhatWg\Url in %s on line %d diff --git a/ext/uri/tests/023.phpt b/ext/uri/tests/023.phpt new file mode 100644 index 0000000000000..b48e2df838eef --- /dev/null +++ b/ext/uri/tests/023.phpt @@ -0,0 +1,31 @@ +--TEST-- +Test property mutation - scheme +--EXTENSIONS-- +uri +--FILE-- +withScheme("http"); + +var_dump($url1->getScheme()); +var_dump($url2->getScheme()); + +try { + $url2->withScheme(""); +} catch (Uri\WhatWg\InvalidUrlException $e) { + echo $e->getMessage() . "\n"; +} + +try { + $url2->withScheme("http%73"); +} catch (Uri\WhatWg\InvalidUrlException $e) { + echo $e->getMessage() . "\n"; +} + +?> +--EXPECT-- +string(5) "https" +string(4) "http" +URL parsing failed +URL parsing failed diff --git a/ext/uri/tests/024.phpt b/ext/uri/tests/024.phpt new file mode 100644 index 0000000000000..907be0091d7c7 --- /dev/null +++ b/ext/uri/tests/024.phpt @@ -0,0 +1,29 @@ +--TEST-- +Test property mutation - username +--EXTENSIONS-- +uri +--FILE-- +withUsername("user"); +$url3 = $url2->withUsername(null); +$url4 = $url3->withUsername("%75s%2Fr"); // us/r +$url5 = $url4->withUsername("u:s/r"); + +$url6 = Uri\WhatWg\Url::parse("file:///foo/bar/"); +$url6 = $url6->withUsername("user"); + +var_dump($url2->getUsername()); +var_dump($url3->getUsername()); +var_dump($url4->getUsername()); +var_dump($url5->getUsername()); +var_dump($url6->getUsername()); + +?> +--EXPECT-- +string(4) "user" +NULL +string(8) "%75s%2Fr" +string(9) "u%3As%2Fr" +NULL diff --git a/ext/uri/tests/025.phpt b/ext/uri/tests/025.phpt new file mode 100644 index 0000000000000..dafc36043bcbc --- /dev/null +++ b/ext/uri/tests/025.phpt @@ -0,0 +1,29 @@ +--TEST-- +Test property mutation - password +--EXTENSIONS-- +uri +--FILE-- +withPassword("pass"); +$url3 = $url2->withPassword(null); +$url4 = $url3->withPassword("p%61ss"); +$url5 = $url4->withPassword("p:s/"); + +$url6 = Uri\WhatWg\Url::parse("file:///foo/bar/"); +$url6 = $url6->withUsername("pass"); + +var_dump($url2->getPassword()); +var_dump($url3->getPassword()); +var_dump($url4->getPassword()); +var_dump($url5->getPassword()); +var_dump($url6->getPassword()); + +?> +--EXPECT-- +string(4) "pass" +NULL +string(6) "p%61ss" +string(8) "p%3As%2F" +NULL diff --git a/ext/uri/tests/026.phpt b/ext/uri/tests/026.phpt new file mode 100644 index 0000000000000..4640ebebae52d --- /dev/null +++ b/ext/uri/tests/026.phpt @@ -0,0 +1,67 @@ +--TEST-- +Test property mutation - host +--EXTENSIONS-- +uri +--FILE-- +withHost("test.com"); +$url3 = $url2->withHost("t%65st.com"); // test.com +$url4 = $url3->withHost("test.com:8080"); + +var_dump($url1->getAsciiHost()); +var_dump($url2->getAsciiHost()); +var_dump($url3->getAsciiHost()); +var_dump($url4->getAsciiHost()); +var_dump($url4->getPort()); + +try { + $url4->withHost("t%3As%2Ft.com"); // t:s/t.com +} catch (Uri\WhatWg\InvalidUrlException $e) { + echo $e->getMessage() . "\n"; +} + +var_dump($url4->withHost("t:s/t.com")); + +try { + $url2->withHost(null); +} catch (Uri\WhatWg\InvalidUrlException $e) { + echo $e->getMessage() . "\n"; +} + +$url1 = Uri\WhatWg\Url::parse("ftp://foo.com?query=abc#foo"); +$url2 = $url1->withHost("test.com"); + +var_dump($url1->getAsciiHost()); +var_dump($url2->getAsciiHost()); + +?> +--EXPECTF-- +string(11) "example.com" +string(8) "test.com" +string(8) "test.com" +string(8) "test.com" +NULL +URL parsing failed +object(Uri\WhatWg\Url)#%d (%d) { + ["scheme"]=> + string(5) "https" + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + string(8) "test.com" + ["port"]=> + NULL + ["path"]=> + string(1) "/" + ["query"]=> + NULL + ["fragment"]=> + NULL +} +URL parsing failed +string(7) "foo.com" +string(8) "test.com" diff --git a/ext/uri/tests/027.phpt b/ext/uri/tests/027.phpt new file mode 100644 index 0000000000000..79c121dd7f383 --- /dev/null +++ b/ext/uri/tests/027.phpt @@ -0,0 +1,36 @@ +--TEST-- +Test property mutation - port +--EXTENSIONS-- +uri +--FILE-- +withPort(22); +$url3 = $url2->withPort(null); + +var_dump($url1->getPort()); +var_dump($url2->getPort()); +var_dump($url3->getPort()); + +$url1 = Uri\WhatWg\Url::parse("ftp://foo.com:443?query=abc#foo"); +$url2 = $url1->withPort(8080); + +var_dump($url1->getPort()); +var_dump($url2->getPort()); + +$url1 = Uri\WhatWg\Url::parse("file:///foo/bar"); +$url2 = $url1->withPort(80); + +var_dump($url1->getPort()); +var_dump($url2->getPort()); + +?> +--EXPECT-- +int(8080) +int(22) +NULL +int(443) +int(8080) +NULL +NULL diff --git a/ext/uri/tests/028.phpt b/ext/uri/tests/028.phpt new file mode 100644 index 0000000000000..fd565c900e02f --- /dev/null +++ b/ext/uri/tests/028.phpt @@ -0,0 +1,37 @@ +--TEST-- +Test property mutation - path +--EXTENSIONS-- +uri +--FILE-- +withPath("/foo"); +$url3 = $url2->withPath(""); +$url4 = $url3->withPath("t%65st"); +$url5 = $url4->withPath("/foo%2Fbar"); +$url6 = $url5->withPath("/#"); + +var_dump($url1->getPath()); +var_dump($url2->getPath()); +var_dump($url3->getPath()); +var_dump($url4->getPath()); +var_dump($url5->getPath()); +var_dump($url6->getPath()); + +$url1 = Uri\WhatWg\Url::parse("https://example.com/"); +$uri2 = $url1->withPath("/foo"); + +var_dump($url1->getPath()); +var_dump($url2->getPath()); + +?> +--EXPECT-- +string(9) "/foo/bar/" +string(4) "/foo" +string(1) "/" +string(7) "/t%65st" +string(10) "/foo%2Fbar" +string(4) "/%23" +string(1) "/" +string(4) "/foo" diff --git a/ext/uri/tests/029.phpt b/ext/uri/tests/029.phpt new file mode 100644 index 0000000000000..e23008a65ad6a --- /dev/null +++ b/ext/uri/tests/029.phpt @@ -0,0 +1,40 @@ +--TEST-- +Test property mutation - query +--EXTENSIONS-- +uri +--FILE-- +withQuery("?foo=baz"); +$url3 = $url2->withQuery(null); + +var_dump($url1->getQuery()); +var_dump($url2->getQuery()); +var_dump($url3->getQuery()); + +$url1 = Uri\WhatWg\Url::parse("https://example.com"); +$url2 = $url1->withQuery("?foo=bar&foo=baz"); +$url3 = $url1->withQuery("foo=bar&foo=baz"); +$url4 = $url3->withQuery("t%65st"); +$url5 = $url4->withQuery("foo=foo%26bar&baz=/qux%3D"); +$url6 = $url5->withQuery("#"); + +var_dump($url1->getQuery()); +var_dump($url2->getQuery()); +var_dump($url3->getQuery()); +var_dump($url4->getQuery()); +var_dump($url5->getQuery()); +var_dump($url6->getQuery()); + +?> +--EXPECT-- +string(7) "foo=bar" +string(7) "foo=baz" +NULL +NULL +string(15) "foo=bar&foo=baz" +string(15) "foo=bar&foo=baz" +string(6) "t%65st" +string(25) "foo=foo%26bar&baz=/qux%3D" +string(3) "%23" diff --git a/ext/uri/tests/030.phpt b/ext/uri/tests/030.phpt new file mode 100644 index 0000000000000..6bb85e6720c95 --- /dev/null +++ b/ext/uri/tests/030.phpt @@ -0,0 +1,31 @@ +--TEST-- +Test property mutation - fragment +--EXTENSIONS-- +uri +--FILE-- +withFragment("#fragment2"); +$url3 = $url2->withFragment(null); +$url4 = $url3->withFragment(" "); + +var_dump($url1->getFragment()); +var_dump($url2->getFragment()); +var_dump($url3->getFragment()); +var_dump($url4->getFragment()); + +$url1 = Uri\WhatWg\Url::parse("https://example.com?abc=def"); +$url2 = $url1->withFragment("#fragment"); + +var_dump($url1->getFragment()); +var_dump($url2->getFragment()); + +?> +--EXPECT-- +string(9) "fragment1" +string(9) "fragment2" +NULL +string(3) "%20" +NULL +string(8) "fragment" diff --git a/ext/uri/tests/031.phpt b/ext/uri/tests/031.phpt new file mode 100644 index 0000000000000..0572a4ec11fe6 --- /dev/null +++ b/ext/uri/tests/031.phpt @@ -0,0 +1,98 @@ +--TEST-- +Test serialization and unserialization +--EXTENSIONS-- +uri +--FILE-- +getMessage() . "\n"; +} + +try { + unserialize('O:14:"Uri\WhatWg\Url":3:{i:0;a:0:{}i:1;a:0:{}i:2;a:0:{}}'); // more than 2 items +} catch (Exception $e) { + echo $e->getMessage() . "\n"; +} + +try { + unserialize('O:14:"Uri\WhatWg\Url":2:{i:0;N;i:1;a:0:{}}'); // first item is not an array +} catch (Exception $e) { + echo $e->getMessage() . "\n"; +} + +try { + unserialize('O:14:"Uri\WhatWg\Url":2:{i:0;a:0:{}i:1;a:0:{}}'); // first array is empty +} catch (Exception $e) { + echo $e->getMessage() . "\n"; +} + +try { + unserialize('O:14:"Uri\WhatWg\Url":2:{i:0;a:2:{s:3:"uri";s:19:"https://example.com";s:1:"a";i:1;}i:1;a:0:{}}'); // "uri" key in first array contains more than 1 item +} catch (Exception $e) { + echo $e->getMessage() . "\n"; +} + +try { + unserialize('O:14:"Uri\WhatWg\Url":2:{i:0;a:1:{s:3:"uri";i:1;}i:1;a:0:{}}'); // "uri" key in first array is not a string +} catch (Exception $e) { + echo $e->getMessage() . "\n"; +} + +try { + unserialize('O:14:"Uri\WhatWg\Url":2:{i:0;a:1:{s:3:"uri";s:11:"invalid-url";}i:1;a:0:{}}'); // "uri" key in first array contains invalid URL +} catch (Exception $e) { + echo $e->getMessage() . "\n"; +} + +try { + unserialize('O:14:"Uri\WhatWg\Url":2:{i:0;a:1:{s:3:"uri";s:19:"https://example.com";}i:1;s:0:"";}'); // second item in not an array +} catch (Exception $e) { + echo $e->getMessage() . "\n"; +} + +try { + unserialize('O:14:"Uri\WhatWg\Url":2:{i:0;a:1:{s:3:"uri";s:19:"https://example.com";}i:1;a:1:{s:5:"prop1";i:123;}}'); // second array contains property +} catch (Exception $e) { + echo $e->getMessage() . "\n"; +} + +?> +--EXPECTF-- +string(162) "O:14:"Uri\WhatWg\Url":2:{i:0;a:1:{s:3:"uri";s:98:"https://username:password@www.google.com:8080/pathname1/pathname2/pathname3?query=true#hash-exists";}i:1;a:0:{}}" +object(Uri\WhatWg\Url)#%d (%d) { + ["scheme"]=> + string(5) "https" + ["username"]=> + string(8) "username" + ["password"]=> + string(8) "password" + ["host"]=> + string(14) "www.google.com" + ["port"]=> + int(8080) + ["path"]=> + string(30) "/pathname1/pathname2/pathname3" + ["query"]=> + string(10) "query=true" + ["fragment"]=> + string(11) "hash-exists" +} +Invalid serialization data for Uri\WhatWg\Url object +Invalid serialization data for Uri\WhatWg\Url object +Invalid serialization data for Uri\WhatWg\Url object +Invalid serialization data for Uri\WhatWg\Url object +Invalid serialization data for Uri\WhatWg\Url object +Invalid serialization data for Uri\WhatWg\Url object +Invalid serialization data for Uri\WhatWg\Url object +Invalid serialization data for Uri\WhatWg\Url object +Invalid serialization data for Uri\WhatWg\Url object diff --git a/ext/uri/tests/032.phpt b/ext/uri/tests/032.phpt new file mode 100644 index 0000000000000..93bb80bcdb72a --- /dev/null +++ b/ext/uri/tests/032.phpt @@ -0,0 +1,13 @@ +--TEST-- +Test JSON encoding +--EXTENSIONS-- +uri +--FILE-- + +--EXPECT-- +string(2) "{}" diff --git a/ext/uri/tests/033.phpt b/ext/uri/tests/033.phpt new file mode 100644 index 0000000000000..5b74af9b74f74 --- /dev/null +++ b/ext/uri/tests/033.phpt @@ -0,0 +1,15 @@ +--TEST-- +Test var_export +--EXTENSIONS-- +uri +--FILE-- + +--EXPECT-- +\Uri\WhatWg\Url::__set_state(array( +)) diff --git a/ext/uri/tests/034.phpt b/ext/uri/tests/034.phpt new file mode 100644 index 0000000000000..ccb0d9f6e5347 --- /dev/null +++ b/ext/uri/tests/034.phpt @@ -0,0 +1,14 @@ +--TEST-- +Test array cast +--EXTENSIONS-- +uri +--FILE-- + +--EXPECTF-- +array(%d) { +} diff --git a/ext/uri/tests/035.phpt b/ext/uri/tests/035.phpt new file mode 100644 index 0000000000000..6760e5dc0fb7a --- /dev/null +++ b/ext/uri/tests/035.phpt @@ -0,0 +1,24 @@ +--TEST-- +Test URI parsing containing null bytes +--EXTENSIONS-- +uri +--FILE-- +getMessage() . "\n"; +} + +$url = new Uri\WhatWg\Url("https://example.com"); +try { + $url->withHost("exam\0ple.com"); +} catch (Error $e) { + echo $e->getMessage() . "\n"; +} + +?> +--EXPECT-- +Uri\WhatWg\Url::__construct(): Argument #1 ($uri) must not contain any null bytes +Uri\WhatWg\Url::withHost(): Argument #1 ($host) must not contain any null bytes diff --git a/ext/uri/tests/036.phpt b/ext/uri/tests/036.phpt new file mode 100644 index 0000000000000..adc4041db5506 --- /dev/null +++ b/ext/uri/tests/036.phpt @@ -0,0 +1,24 @@ +--TEST-- +Test URI equality checks +--EXTENSIONS-- +uri +--FILE-- +equals(new Uri\WhatWg\Url("https://example.com"))); // true: identical URIs +var_dump(new Uri\WhatWg\Url("https://example.com#foo")->equals(new Uri\WhatWg\Url("https://example.com#bar"), Uri\UriComparisonMode::ExcludeFragment)); // true: fragment differs, but fragment is excluded +var_dump(new Uri\WhatWg\Url("https://example.com#foo")->equals(new Uri\WhatWg\Url("https://example.com#bar"), Uri\UriComparisonMode::IncludeFragment)); // false: fragment differs and fragment is included +var_dump(new Uri\WhatWg\Url("https://example.com/foo/..")->equals(new Uri\WhatWg\Url("https://example.com"))); // true: both URIs are https://example.com/ after normalization +var_dump(new Uri\WhatWg\Url("https://example.com/foo/..")->equals(new Uri\WhatWg\Url("https://example.com/"))); // true: both URIs are https://example.com/ after normalization +var_dump(new Uri\WhatWg\Url("http://example%2ecom/foo%2fb%61r")->equals(new Uri\WhatWg\Url("http://example%2ecom/foo/bar"))); // false: WHATWG doesn't percent-decode the path during normalization +var_dump(new Uri\WhatWg\Url("http://example%2ecom/foo/b%61r")->equals(new Uri\WhatWg\Url("http://example.com/foo/b%61r"))); // true: WHATWG percent-decodes the host during normalization + +?> +--EXPECT-- +bool(true) +bool(true) +bool(false) +bool(true) +bool(true) +bool(false) +bool(true) diff --git a/ext/uri/tests/038.phpt b/ext/uri/tests/038.phpt new file mode 100644 index 0000000000000..06171b258d6f8 --- /dev/null +++ b/ext/uri/tests/038.phpt @@ -0,0 +1,26 @@ +--TEST-- +Test toString() +--EXTENSIONS-- +uri +--FILE-- +toUnicodeString()); +var_dump($url1->toAsciiString()); +var_dump($url2->toUnicodeString()); +var_dump($url2->toAsciiString()); +var_dump($url3->toUnicodeString()); +var_dump($url3->toAsciiString()); + +?> +--EXPECT-- +string(20) "https://example.com/" +string(20) "https://example.com/" +string(20) "https://example.com/" +string(20) "https://example.com/" +string(20) "https://example.com/" +string(20) "https://example.com/" diff --git a/ext/uri/tests/039.phpt b/ext/uri/tests/039.phpt new file mode 100644 index 0000000000000..6bf57cde97d95 --- /dev/null +++ b/ext/uri/tests/039.phpt @@ -0,0 +1,34 @@ +--TEST-- +Test percent-encoding of different URI components +--EXTENSIONS-- +uri +--FILE-- +getScheme()); + var_dump($url->getUsername()); + var_dump($url->getPassword()); + var_dump($url->getAsciiHost()); + var_dump($url->getUnicodeHost()); + var_dump($url->getPort()); + var_dump($url->getPath()); + var_dump($url->getQuery()); + var_dump($url->getFragment()); +} + +$url = Uri\WhatWg\Url::parse("http://%61pple:p%61ss@ex%61mple.com/foob%61r?%61bc=%61bc#%61bc"); +callWhatWgGetters($url); + +?> +--EXPECT-- +string(4) "http" +string(7) "%61pple" +string(6) "p%61ss" +string(11) "example.com" +string(11) "example.com" +NULL +string(9) "/foob%61r" +string(11) "%61bc=%61bc" +string(5) "%61bc" diff --git a/ext/uri/tests/040.phpt b/ext/uri/tests/040.phpt new file mode 100644 index 0000000000000..6bd66fd396f21 --- /dev/null +++ b/ext/uri/tests/040.phpt @@ -0,0 +1,32 @@ +--TEST-- +Test HTTP URL validation +--EXTENSIONS-- +uri +--FILE-- +toAsciiString()); + +?> +--EXPECTF-- +object(Uri\WhatWg\Url)#%d (%d) { + ["scheme"]=> + string(5) "https" + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + string(11) "example.com" + ["port"]=> + NULL + ["path"]=> + string(1) "/" + ["query"]=> + NULL + ["fragment"]=> + NULL +} +string(20) "https://example.com/" diff --git a/ext/uri/tests/041.phpt b/ext/uri/tests/041.phpt new file mode 100644 index 0000000000000..5cfcec2628dab --- /dev/null +++ b/ext/uri/tests/041.phpt @@ -0,0 +1,26 @@ +--TEST-- +Test relative URI parsing +--EXTENSIONS-- +uri +--FILE-- + +--EXPECTF-- +NULL +array(%d) { + [0]=> + object(Uri\WhatWg\UrlValidationError)#%d (%d) { + ["context"]=> + string(15) "?query#fragment" + ["type"]=> + enum(Uri\WhatWg\UrlValidationErrorType::MissingSchemeNonRelativeUrl) + ["failure"]=> + bool(true) + } +} diff --git a/ext/uri/tests/042.phpt b/ext/uri/tests/042.phpt new file mode 100644 index 0000000000000..caf63366e2b19 --- /dev/null +++ b/ext/uri/tests/042.phpt @@ -0,0 +1,29 @@ +--TEST-- +Test URN parsing +--EXTENSIONS-- +uri +--FILE-- + +--EXPECTF-- +object(Uri\WhatWg\Url)#%d (%d) { + ["scheme"]=> + string(3) "urn" + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + NULL + ["port"]=> + NULL + ["path"]=> + string(41) "uuid:6e8bc430-9c3a-11d9-9669-0800200c9a66" + ["query"]=> + NULL + ["fragment"]=> + NULL +} diff --git a/ext/uri/tests/043.phpt b/ext/uri/tests/043.phpt new file mode 100644 index 0000000000000..d9f17d45024c9 --- /dev/null +++ b/ext/uri/tests/043.phpt @@ -0,0 +1,71 @@ +--TEST-- +Test reference resolution +--EXTENSIONS-- +uri +--FILE-- + +--EXPECTF-- +object(Uri\WhatWg\Url)#%d (%d) { + ["scheme"]=> + string(5) "https" + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + string(11) "example.com" + ["port"]=> + NULL + ["path"]=> + string(14) "/without-base/" + ["query"]=> + NULL + ["fragment"]=> + NULL +} +object(Uri\WhatWg\Url)#%d (%d) { + ["scheme"]=> + string(5) "https" + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + string(11) "example.com" + ["port"]=> + NULL + ["path"]=> + string(10) "/with-base" + ["query"]=> + NULL + ["fragment"]=> + NULL +} +object(Uri\WhatWg\Url)#%d (%d) { + ["scheme"]=> + string(5) "https" + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + string(8) "test.com" + ["port"]=> + NULL + ["path"]=> + string(18) "/with-base-in-vain" + ["query"]=> + NULL + ["fragment"]=> + NULL +} diff --git a/ext/uri/tests/045.phpt b/ext/uri/tests/045.phpt new file mode 100644 index 0000000000000..13137d6a42b69 --- /dev/null +++ b/ext/uri/tests/045.phpt @@ -0,0 +1,16 @@ +--TEST-- +Test percent-decoding of reserved characters in the path +--EXTENSIONS-- +uri +--FILE-- +toAsciiString()); +var_dump($url->getPath()); + +?> +--EXPECT-- +string(33) "https://example.com/foo/bar%2Fbaz" +string(14) "/foo/bar%2Fbaz" diff --git a/ext/uri/tests/046.phpt b/ext/uri/tests/046.phpt new file mode 100644 index 0000000000000..cf283ad5297e5 --- /dev/null +++ b/ext/uri/tests/046.phpt @@ -0,0 +1,30 @@ +--TEST-- +Test special path variants +--EXTENSIONS-- +uri +--FILE-- +toAsciiString()); +var_dump($url->getPath()); + +$url = new Uri\Whatwg\Url("https://example.com"); + +var_dump($url->toAsciiString()); +var_dump($url->getPath()); + +$url = new Uri\Whatwg\Url("https://example.com/"); + +var_dump($url->toAsciiString()); +var_dump($url->getPath()); + +?> +--EXPECT-- +string(26) "mailto:johndoe@example.com" +string(19) "johndoe@example.com" +string(20) "https://example.com/" +string(1) "/" +string(20) "https://example.com/" +string(1) "/" diff --git a/ext/uri/tests/047.phpt b/ext/uri/tests/047.phpt new file mode 100644 index 0000000000000..4ab3a0584de79 --- /dev/null +++ b/ext/uri/tests/047.phpt @@ -0,0 +1,27 @@ +--TEST-- +Test IP addresses +--EXTENSIONS-- +uri +--FILE-- +getAsciiHost()); +var_dump($url->toAsciiString()); + +$url = new Uri\WhatWg\Url("https://[2001:0db8:0001:0000:0000:0ab9:C0A8:0102]"); +var_dump($url->getAsciiHost()); +var_dump($url->toAsciiString()); + +$url = new Uri\WhatWg\Url("https://[0:0::1]"); +var_dump($url->getAsciiHost()); +var_dump($url->toAsciiString()); + +?> +--EXPECT-- +string(11) "192.168.0.1" +string(20) "https://192.168.0.1/" +string(26) "[2001:db8:1::ab9:c0a8:102]" +string(35) "https://[2001:db8:1::ab9:c0a8:102]/" +string(5) "[::1]" +string(14) "https://[::1]/" diff --git a/ext/uri/tests/049.phpt b/ext/uri/tests/049.phpt new file mode 100644 index 0000000000000..41e6eaeea3cf9 --- /dev/null +++ b/ext/uri/tests/049.phpt @@ -0,0 +1,13 @@ +--TEST-- +Test percent-encoding normalization - special case +--EXTENSIONS-- +uri +--FILE-- +getPath()); + +?> +--EXPECT-- +string(14) "/foo/bar%2Fbaz" diff --git a/ext/uri/tests/050.phpt b/ext/uri/tests/050.phpt new file mode 100644 index 0000000000000..12af66721cf65 --- /dev/null +++ b/ext/uri/tests/050.phpt @@ -0,0 +1,16 @@ +--TEST-- +Test resolve() method - success cases +--EXTENSIONS-- +uri +--FILE-- +resolve("/foo/")->toAsciiString()); +var_dump($url->resolve("https://test.com/foo")->toAsciiString()); + +?> +--EXPECTF-- +string(24) "https://example.com/foo/" +string(20) "https://test.com/foo" diff --git a/ext/uri/tests/051.phpt b/ext/uri/tests/051.phpt new file mode 100644 index 0000000000000..5911f8767567c --- /dev/null +++ b/ext/uri/tests/051.phpt @@ -0,0 +1,35 @@ +--TEST-- +Test resolve() method - error cases +--EXTENSIONS-- +uri +--FILE-- +resolve("https://1.2.3.4.5"); +} catch (Uri\WhatWg\InvalidUrlException $e) { + echo $e->getMessage() . "\n"; +} + +$softErrors = []; + +var_dump($url->resolve(" /foo", $softErrors)->toAsciiString()); +var_dump($softErrors); + +?> +--EXPECTF-- +URL parsing failed +string(23) "https://example.com/foo" +array(%d) { + [0]=> + object(Uri\WhatWg\UrlValidationError)#%d (%d) { + ["context"]=> + string(5) " /foo" + ["type"]=> + enum(Uri\WhatWg\UrlValidationErrorType::InvalidUrlUnit) + ["failure"]=> + bool(false) + } +} diff --git a/ext/uri/tests/052.phpt b/ext/uri/tests/052.phpt new file mode 100644 index 0000000000000..af7d05b893ea5 --- /dev/null +++ b/ext/uri/tests/052.phpt @@ -0,0 +1,28 @@ +--TEST-- +Test UrlValidationError constructor error handling +--EXTENSIONS-- +uri +--FILE-- +__construct('bar', Uri\WhatWg\UrlValidationErrorType::HostMissing, false); +} catch (Error $e) { + echo $e->getMessage() . "\n"; +} + +var_dump($r); + +?> +--EXPECTF-- +Cannot modify readonly property Uri\WhatWg\UrlValidationError::$context +object(Uri\WhatWg\UrlValidationError)#%d (%d) { + ["context"]=> + string(3) "foo" + ["type"]=> + enum(Uri\WhatWg\UrlValidationErrorType::DomainInvalidCodePoint) + ["failure"]=> + bool(true) +} diff --git a/ext/uri/tests/053.phpt b/ext/uri/tests/053.phpt new file mode 100644 index 0000000000000..93ff77b15c0a5 --- /dev/null +++ b/ext/uri/tests/053.phpt @@ -0,0 +1,63 @@ +--TEST-- +Test InvalidUrlException constructor error handling +--EXTENSIONS-- +uri +--FILE-- +__construct("foo"); +} catch (Error $e) { + echo $e->getMessage() . "\n"; +} + +try { + $r->__construct("bar", []); +} catch (Error $e) { + echo $e->getMessage() . "\n"; +} + +try { + $r->__construct("baz", [], false); +} catch (Error $e) { + echo $e->getMessage() . "\n"; +} + +try { + $r->__construct("qax", [], false, null); +} catch (Error $e) { + echo $e->getMessage() . "\n"; +} + +var_dump($r->getMessage()); +var_dump($r->errors); +var_dump($r->getCode()); +var_dump($r->getPrevious()::class); + +?> +--EXPECTF-- +Cannot modify readonly property Uri\WhatWg\InvalidUrlException::$errors +Cannot modify readonly property Uri\WhatWg\InvalidUrlException::$errors +Cannot modify readonly property Uri\WhatWg\InvalidUrlException::$errors +Cannot modify readonly property Uri\WhatWg\InvalidUrlException::$errors +string(3) "qax" +array(%d) { + [%d]=> + object(Uri\WhatWg\UrlValidationError)#%d (%d) { + ["context"]=> + string(3) "abc" + ["type"]=> + enum(Uri\WhatWg\UrlValidationErrorType::DomainInvalidCodePoint) + ["failure"]=> + bool(true) + } +} +int(1) +string(9) "Exception" From 5f9a0b568b18d74391807d56afaee69e9e4f8aea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Tue, 10 Jun 2025 15:28:34 +0200 Subject: [PATCH 068/682] gen_stub: Fix undefined variable warning (#18821) > PHP Warning: Undefined variable $code in build/gen_stub.php on line 5322 Introduced in php/php-src#18735. --- build/gen_stub.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build/gen_stub.php b/build/gen_stub.php index 0e87cdd9a0b40..ff86106e8a2a4 100755 --- a/build/gen_stub.php +++ b/build/gen_stub.php @@ -5313,6 +5313,8 @@ function generateGlobalConstantAttributeInitialization( $constInfos, "", static function (ConstInfo $constInfo) use ($allConstInfos, $phpVersionIdMinimumCompatibility) { + $code = ""; + if ($constInfo->attributes === []) { return null; } From 8f3e5553f386b66ad77fb562d641a8fd8c2fcefb Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Tue, 10 Jun 2025 17:33:42 +0200 Subject: [PATCH 069/682] Use zval_try_get_string_func() in concat_function() (#18815) This allows a cheaper exception check and also does not need a release call. This shrinks concat_function() on x86-64 with GCC 15.1.1 from 3443 bytes to 3332 bytes. --- Zend/zend_operators.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/Zend/zend_operators.c b/Zend/zend_operators.c index 140734d1b9602..7e456ff68246a 100644 --- a/Zend/zend_operators.c +++ b/Zend/zend_operators.c @@ -1989,9 +1989,8 @@ ZEND_API zend_result ZEND_FASTCALL concat_function(zval *result, zval *op1, zval } } ZEND_TRY_BINARY_OBJECT_OPERATION(ZEND_CONCAT); - op1_string = zval_get_string_func(op1); - if (UNEXPECTED(EG(exception))) { - zend_string_release(op1_string); + op1_string = zval_try_get_string_func(op1); + if (UNEXPECTED(!op1_string)) { if (orig_op1 != result) { ZVAL_UNDEF(result); } @@ -2023,10 +2022,9 @@ ZEND_API zend_result ZEND_FASTCALL concat_function(zval *result, zval *op1, zval free_op1_string = true; } ZEND_TRY_BINARY_OP2_OBJECT_OPERATION(ZEND_CONCAT); - op2_string = zval_get_string_func(op2); - if (UNEXPECTED(EG(exception))) { + op2_string = zval_try_get_string_func(op2); + if (UNEXPECTED(!op2_string)) { zend_string_release(op1_string); - zend_string_release(op2_string); if (orig_op1 != result) { ZVAL_UNDEF(result); } From 594221fff2d635528c58bd6273e923e56ff53c14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Tue, 10 Jun 2025 19:15:35 +0200 Subject: [PATCH 070/682] cli: Fix tests/bug80092.phpt expectation for `PHP_BUILD_PROVIDER` (#18824) see afc5738154b8e0e7f8bcb5d6a521514bb495a0c0 see 40d88cacc1db11787aa2fde6d0ee4b6064746d94 --- sapi/cli/tests/bug80092.phpt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sapi/cli/tests/bug80092.phpt b/sapi/cli/tests/bug80092.phpt index 350b46b3f57a6..1fb2e8664cc1e 100644 --- a/sapi/cli/tests/bug80092.phpt +++ b/sapi/cli/tests/bug80092.phpt @@ -43,5 +43,5 @@ foreach (explode("\n", $output) as $line) { preloaded PHP %s Copyright (c) The PHP Group -Zend Engine %s +%AZend Engine %s %A with Zend OPcache %a From eb151e39b029ced2e40e013718d305a67b81d205 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Mon, 9 Jun 2025 14:47:02 +0200 Subject: [PATCH 071/682] Properly handle reference return value from __toString() It's possible to return a reference from __toString(), but this is not handled and results in a (confusing) error telling that the return value must be a string. Properly handle this by unwrapping the reference. Closes GH-18810. --- NEWS | 1 + ...tal_uncaught_error_reference_tostring.phpt | 19 +++++++++++++++++++ .../string_cast_reference_tostring.phpt | 17 +++++++++++++++++ Zend/zend_exceptions.c | 3 +++ Zend/zend_object_handlers.c | 4 ++++ sapi/phpdbg/phpdbg_prompt.c | 4 ++++ 6 files changed, 48 insertions(+) create mode 100644 Zend/tests/exceptions/exception_fatal_uncaught_error_reference_tostring.phpt create mode 100644 Zend/tests/type_casts/string_cast_reference_tostring.phpt diff --git a/NEWS b/NEWS index b1100f7672226..bf2b1937d7945 100644 --- a/NEWS +++ b/NEWS @@ -54,6 +54,7 @@ PHP NEWS released on bailout). (DanielEScherzer and ilutov) . Fixed AST printing for immediately invoked Closure. (Dmitrii Derepko) . Properly handle __debugInfo() returning an array reference. (nielsdos) + . Properly handle reference return value from __toString(). (nielsdos) . Added the pipe (|>) operator. (crell) - Curl: diff --git a/Zend/tests/exceptions/exception_fatal_uncaught_error_reference_tostring.phpt b/Zend/tests/exceptions/exception_fatal_uncaught_error_reference_tostring.phpt new file mode 100644 index 0000000000000..6222a0895baf2 --- /dev/null +++ b/Zend/tests/exceptions/exception_fatal_uncaught_error_reference_tostring.phpt @@ -0,0 +1,19 @@ +--TEST-- +Exception fatal uncaught error with reference __toString +--FILE-- +field; + } +} + +// Must not be caught to trigger the issue! +throw new MyException; + +?> +--EXPECTF-- +Fatal error: Uncaught my string + thrown in %s on line %d diff --git a/Zend/tests/type_casts/string_cast_reference_tostring.phpt b/Zend/tests/type_casts/string_cast_reference_tostring.phpt new file mode 100644 index 0000000000000..96b47a4fba9bc --- /dev/null +++ b/Zend/tests/type_casts/string_cast_reference_tostring.phpt @@ -0,0 +1,17 @@ +--TEST-- +String cast with reference __toString +--FILE-- +field; + } +} + +echo new MyClass; + +?> +--EXPECT-- +my string diff --git a/Zend/zend_exceptions.c b/Zend/zend_exceptions.c index ab9c815718a0d..212fe3cb7ab66 100644 --- a/Zend/zend_exceptions.c +++ b/Zend/zend_exceptions.c @@ -969,6 +969,9 @@ ZEND_API ZEND_COLD zend_result zend_exception_error(zend_object *ex, int severit zend_call_known_instance_method_with_0_params(ex->ce->__tostring, ex, &tmp); if (!EG(exception)) { + if (UNEXPECTED(Z_ISREF(tmp))) { + zend_unwrap_reference(&tmp); + } if (Z_TYPE(tmp) != IS_STRING) { zend_error(E_WARNING, "%s::__toString() must return a string", ZSTR_VAL(ce_exception->name)); } else { diff --git a/Zend/zend_object_handlers.c b/Zend/zend_object_handlers.c index f79023ade1c25..7b804e7afe95a 100644 --- a/Zend/zend_object_handlers.c +++ b/Zend/zend_object_handlers.c @@ -2440,8 +2440,12 @@ ZEND_API zend_result zend_std_cast_object_tostring(zend_object *readobj, zval *w zend_call_known_instance_method_with_0_params(ce->__tostring, readobj, &retval); zend_object_release(readobj); if (EXPECTED(Z_TYPE(retval) == IS_STRING)) { +is_string: ZVAL_COPY_VALUE(writeobj, &retval); return SUCCESS; + } else if (Z_ISREF(retval)) { + zend_unwrap_reference(&retval); + goto is_string; } zval_ptr_dtor(&retval); if (!EG(exception)) { diff --git a/sapi/phpdbg/phpdbg_prompt.c b/sapi/phpdbg/phpdbg_prompt.c index 92c139fa52abe..84bd7a076acec 100644 --- a/sapi/phpdbg/phpdbg_prompt.c +++ b/sapi/phpdbg/phpdbg_prompt.c @@ -702,6 +702,10 @@ static inline void phpdbg_handle_exception(void) /* {{{ */ EG(exception) = NULL; msg = ZSTR_EMPTY_ALLOC(); } else { + if (UNEXPECTED(Z_ISREF(tmp))) { + zend_unwrap_reference(&tmp); + } + ZEND_ASSERT(Z_TYPE(tmp) == IS_STRING); zend_update_property_string(zend_get_exception_base(ex), ex, ZEND_STRL("string"), Z_STRVAL(tmp)); zval_ptr_dtor(&tmp); msg = zval_get_string(zend_read_property_ex(zend_get_exception_base(ex), ex, ZSTR_KNOWN(ZEND_STR_STRING), /* silent */ true, &rv)); From 0cd3ebfc40c6f4aa90fad8bab05b09f274465385 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Mon, 9 Jun 2025 17:49:27 +0200 Subject: [PATCH 072/682] Fix 'phpdbg --help' segfault on shutdown with USE_ZEND_ALLOC=0 This hack not only breaks the handling of custom allocators, but also breaks if zend_alloc is compiled with USE_CUSTOM_MM. This hack is just no good, if you want leak information then use ASAN. Closes GH-18813. --- NEWS | 3 +++ sapi/phpdbg/phpdbg.c | 6 ------ 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/NEWS b/NEWS index 11fb787662c4a..a665633283d0c 100644 --- a/NEWS +++ b/NEWS @@ -39,6 +39,9 @@ PHP NEWS . Add missing filter cleanups on phar failure. (nielsdos) . Fixed bug GH-18642 (Signed integer overflow in ext/phar fseek). (nielsdos) +- PHPDBG: + . Fix 'phpdbg --help' segfault on shutdown with USE_ZEND_ALLOC=0. (nielsdos) + - PDO ODBC: . Fix memory leak if WideCharToMultiByte() fails. (nielsdos) diff --git a/sapi/phpdbg/phpdbg.c b/sapi/phpdbg/phpdbg.c index 5a4dd6acdbe2f..f27d87de84187 100644 --- a/sapi/phpdbg/phpdbg.c +++ b/sapi/phpdbg/phpdbg.c @@ -179,12 +179,6 @@ static PHP_MSHUTDOWN_FUNCTION(phpdbg) /* {{{ */ phpdbg_notice("Script ended normally"); } - /* hack to restore mm_heap->use_custom_heap in order to receive memory leak info */ - if (use_mm_wrappers) { - /* ASSUMING that mm_heap->use_custom_heap is the first element of the struct ... */ - *(int *) zend_mm_get_heap() = 0; - } - if (PHPDBG_G(buffer)) { free(PHPDBG_G(buffer)); PHPDBG_G(buffer) = NULL; From 0a95b2f30caa26cf540d48b47c623995acd1183c Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Tue, 10 Jun 2025 21:54:29 +0200 Subject: [PATCH 073/682] Fix GH-18820: Windows compilation issue: php-src\Zend\zend_exceptions.h(75): error C2122: 'message': prototype parameter in name list illegal INTERNAL_FUNCTION_PARAMETERS is defined in zend.h, but not included in zend_exceptions.h (and it shouldn't). Expand the macro to fix the compile issue. --- Zend/zend_exceptions.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Zend/zend_exceptions.h b/Zend/zend_exceptions.h index 86dc379cce871..35f6699559421 100644 --- a/Zend/zend_exceptions.h +++ b/Zend/zend_exceptions.h @@ -69,7 +69,7 @@ ZEND_API zend_object *zend_throw_error_exception(zend_class_entry *exception_ce, extern ZEND_API void (*zend_throw_exception_hook)(zend_object *ex); -ZEND_API zend_result zend_update_exception_properties(INTERNAL_FUNCTION_PARAMETERS, zend_string *message, zend_long code, zval *previous); +ZEND_API zend_result zend_update_exception_properties(zend_execute_data *execute_data, zval *return_value, zend_string *message, zend_long code, zval *previous); /* show an exception using zend_error(severity,...), severity should be E_ERROR */ ZEND_API ZEND_COLD zend_result zend_exception_error(zend_object *exception, int severity); From 559858c82249155db5c362cf6a7efcc2665414a8 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Tue, 10 Jun 2025 22:33:33 +0200 Subject: [PATCH 074/682] Improve performance of unpack() with nameless repetitions (#18803) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We can avoid creating temporary strings, and then reparsing them into numbers with zend_symtable_update() by using zend_hash_index_update() directly. For the following benchmark on an i7-4790: ```php $file = str_repeat('A', 100000); for ($i=0;$i<100;$i++) unpack('C*',$file); ``` I get: ``` Benchmark 1: ./sapi/cli/php y.php Time (mean ± σ): 85.8 ms ± 1.8 ms [User: 74.5 ms, System: 10.4 ms] Range (min … max): 83.8 ms … 92.4 ms 33 runs Benchmark 2: ./sapi/cli/php_old y.php Time (mean ± σ): 318.3 ms ± 2.7 ms [User: 306.7 ms, System: 9.9 ms] Range (min … max): 314.9 ms … 321.6 ms 10 runs Summary ./sapi/cli/php y.php ran 3.71 ± 0.08 times faster than ./sapi/cli/php_old y.php ``` On an i7-1185G7 I get: ``` Benchmark 1: ./sapi/cli/php test.php Time (mean ± σ): 60.1 ms ± 0.7 ms [User: 47.8 ms, System: 12.0 ms] Range (min … max): 59.2 ms … 63.8 ms 48 runs Warning: Statistical outliers were detected. Consider re-running this benchmark on a quiet system without any interferences from other programs. It might help to use the '--warmup' or '--prepare' options. Benchmark 2: ./sapi/cli/php_old test.php Time (mean ± σ): 220.8 ms ± 2.2 ms [User: 209.6 ms, System: 10.7 ms] Range (min … max): 218.5 ms … 224.5 ms 13 runs Summary ./sapi/cli/php test.php ran 3.67 ± 0.06 times faster than ./sapi/cli/php_old test.php ``` --- UPGRADING | 2 ++ ext/standard/pack.c | 40 +++++++++++++++++++++------------------- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/UPGRADING b/UPGRADING index 7025c9778e7ac..26c87d7aeb795 100644 --- a/UPGRADING +++ b/UPGRADING @@ -582,6 +582,8 @@ PHP 8.5 UPGRADE NOTES . Improved performance of array functions with callbacks (array_find, array_filter, array_map, usort, ...). . Improved performance of urlencode() and rawurlencode(). + . Improved unpack() performance with nameless repetitions by avoiding + creating temporary strings and reparsing them. - XMLReader: . Improved property access performance. diff --git a/ext/standard/pack.c b/ext/standard/pack.c index ec30be436741d..1dc04dab86c1a 100644 --- a/ext/standard/pack.c +++ b/ext/standard/pack.c @@ -885,12 +885,15 @@ PHP_FUNCTION(unpack) if ((inputpos + size) <= inputlen) { zend_string* real_name; + zend_long long_key = 0; zval val; - if (repetitions == 1 && namelen > 0) { + if (namelen == 0) { + real_name = NULL; + long_key = i + 1; + } else if (repetitions == 1) { /* Use a part of the formatarg argument directly as the name. */ real_name = zend_string_init_fast(name, namelen); - } else { /* Need to add the 1-based element number to the name */ char buf[MAX_LENGTH_OF_LONG + 1]; @@ -912,7 +915,6 @@ PHP_FUNCTION(unpack) size = len; ZVAL_STRINGL(&val, &input[inputpos], len); - zend_symtable_update(Z_ARRVAL_P(return_value), real_name, &val); break; } case 'A': { @@ -939,7 +941,6 @@ PHP_FUNCTION(unpack) } ZVAL_STRINGL(&val, &input[inputpos], len + 1); - zend_symtable_update(Z_ARRVAL_P(return_value), real_name, &val); break; } /* New option added for Z to remain in-line with the Perl implementation */ @@ -964,7 +965,6 @@ PHP_FUNCTION(unpack) len = s; ZVAL_STRINGL(&val, &input[inputpos], len); - zend_symtable_update(Z_ARRVAL_P(return_value), real_name, &val); break; } @@ -979,7 +979,9 @@ PHP_FUNCTION(unpack) if (size > INT_MAX / 2) { - zend_string_release(real_name); + if (real_name) { + zend_string_release_ex(real_name, false); + } zend_argument_value_error(1, "repeater must be less than or equal to %d", INT_MAX / 2); RETURN_THROWS(); } @@ -1016,7 +1018,6 @@ PHP_FUNCTION(unpack) ZSTR_VAL(buf)[len] = '\0'; ZVAL_STR(&val, buf); - zend_symtable_update(Z_ARRVAL_P(return_value), real_name, &val); break; } @@ -1026,7 +1027,6 @@ PHP_FUNCTION(unpack) zend_long v = (type == 'c') ? (int8_t) x : x; ZVAL_LONG(&val, v); - zend_symtable_update(Z_ARRVAL_P(return_value), real_name, &val); break; } @@ -1046,7 +1046,6 @@ PHP_FUNCTION(unpack) } ZVAL_LONG(&val, v); - zend_symtable_update(Z_ARRVAL_P(return_value), real_name, &val); break; } @@ -1062,7 +1061,6 @@ PHP_FUNCTION(unpack) } ZVAL_LONG(&val, v); - zend_symtable_update(Z_ARRVAL_P(return_value), real_name, &val); break; } @@ -1082,8 +1080,6 @@ PHP_FUNCTION(unpack) } ZVAL_LONG(&val, v); - zend_symtable_update(Z_ARRVAL_P(return_value), real_name, &val); - break; } @@ -1104,7 +1100,6 @@ PHP_FUNCTION(unpack) } ZVAL_LONG(&val, v); - zend_symtable_update(Z_ARRVAL_P(return_value), real_name, &val); break; } #endif @@ -1124,7 +1119,6 @@ PHP_FUNCTION(unpack) } ZVAL_DOUBLE(&val, v); - zend_symtable_update(Z_ARRVAL_P(return_value), real_name, &val); break; } @@ -1143,13 +1137,12 @@ PHP_FUNCTION(unpack) } ZVAL_DOUBLE(&val, v); - zend_symtable_update(Z_ARRVAL_P(return_value), real_name, &val); break; } case 'x': /* Do nothing with input, just skip it */ - break; + goto no_output; case 'X': if (inputpos < size) { @@ -1160,7 +1153,7 @@ PHP_FUNCTION(unpack) php_error_docref(NULL, E_WARNING, "Type %c: outside of string", type); } } - break; + goto no_output; case '@': if (repetitions <= inputlen) { @@ -1170,10 +1163,19 @@ PHP_FUNCTION(unpack) } i = repetitions - 1; /* Done, break out of for loop */ - break; + goto no_output; } - zend_string_release(real_name); + if (real_name) { + zend_symtable_update(Z_ARRVAL_P(return_value), real_name, &val); + } else { + zend_hash_index_update(Z_ARRVAL_P(return_value), long_key, &val); + } + +no_output: + if (real_name) { + zend_string_release_ex(real_name, false); + } inputpos += size; if (inputpos < 0) { From dbabbe180b157eeaac5002276667f1f56f0b4def Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Tue, 10 Jun 2025 22:35:56 +0200 Subject: [PATCH 075/682] Remove dead code from openssl_spki_new() implementation (#18752) If s is not NULL, the length can't be <= 0 because we at least append `spkac` in the string, which is non-empty. I noticed this condition because if it were actually possible to execute, then it would leak memory. --- ext/openssl/openssl.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/ext/openssl/openssl.c b/ext/openssl/openssl.c index 6518a719314fd..4d1567f56d8c2 100644 --- a/ext/openssl/openssl.c +++ b/ext/openssl/openssl.c @@ -652,10 +652,6 @@ PHP_FUNCTION(openssl_spki_new) if (spki != NULL) { NETSCAPE_SPKI_free(spki); } - - if (s && ZSTR_LEN(s) <= 0) { - RETVAL_FALSE; - } } /* }}} */ From 2a77e282f86f3f2ecfcd68a83e7dcddf92165995 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Tue, 10 Jun 2025 13:09:49 +0100 Subject: [PATCH 076/682] ext/standard/pack: Inline constant single use variables They serve no purpose and are just confusing --- ext/standard/pack.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/ext/standard/pack.c b/ext/standard/pack.c index 1dc04dab86c1a..b0ed378b49ec0 100644 --- a/ext/standard/pack.c +++ b/ext/standard/pack.c @@ -919,7 +919,6 @@ PHP_FUNCTION(unpack) } case 'A': { /* A will strip any trailing whitespace */ - char padn = '\0'; char pads = ' '; char padt = '\t'; char padc = '\r'; char padl = '\n'; zend_long len = inputlen - inputpos; /* Remaining string */ /* If size was given take minimum of len and size */ @@ -931,11 +930,11 @@ PHP_FUNCTION(unpack) /* Remove trailing white space and nulls chars from unpacked data */ while (--len >= 0) { - if (input[inputpos + len] != padn - && input[inputpos + len] != pads - && input[inputpos + len] != padt - && input[inputpos + len] != padc - && input[inputpos + len] != padl + if (input[inputpos + len] != '\0' + && input[inputpos + len] != ' ' + && input[inputpos + len] != '\t' + && input[inputpos + len] != '\r' + && input[inputpos + len] != '\n' ) break; } @@ -946,7 +945,6 @@ PHP_FUNCTION(unpack) /* New option added for Z to remain in-line with the Perl implementation */ case 'Z': { /* Z will strip everything after the first null character */ - char pad = '\0'; zend_long s, len = inputlen - inputpos; /* Remaining string */ @@ -959,7 +957,7 @@ PHP_FUNCTION(unpack) /* Remove everything after the first null */ for (s=0 ; s < len ; s++) { - if (input[inputpos + s] == pad) + if (input[inputpos + s] == '\0') break; } len = s; From e96a7f0dfb50aaab94a04276d2bf06e37ecfc62d Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Tue, 10 Jun 2025 13:14:22 +0100 Subject: [PATCH 077/682] ext/standard/pack: Remove useless casts And use char instead of widening to int for no reason --- ext/standard/pack.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/ext/standard/pack.c b/ext/standard/pack.c index b0ed378b49ec0..e6ca60d456d42 100644 --- a/ext/standard/pack.c +++ b/ext/standard/pack.c @@ -274,7 +274,7 @@ PHP_FUNCTION(pack) } /* Handle special arg '*' for all codes and check argv overflows */ - switch ((int) code) { + switch (code) { /* Never uses any args */ case 'x': case 'X': @@ -380,10 +380,10 @@ PHP_FUNCTION(pack) /* Calculate output length and upper bound while processing*/ for (i = 0; i < formatcount; i++) { - int code = (int) formatcodes[i]; + char code = formatcodes[i]; int arg = formatargs[i]; - switch ((int) code) { + switch (code) { case 'h': case 'H': INC_OUTPUTPOS((arg + (arg % 2)) / 2,1) /* 4 bit per arg */ @@ -463,10 +463,10 @@ PHP_FUNCTION(pack) /* Do actual packing */ for (i = 0; i < formatcount; i++) { - int code = (int) formatcodes[i]; + char code = formatcodes[i]; int arg = formatargs[i]; - switch ((int) code) { + switch (code) { case 'a': case 'A': case 'Z': { @@ -632,7 +632,7 @@ PHP_FUNCTION(pack) case 'd': { while (arg-- > 0) { - double v = (double) zval_get_double(&argv[currentarg++]); + double v = zval_get_double(&argv[currentarg++]); memcpy(&ZSTR_VAL(output)[outputpos], &v, sizeof(v)); outputpos += sizeof(v); } @@ -642,7 +642,7 @@ PHP_FUNCTION(pack) case 'e': { /* pack little endian double */ while (arg-- > 0) { - double v = (double) zval_get_double(&argv[currentarg++]); + double v = zval_get_double(&argv[currentarg++]); php_pack_copy_double(1, &ZSTR_VAL(output)[outputpos], v); outputpos += sizeof(v); } @@ -652,7 +652,7 @@ PHP_FUNCTION(pack) case 'E': { /* pack big endian double */ while (arg-- > 0) { - double v = (double) zval_get_double(&argv[currentarg++]); + double v = zval_get_double(&argv[currentarg++]); php_pack_copy_double(0, &ZSTR_VAL(output)[outputpos], v); outputpos += sizeof(v); } @@ -784,7 +784,7 @@ PHP_FUNCTION(unpack) if (namelen > 200) namelen = 200; - switch ((int) type) { + switch (type) { /* Never use any input */ case 'X': size = -1; @@ -902,7 +902,7 @@ PHP_FUNCTION(unpack) real_name = zend_string_concat2(name, namelen, res, digits); } - switch ((int) type) { + switch (type) { case 'a': { /* a will not strip any trailing whitespace or null padding */ zend_long len = inputlen - inputpos; /* Remaining string */ From 34e22c54bc75b7bbb1c3a2daa01e0a3d300cb6cb Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Tue, 10 Jun 2025 13:20:04 +0100 Subject: [PATCH 078/682] ext/standard/pack: Reduce scope of variable --- ext/standard/pack.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ext/standard/pack.c b/ext/standard/pack.c index e6ca60d456d42..7db7724cdb2ed 100644 --- a/ext/standard/pack.c +++ b/ext/standard/pack.c @@ -737,7 +737,6 @@ PHP_FUNCTION(unpack) while (formatlen-- > 0) { char type = *(format++); - char c; int repetitions = 1, argb; char *name; int namelen; @@ -745,7 +744,7 @@ PHP_FUNCTION(unpack) /* Handle format arguments if any */ if (formatlen > 0) { - c = *format; + char c = *format; if (c >= '0' && c <= '9') { errno = 0; From a297a44d2fbd9a32148c2373111131dce685f9bb Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Tue, 10 Jun 2025 13:24:51 +0100 Subject: [PATCH 079/682] ext/standard/pack: Remove unused header includes --- ext/standard/pack.c | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/ext/standard/pack.c b/ext/standard/pack.c index 7db7724cdb2ed..a7568544be021 100644 --- a/ext/standard/pack.c +++ b/ext/standard/pack.c @@ -16,30 +16,10 @@ #include "php.h" -#include #include #include #include -#include -#include -#ifdef PHP_WIN32 -#define O_RDONLY _O_RDONLY -#include "win32/param.h" -#else -#include -#endif #include "pack.h" -#ifdef HAVE_PWD_H -#ifdef PHP_WIN32 -#include "win32/pwd.h" -#else -#include -#endif -#endif -#include "fsock.h" -#ifdef HAVE_NETINET_IN_H -#include -#endif #define INC_OUTPUTPOS(a,b) \ if ((a) < 0 || ((INT_MAX - outputpos)/((int)b)) < (a)) { \ From def3a95b14618774e63f42829edcc2ffe026bf63 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Tue, 10 Jun 2025 15:14:07 +0100 Subject: [PATCH 080/682] [skip ci] ext/standard/pack: Fix indentation to use tabs --- ext/standard/pack.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/standard/pack.c b/ext/standard/pack.c index a7568544be021..d4c5cc1f04cfa 100644 --- a/ext/standard/pack.c +++ b/ext/standard/pack.c @@ -360,7 +360,7 @@ PHP_FUNCTION(pack) /* Calculate output length and upper bound while processing*/ for (i = 0; i < formatcount; i++) { - char code = formatcodes[i]; + char code = formatcodes[i]; int arg = formatargs[i]; switch (code) { @@ -443,7 +443,7 @@ PHP_FUNCTION(pack) /* Do actual packing */ for (i = 0; i < formatcount; i++) { - char code = formatcodes[i]; + char code = formatcodes[i]; int arg = formatargs[i]; switch (code) { From 029a78813dd57929934976ad032743155f28503e Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Thu, 12 Jun 2025 17:49:22 +0200 Subject: [PATCH 081/682] Simplify callers of zval_try_get_long() (#18830) Since 2b383848 references are handled properly by the Zend API, so we can simplify the callers by removing reference handling from there. --- ext/curl/share.c | 4 +--- ext/zip/php_zip.c | 8 +++----- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/ext/curl/share.c b/ext/curl/share.c index e5f9e3d807dc2..ba23faa46bf54 100644 --- a/ext/curl/share.c +++ b/ext/curl/share.c @@ -160,9 +160,7 @@ PHP_FUNCTION(curl_share_init_persistent) } ZEND_HASH_FOREACH_VAL(share_opts, zval *entry) { - ZVAL_DEREF(entry); - - bool failed = false; + bool failed; zend_ulong option = zval_try_get_long(entry, &failed); if (failed) { diff --git a/ext/zip/php_zip.c b/ext/zip/php_zip.c index 5272a161ae292..450c297762b87 100644 --- a/ext/zip/php_zip.c +++ b/ext/zip/php_zip.c @@ -3037,13 +3037,11 @@ static int php_zip_cancel_callback(zip_t *arch, void *ptr) /* Cancel if an exception has been thrown */ return -1; } - bool failed = false; - zval *cb_retval_ptr = &cb_retval; - ZVAL_DEREF(cb_retval_ptr); - zend_long retval = zval_try_get_long(cb_retval_ptr, &failed); + bool failed; + zend_long retval = zval_try_get_long(&cb_retval, &failed); if (failed) { zend_type_error("Return value of callback provided to ZipArchive::registerCancelCallback()" - " must be of type int, %s returned", zend_zval_value_name(cb_retval_ptr)); + " must be of type int, %s returned", zend_zval_value_name(&cb_retval)); zval_ptr_dtor(&cb_retval); return -1; } From 28a083bddcacdad9ec2b846c2b87b2a6f3204bd5 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Thu, 12 Jun 2025 17:49:38 +0200 Subject: [PATCH 082/682] Use zend_string_release_ex() in concat_function() (#18827) The strings we encounter are either interned in which case the persistent bool doesn't matter; or they're temporary as the code already assumes that anyway. This patch shrinks the function from 3332 bytes to 3173 bytes on x86-64 with GCC 15.1.1. --- Zend/zend_operators.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Zend/zend_operators.c b/Zend/zend_operators.c index 7e456ff68246a..712a6039dfb14 100644 --- a/Zend/zend_operators.c +++ b/Zend/zend_operators.c @@ -2024,7 +2024,7 @@ ZEND_API zend_result ZEND_FASTCALL concat_function(zval *result, zval *op1, zval ZEND_TRY_BINARY_OP2_OBJECT_OPERATION(ZEND_CONCAT); op2_string = zval_try_get_string_func(op2); if (UNEXPECTED(!op2_string)) { - zend_string_release(op1_string); + zend_string_release_ex(op1_string, false); if (orig_op1 != result) { ZVAL_UNDEF(result); } @@ -2069,8 +2069,8 @@ has_op2_string:; uint32_t flags = ZSTR_GET_COPYABLE_CONCAT_PROPERTIES_BOTH(op1_string, op2_string); if (UNEXPECTED(op1_len > ZSTR_MAX_LEN - op2_len)) { - if (free_op1_string) zend_string_release(op1_string); - if (free_op2_string) zend_string_release(op2_string); + if (free_op1_string) zend_string_release_ex(op1_string, false); + if (free_op2_string) zend_string_release_ex(op2_string, false); zend_throw_error(NULL, "String size overflow"); if (orig_op1 != result) { ZVAL_UNDEF(result); @@ -2093,7 +2093,7 @@ has_op2_string:; /* account for the case where result_str == op1_string == op2_string and the realloc is done */ if (op1_string == op2_string) { if (free_op2_string) { - zend_string_release(op2_string); + zend_string_release_ex(op2_string, false); free_op2_string = false; } op2_string = result_str; @@ -2112,8 +2112,8 @@ has_op2_string:; ZSTR_VAL(result_str)[result_len] = '\0'; } - if (free_op1_string) zend_string_release(op1_string); - if (free_op2_string) zend_string_release(op2_string); + if (free_op1_string) zend_string_release_ex(op1_string, false); + if (free_op2_string) zend_string_release_ex(op2_string, false); return SUCCESS; } From 42b9b2f5b36200f8675bbf2eae623d12d478f9b8 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Thu, 12 Jun 2025 18:48:43 +0200 Subject: [PATCH 083/682] [ci skip] Fix pipe optimization test wrt temps for observers --- Zend/tests/pipe_operator/optimizations.phpt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Zend/tests/pipe_operator/optimizations.phpt b/Zend/tests/pipe_operator/optimizations.phpt index afdc528337c13..5a8750845ddc8 100644 --- a/Zend/tests/pipe_operator/optimizations.phpt +++ b/Zend/tests/pipe_operator/optimizations.phpt @@ -35,7 +35,7 @@ var_dump($res1); ?> --EXPECTF-- $_main: - ; (lines=18, args=0, vars=2, tmps=2) + ; (lines=18, args=0, vars=2, tmps=%d) ; (after optimizer) ; %s:1-27 0000 V2 = NEW 0 string("Other") @@ -61,7 +61,7 @@ LIVE RANGES: 2: 0010 - 0011 (tmp/var) _test1: - ; (lines=4, args=1, vars=1, tmps=1) + ; (lines=4, args=1, vars=1, tmps=%d) ; (after optimizer) ; %s:3-5 0000 CV0($a) = RECV 1 @@ -70,7 +70,7 @@ _test1: 0003 RETURN T1 Other::foo: - ; (lines=4, args=1, vars=1, tmps=1) + ; (lines=4, args=1, vars=1, tmps=%d) ; (after optimizer) ; %s:8-10 0000 CV0($a) = RECV 1 @@ -79,7 +79,7 @@ Other::foo: 0003 RETURN T1 Other::bar: - ; (lines=4, args=1, vars=1, tmps=1) + ; (lines=4, args=1, vars=1, tmps=%d) ; (after optimizer) ; %s:12-14 0000 CV0($a) = RECV 1 From afb1c574700e035ba20a3b8ddd203471a31ba6c6 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Mon, 9 Jun 2025 17:52:21 +0200 Subject: [PATCH 084/682] Fix GH-14551: PGO build fails with xxhash Turns out that the instrumentation added for gcov can change inlining decisions of the compiler, which results in a mismatch between the profile data CFG and the actual generated CFG between compiles. There are two functions that suffer from this issue: 1. _PHP_XXH3_Init: Removing the inline hint fixes this one. In fact, always inlining this makes no sense as there's no real opportunity for specialising. It just bloats the binary and increases I$ pressure. So besides fixing this issue it's beneficial on its own to drop the attribute. 2. PHP_XXH3_128_Final: Sometimes XXH128_canonicalFromHash gets inlined and sometimes not. Make sure it gets always inlined. Closes GH-18814. --- NEWS | 3 +++ ext/hash/hash_xxhash.c | 2 +- ext/hash/xxhash/xxhash.h | 4 ++-- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/NEWS b/NEWS index a665633283d0c..d32c60625ed23 100644 --- a/NEWS +++ b/NEWS @@ -18,6 +18,9 @@ PHP NEWS - FPM: . Fixed GH-18662 (fpm_get_status segfault). (txuna) +- Hash: + . Fixed bug GH-14551 (PGO build fails with xxhash). (nielsdos) + - Intl: . Fix memory leak in intl_datetime_decompose() on failure. (nielsdos) . Fix memory leak in locale lookup on failure. (nielsdos) diff --git a/ext/hash/hash_xxhash.c b/ext/hash/hash_xxhash.c index 24da754d8835a..070bd06bff070 100644 --- a/ext/hash/hash_xxhash.c +++ b/ext/hash/hash_xxhash.c @@ -154,7 +154,7 @@ const php_hash_ops php_hash_xxh3_64_ops = { typedef XXH_errorcode (*xxh3_reset_with_secret_func_t)(XXH3_state_t*, const void*, size_t); typedef XXH_errorcode (*xxh3_reset_with_seed_func_t)(XXH3_state_t*, XXH64_hash_t); -zend_always_inline static void _PHP_XXH3_Init(PHP_XXH3_64_CTX *ctx, HashTable *args, +static void _PHP_XXH3_Init(PHP_XXH3_64_CTX *ctx, HashTable *args, xxh3_reset_with_seed_func_t func_init_seed, xxh3_reset_with_secret_func_t func_init_secret, const char* algo_name) { memset(&ctx->s, 0, sizeof ctx->s); diff --git a/ext/hash/xxhash/xxhash.h b/ext/hash/xxhash/xxhash.h index 8e816c0584ebd..5874c9a1f97b5 100644 --- a/ext/hash/xxhash/xxhash.h +++ b/ext/hash/xxhash/xxhash.h @@ -931,7 +931,7 @@ XXH_PUBLIC_API int XXH128_cmp(const void* h128_1, const void* h128_2); /******* Canonical representation *******/ typedef struct { unsigned char digest[sizeof(XXH128_hash_t)]; } XXH128_canonical_t; -XXH_PUBLIC_API void XXH128_canonicalFromHash(XXH128_canonical_t* dst, XXH128_hash_t hash); +static zend_always_inline void XXH128_canonicalFromHash(XXH128_canonical_t* dst, XXH128_hash_t hash); XXH_PUBLIC_API XXH128_hash_t XXH128_hashFromCanonical(const XXH128_canonical_t* src); @@ -5503,7 +5503,7 @@ XXH_PUBLIC_API int XXH128_cmp(const void* h128_1, const void* h128_2) /*====== Canonical representation ======*/ /*! @ingroup xxh3_family */ -XXH_PUBLIC_API void +static zend_always_inline void XXH128_canonicalFromHash(XXH128_canonical_t* dst, XXH128_hash_t hash) { XXH_STATIC_ASSERT(sizeof(XXH128_canonical_t) == sizeof(XXH128_hash_t)); From 71a254489c9c4e3f92bd4893bbf591ac4c8d67c6 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Tue, 10 Jun 2025 19:24:35 +0100 Subject: [PATCH 085/682] ext/pdo_sqlite: EXPLAIN mode support for SQL statements. available since 3.41.0 we can reprepare a statement in either explain, explain query plan or the usual prepared mode. close GH-18829 --- NEWS | 3 + UPGRADING | 3 + ext/pdo_sqlite/pdo_sqlite.stub.php | 9 + ext/pdo_sqlite/pdo_sqlite_arginfo.h | 32 +- ext/pdo_sqlite/php_pdo_sqlite_int.h | 3 +- ext/pdo_sqlite/sqlite_statement.c | 64 ++- .../pdo_sqlite_getsetattr_explain.phpt | 400 ++++++++++++++++++ 7 files changed, 511 insertions(+), 3 deletions(-) create mode 100644 ext/pdo_sqlite/tests/subclasses/pdo_sqlite_getsetattr_explain.phpt diff --git a/NEWS b/NEWS index bf2b1937d7945..4e9aa8f483b2f 100644 --- a/NEWS +++ b/NEWS @@ -148,6 +148,9 @@ PHP NEWS has a wrong return type. (David Carlier) . Added Pdo_Sqlite::ATTR_BUSY_STATEMENT constant to check if a statement is currently executing. (David Carlier) + . Added Pdo_Sqlite::ATTR_EXPLAIN_STATEMENT constant to set a statement + in either EXPLAIN_MODE_PREPARED, EXPLAIN_MODE_EXPLAIN, + EXPLAIN_MODE_EXPLAIN_QUERY_PLAN modes. (David Carlier) - PGSQL: . Added pg_close_stmt to close a prepared statement while allowing diff --git a/UPGRADING b/UPGRADING index 26c87d7aeb795..98a7ec41c3a01 100644 --- a/UPGRADING +++ b/UPGRADING @@ -198,6 +198,9 @@ PHP 8.5 UPGRADE NOTES - PDO_Sqlite: . Added class constant Pdo_Sqlite::ATTR_BUSY_STATEMENT. + . Added class constants Pdo_Sqlite::ATTR_EXPLAIN_STATEMENT, + Pdo_Sqlite::EXPLAIN_MODE_PREPARED, Pdo_Sqlite::EXPLAIN_MODE_EXPLAIN, + Pdo_Sqlite::EXPLAIN_MODE_EXPLAIN_QUERY_PLAN. - SOAP: . Enumeration cases are now dumped in __getTypes(). diff --git a/ext/pdo_sqlite/pdo_sqlite.stub.php b/ext/pdo_sqlite/pdo_sqlite.stub.php index 4cb6c14eae0a4..596029524dea0 100644 --- a/ext/pdo_sqlite/pdo_sqlite.stub.php +++ b/ext/pdo_sqlite/pdo_sqlite.stub.php @@ -36,6 +36,15 @@ class Sqlite extends \PDO /** @cvalue PDO_SQLITE_ATTR_BUSY_STATEMENT */ public const int ATTR_BUSY_STATEMENT = UNKNOWN; + /** @cvalue PDO_SQLITE_ATTR_EXPLAIN_STATEMENT */ + public const int ATTR_EXPLAIN_STATEMENT = UNKNOWN; + +#if SQLITE_VERSION_NUMBER >= 3041000 + public const int EXPLAIN_MODE_PREPARED = 0; + public const int EXPLAIN_MODE_EXPLAIN = 1; + public const int EXPLAIN_MODE_EXPLAIN_QUERY_PLAN = 2; +#endif + /** @cvalue SQLITE_OK */ public const int OK = UNKNOWN; diff --git a/ext/pdo_sqlite/pdo_sqlite_arginfo.h b/ext/pdo_sqlite/pdo_sqlite_arginfo.h index ec826bc4bbc5a..b81790fa1df1d 100644 --- a/ext/pdo_sqlite/pdo_sqlite_arginfo.h +++ b/ext/pdo_sqlite/pdo_sqlite_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: ae1e62d72c3c8290c9f39f21b583e980ea9b8eb2 */ + * Stub hash: fa489a46c586ae935036f76a992163aeb67246d3 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Pdo_Sqlite_createAggregate, 0, 3, _IS_BOOL, 0) ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 0) @@ -116,6 +116,36 @@ static zend_class_entry *register_class_Pdo_Sqlite(zend_class_entry *class_entry zend_declare_typed_class_constant(class_entry, const_ATTR_BUSY_STATEMENT_name, &const_ATTR_BUSY_STATEMENT_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release(const_ATTR_BUSY_STATEMENT_name); + zval const_ATTR_EXPLAIN_STATEMENT_value; + ZVAL_LONG(&const_ATTR_EXPLAIN_STATEMENT_value, PDO_SQLITE_ATTR_EXPLAIN_STATEMENT); + zend_string *const_ATTR_EXPLAIN_STATEMENT_name = zend_string_init_interned("ATTR_EXPLAIN_STATEMENT", sizeof("ATTR_EXPLAIN_STATEMENT") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_ATTR_EXPLAIN_STATEMENT_name, &const_ATTR_EXPLAIN_STATEMENT_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_ATTR_EXPLAIN_STATEMENT_name); +#if SQLITE_VERSION_NUMBER >= 3041000 + + zval const_EXPLAIN_MODE_PREPARED_value; + ZVAL_LONG(&const_EXPLAIN_MODE_PREPARED_value, 0); + zend_string *const_EXPLAIN_MODE_PREPARED_name = zend_string_init_interned("EXPLAIN_MODE_PREPARED", sizeof("EXPLAIN_MODE_PREPARED") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_EXPLAIN_MODE_PREPARED_name, &const_EXPLAIN_MODE_PREPARED_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_EXPLAIN_MODE_PREPARED_name); +#endif +#if SQLITE_VERSION_NUMBER >= 3041000 + + zval const_EXPLAIN_MODE_EXPLAIN_value; + ZVAL_LONG(&const_EXPLAIN_MODE_EXPLAIN_value, 1); + zend_string *const_EXPLAIN_MODE_EXPLAIN_name = zend_string_init_interned("EXPLAIN_MODE_EXPLAIN", sizeof("EXPLAIN_MODE_EXPLAIN") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_EXPLAIN_MODE_EXPLAIN_name, &const_EXPLAIN_MODE_EXPLAIN_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_EXPLAIN_MODE_EXPLAIN_name); +#endif +#if SQLITE_VERSION_NUMBER >= 3041000 + + zval const_EXPLAIN_MODE_EXPLAIN_QUERY_PLAN_value; + ZVAL_LONG(&const_EXPLAIN_MODE_EXPLAIN_QUERY_PLAN_value, 2); + zend_string *const_EXPLAIN_MODE_EXPLAIN_QUERY_PLAN_name = zend_string_init_interned("EXPLAIN_MODE_EXPLAIN_QUERY_PLAN", sizeof("EXPLAIN_MODE_EXPLAIN_QUERY_PLAN") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_EXPLAIN_MODE_EXPLAIN_QUERY_PLAN_name, &const_EXPLAIN_MODE_EXPLAIN_QUERY_PLAN_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_EXPLAIN_MODE_EXPLAIN_QUERY_PLAN_name); +#endif + zval const_OK_value; ZVAL_LONG(&const_OK_value, SQLITE_OK); zend_string *const_OK_name = zend_string_init_interned("OK", sizeof("OK") - 1, 1); diff --git a/ext/pdo_sqlite/php_pdo_sqlite_int.h b/ext/pdo_sqlite/php_pdo_sqlite_int.h index 8acb95015e79a..69ac003356b87 100644 --- a/ext/pdo_sqlite/php_pdo_sqlite_int.h +++ b/ext/pdo_sqlite/php_pdo_sqlite_int.h @@ -74,7 +74,8 @@ enum { PDO_SQLITE_ATTR_OPEN_FLAGS = PDO_ATTR_DRIVER_SPECIFIC, PDO_SQLITE_ATTR_READONLY_STATEMENT, PDO_SQLITE_ATTR_EXTENDED_RESULT_CODES, - PDO_SQLITE_ATTR_BUSY_STATEMENT + PDO_SQLITE_ATTR_BUSY_STATEMENT, + PDO_SQLITE_ATTR_EXPLAIN_STATEMENT }; typedef int pdo_sqlite_create_collation_callback(void*, int, const void*, int, const void*); diff --git a/ext/pdo_sqlite/sqlite_statement.c b/ext/pdo_sqlite/sqlite_statement.c index 64c8c8a86dd9a..e683cc5ed51bf 100644 --- a/ext/pdo_sqlite/sqlite_statement.c +++ b/ext/pdo_sqlite/sqlite_statement.c @@ -26,6 +26,11 @@ #include "php_pdo_sqlite.h" #include "php_pdo_sqlite_int.h" +#if defined(__APPLE__) +// If more than one usage, a Zend macro could be created +// around this runtime check +#include +#endif static int pdo_sqlite_stmt_dtor(pdo_stmt_t *stmt) { @@ -387,6 +392,23 @@ static int pdo_sqlite_stmt_get_attribute(pdo_stmt_t *stmt, zend_long attr, zval ZVAL_TRUE(val); } break; + case PDO_SQLITE_ATTR_EXPLAIN_STATEMENT: +#if SQLITE_VERSION_NUMBER >= 3041000 +#if defined(__APPLE__) + if (__builtin_available(macOS 14.2, *)) { +#endif + ZVAL_LONG(val, (zend_long)sqlite3_stmt_isexplain(S->stmt)); + return 1; +#if defined(__APPLE__) + } else { + zend_value_error("explain statement unsupported"); + return 0; + } +#endif +#else + zend_value_error("explain statement unsupported"); + return 0; +#endif default: return 0; } @@ -394,6 +416,46 @@ static int pdo_sqlite_stmt_get_attribute(pdo_stmt_t *stmt, zend_long attr, zval return 1; } +static int pdo_sqlite_stmt_set_attribute(pdo_stmt_t *stmt, zend_long attr, zval *zval) +{ + pdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data; + + switch (attr) { + case PDO_SQLITE_ATTR_EXPLAIN_STATEMENT: +#if SQLITE_VERSION_NUMBER >= 3041000 +#if defined(__APPLE__) + if (__builtin_available(macOS 14.2, *)) { +#endif + if (Z_TYPE_P(zval) != IS_LONG) { + zend_type_error("explain mode must be of type int, %s given", zend_zval_value_name(zval)); + return 0; + } + if (Z_LVAL_P(zval) < 0 || Z_LVAL_P(zval) > 2) { + zend_value_error("explain mode must be one of the EXPLAIN_MODE_* constants"); + return 0; + } + if (sqlite3_stmt_explain(S->stmt, (int)Z_LVAL_P(zval)) != SQLITE_OK) { + return 0; + } + + return 1; +#if defined(__APPLE__) + } else { + zend_value_error("explain statement unsupported"); + return 0; + } +#endif +#else + zend_value_error("explain statement unsupported"); + return 0; +#endif + default: + return 0; + } + + return 1; +} + const struct pdo_stmt_methods sqlite_stmt_methods = { pdo_sqlite_stmt_dtor, pdo_sqlite_stmt_execute, @@ -401,7 +463,7 @@ const struct pdo_stmt_methods sqlite_stmt_methods = { pdo_sqlite_stmt_describe, pdo_sqlite_stmt_get_col, pdo_sqlite_stmt_param_hook, - NULL, /* set_attr */ + pdo_sqlite_stmt_set_attribute, /* set_attr */ pdo_sqlite_stmt_get_attribute, /* get_attr */ pdo_sqlite_stmt_col_meta, NULL, /* next_rowset */ diff --git a/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_getsetattr_explain.phpt b/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_getsetattr_explain.phpt new file mode 100644 index 0000000000000..c91fb892477b3 --- /dev/null +++ b/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_getsetattr_explain.phpt @@ -0,0 +1,400 @@ +--TEST-- +Pdo\Sqlite::ATTR_EXPLAIN_STATEMENT usage +--EXTENSIONS-- +pdo_sqlite +--SKIPIF-- + +--FILE-- +query('CREATE TABLE test_explain (a string);'); +$stmt = $db->prepare('INSERT INTO test_explain VALUES ("first insert"), ("second_insert")'); +$stmt->setAttribute(Pdo\Sqlite::ATTR_EXPLAIN_STATEMENT, Pdo\Sqlite::EXPLAIN_MODE_EXPLAIN); +var_dump($stmt->getAttribute(Pdo\Sqlite::ATTR_EXPLAIN_STATEMENT) == Pdo\Sqlite::EXPLAIN_MODE_EXPLAIN); +$r = $stmt->execute(); +var_dump($stmt->fetchAll(PDO::FETCH_ASSOC)); +$stmts = $db->prepare('SELECT * FROM test_explain'); +$stmts->setAttribute(Pdo\Sqlite::ATTR_EXPLAIN_STATEMENT, Pdo\Sqlite::EXPLAIN_MODE_EXPLAIN_QUERY_PLAN); +var_dump($stmt->getAttribute(Pdo\Sqlite::ATTR_EXPLAIN_STATEMENT) == Pdo\Sqlite::EXPLAIN_MODE_EXPLAIN_QUERY_PLAN); +$r = $stmts->execute(); +var_dump($stmts->fetchAll(PDO::FETCH_ASSOC)); + +$stmt = $db->prepare('INSERT INTO test_explain VALUES ("first insert"), ("second_insert")'); +$stmt->setAttribute(Pdo\Sqlite::ATTR_EXPLAIN_STATEMENT, Pdo\Sqlite::EXPLAIN_MODE_PREPARED); +$stmt->execute(); +$stmts->setAttribute(Pdo\Sqlite::ATTR_EXPLAIN_STATEMENT, Pdo\Sqlite::EXPLAIN_MODE_PREPARED); +$r = $stmts->execute(); +var_dump($stmts->fetchAll(PDO::FETCH_ASSOC)); + +class Duh {} + +try { + $stmts->setAttribute(Pdo\Sqlite::ATTR_EXPLAIN_STATEMENT, "EXPLAIN"); +} catch (\TypeError $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + $stmts->setAttribute(Pdo\Sqlite::ATTR_EXPLAIN_STATEMENT, new Duh()); +} catch (\TypeError $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + $stmts->setAttribute(Pdo\Sqlite::ATTR_EXPLAIN_STATEMENT, -1); +} catch (\ValueError $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + $stmts->setAttribute(Pdo\Sqlite::ATTR_EXPLAIN_STATEMENT, 256); +} catch (\ValueError $e) { + echo $e->getMessage(), PHP_EOL; +} + +var_dump($stmts->getAttribute(Pdo\Sqlite::ATTR_EXPLAIN_STATEMENT) == Pdo\Sqlite::EXPLAIN_MODE_PREPARED); +?> +--EXPECT-- +bool(true) +array(16) { + [0]=> + array(8) { + ["addr"]=> + int(0) + ["opcode"]=> + string(4) "Init" + ["p1"]=> + int(0) + ["p2"]=> + int(14) + ["p3"]=> + int(0) + ["p4"]=> + NULL + ["p5"]=> + int(0) + ["comment"]=> + NULL + } + [1]=> + array(8) { + ["addr"]=> + int(1) + ["opcode"]=> + string(13) "InitCoroutine" + ["p1"]=> + int(3) + ["p2"]=> + int(7) + ["p3"]=> + int(2) + ["p4"]=> + NULL + ["p5"]=> + int(0) + ["comment"]=> + NULL + } + [2]=> + array(8) { + ["addr"]=> + int(2) + ["opcode"]=> + string(7) "String8" + ["p1"]=> + int(0) + ["p2"]=> + int(2) + ["p3"]=> + int(0) + ["p4"]=> + string(12) "first insert" + ["p5"]=> + int(0) + ["comment"]=> + NULL + } + [3]=> + array(8) { + ["addr"]=> + int(3) + ["opcode"]=> + string(5) "Yield" + ["p1"]=> + int(3) + ["p2"]=> + int(0) + ["p3"]=> + int(0) + ["p4"]=> + NULL + ["p5"]=> + int(0) + ["comment"]=> + NULL + } + [4]=> + array(8) { + ["addr"]=> + int(4) + ["opcode"]=> + string(7) "String8" + ["p1"]=> + int(0) + ["p2"]=> + int(2) + ["p3"]=> + int(0) + ["p4"]=> + string(13) "second_insert" + ["p5"]=> + int(0) + ["comment"]=> + NULL + } + [5]=> + array(8) { + ["addr"]=> + int(5) + ["opcode"]=> + string(5) "Yield" + ["p1"]=> + int(3) + ["p2"]=> + int(0) + ["p3"]=> + int(0) + ["p4"]=> + NULL + ["p5"]=> + int(0) + ["comment"]=> + NULL + } + [6]=> + array(8) { + ["addr"]=> + int(6) + ["opcode"]=> + string(12) "EndCoroutine" + ["p1"]=> + int(3) + ["p2"]=> + int(0) + ["p3"]=> + int(0) + ["p4"]=> + NULL + ["p5"]=> + int(0) + ["comment"]=> + NULL + } + [7]=> + array(8) { + ["addr"]=> + int(7) + ["opcode"]=> + string(9) "OpenWrite" + ["p1"]=> + int(0) + ["p2"]=> + int(2) + ["p3"]=> + int(0) + ["p4"]=> + string(1) "1" + ["p5"]=> + int(0) + ["comment"]=> + NULL + } + [8]=> + array(8) { + ["addr"]=> + int(8) + ["opcode"]=> + string(5) "Yield" + ["p1"]=> + int(3) + ["p2"]=> + int(13) + ["p3"]=> + int(0) + ["p4"]=> + NULL + ["p5"]=> + int(0) + ["comment"]=> + NULL + } + [9]=> + array(8) { + ["addr"]=> + int(9) + ["opcode"]=> + string(8) "NewRowid" + ["p1"]=> + int(0) + ["p2"]=> + int(1) + ["p3"]=> + int(0) + ["p4"]=> + NULL + ["p5"]=> + int(0) + ["comment"]=> + NULL + } + [10]=> + array(8) { + ["addr"]=> + int(10) + ["opcode"]=> + string(10) "MakeRecord" + ["p1"]=> + int(2) + ["p2"]=> + int(1) + ["p3"]=> + int(4) + ["p4"]=> + string(1) "C" + ["p5"]=> + int(0) + ["comment"]=> + NULL + } + [11]=> + array(8) { + ["addr"]=> + int(11) + ["opcode"]=> + string(6) "Insert" + ["p1"]=> + int(0) + ["p2"]=> + int(4) + ["p3"]=> + int(1) + ["p4"]=> + string(12) "test_explain" + ["p5"]=> + int(57) + ["comment"]=> + NULL + } + [12]=> + array(8) { + ["addr"]=> + int(12) + ["opcode"]=> + string(4) "Goto" + ["p1"]=> + int(0) + ["p2"]=> + int(8) + ["p3"]=> + int(0) + ["p4"]=> + NULL + ["p5"]=> + int(0) + ["comment"]=> + NULL + } + [13]=> + array(8) { + ["addr"]=> + int(13) + ["opcode"]=> + string(4) "Halt" + ["p1"]=> + int(0) + ["p2"]=> + int(0) + ["p3"]=> + int(0) + ["p4"]=> + NULL + ["p5"]=> + int(0) + ["comment"]=> + NULL + } + [14]=> + array(8) { + ["addr"]=> + int(14) + ["opcode"]=> + string(11) "Transaction" + ["p1"]=> + int(0) + ["p2"]=> + int(1) + ["p3"]=> + int(1) + ["p4"]=> + string(1) "0" + ["p5"]=> + int(1) + ["comment"]=> + NULL + } + [15]=> + array(8) { + ["addr"]=> + int(15) + ["opcode"]=> + string(4) "Goto" + ["p1"]=> + int(0) + ["p2"]=> + int(1) + ["p3"]=> + int(0) + ["p4"]=> + NULL + ["p5"]=> + int(0) + ["comment"]=> + NULL + } +} +bool(false) +array(1) { + [0]=> + array(4) { + ["id"]=> + int(2) + ["parent"]=> + int(0) + ["notused"]=> + int(0) + ["detail"]=> + string(17) "SCAN test_explain" + } +} +array(2) { + [0]=> + array(1) { + ["a"]=> + string(12) "first insert" + } + [1]=> + array(1) { + ["a"]=> + string(13) "second_insert" + } +} +explain mode must be of type int, string given +explain mode must be of type int, Duh given +explain mode must be one of the EXPLAIN_MODE_* constants +explain mode must be one of the EXPLAIN_MODE_* constants +bool(true) From 058c0348fdd9599d16a49f292613e9ba91c59fe0 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Thu, 12 Jun 2025 21:23:15 +0100 Subject: [PATCH 086/682] ext/sqlite3: adding busy() call. checks if the prepared statement had been fetched but did not complete yet. close GH-18843 --- NEWS | 4 ++++ UPGRADING | 4 ++++ ext/sqlite3/sqlite3.c | 17 +++++++++++++++++ ext/sqlite3/sqlite3.stub.php | 2 ++ ext/sqlite3/sqlite3_arginfo.h | 7 ++++++- ext/sqlite3/tests/sqlite3_stmt_busy.phpt | 24 ++++++++++++++++++++++++ 6 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 ext/sqlite3/tests/sqlite3_stmt_busy.phpt diff --git a/NEWS b/NEWS index 4e9aa8f483b2f..01978f7e89e43 100644 --- a/NEWS +++ b/NEWS @@ -240,6 +240,10 @@ PHP NEWS . Fix overall theorical overflows on zend_string buffer allocations. (David Carlier/nielsdos) +- Sqlite: + . Added Sqlite3Stmt::busy to check if a statement is still being executed. + (David Carlier) + - Standard: . Fixed crypt() tests on musl when using --with-external-libcrypt (Michael Orlitzky). diff --git a/UPGRADING b/UPGRADING index 98a7ec41c3a01..a9bb61fddd86c 100644 --- a/UPGRADING +++ b/UPGRADING @@ -393,6 +393,10 @@ PHP 8.5 UPGRADE NOTES . ReflectionConstant::getAttributes() was introduced. RFC: https://wiki.php.net/rfc/attributes-on-constants +- Sqlite: + . Sqlite3Stmt::busy to check if a statement had been fetched + but not completely. + - Standard: . Added array_first() and array_last(). RFC: https://wiki.php.net/rfc/array_first_last diff --git a/ext/sqlite3/sqlite3.c b/ext/sqlite3/sqlite3.c index 1396e693d9955..3b0811ccd7d2c 100644 --- a/ext/sqlite3/sqlite3.c +++ b/ext/sqlite3/sqlite3.c @@ -1479,6 +1479,23 @@ PHP_METHOD(SQLite3Stmt, readOnly) } /* }}} */ +PHP_METHOD(SQLite3Stmt, busy) +{ + php_sqlite3_stmt *stmt_obj; + zval *object = ZEND_THIS; + stmt_obj = Z_SQLITE3_STMT_P(object); + + ZEND_PARSE_PARAMETERS_NONE(); + + SQLITE3_CHECK_INITIALIZED(stmt_obj->db_obj, stmt_obj->initialised, SQLite3); + SQLITE3_CHECK_INITIALIZED_STMT(stmt_obj->stmt, SQLite3Stmt); + + if (sqlite3_stmt_busy(stmt_obj->stmt)) { + RETURN_TRUE; + } + RETURN_FALSE; +} + /* bind parameters to a statement before execution */ static int php_sqlite3_bind_params(php_sqlite3_stmt *stmt_obj) /* {{{ */ { diff --git a/ext/sqlite3/sqlite3.stub.php b/ext/sqlite3/sqlite3.stub.php index 3545da36acfcd..8a3d90470767a 100644 --- a/ext/sqlite3/sqlite3.stub.php +++ b/ext/sqlite3/sqlite3.stub.php @@ -272,6 +272,8 @@ public function readOnly(): bool {} /** @tentative-return-type */ public function reset(): bool {} + + public function busy(): bool {} } /** @not-serializable */ diff --git a/ext/sqlite3/sqlite3_arginfo.h b/ext/sqlite3/sqlite3_arginfo.h index 654e25edead6c..f83188841b43f 100644 --- a/ext/sqlite3/sqlite3_arginfo.h +++ b/ext/sqlite3/sqlite3_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: edf910997672a2b8d8b5c25e8a7a4ff1c135e7b1 */ + * Stub hash: 28132e0e4df61f19dc4b23a7c9f79be6b3e40a8e */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_SQLite3___construct, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, filename, IS_STRING, 0) @@ -144,6 +144,9 @@ ZEND_END_ARG_INFO() #define arginfo_class_SQLite3Stmt_reset arginfo_class_SQLite3_close +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_SQLite3Stmt_busy, 0, 0, _IS_BOOL, 0) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_INFO_EX(arginfo_class_SQLite3Result___construct, 0, 0, 0) ZEND_END_ARG_INFO() @@ -202,6 +205,7 @@ ZEND_METHOD(SQLite3Stmt, getSQL); ZEND_METHOD(SQLite3Stmt, paramCount); ZEND_METHOD(SQLite3Stmt, readOnly); ZEND_METHOD(SQLite3Stmt, reset); +ZEND_METHOD(SQLite3Stmt, busy); ZEND_METHOD(SQLite3Result, __construct); ZEND_METHOD(SQLite3Result, numColumns); ZEND_METHOD(SQLite3Result, columnName); @@ -253,6 +257,7 @@ static const zend_function_entry class_SQLite3Stmt_methods[] = { ZEND_ME(SQLite3Stmt, paramCount, arginfo_class_SQLite3Stmt_paramCount, ZEND_ACC_PUBLIC) ZEND_ME(SQLite3Stmt, readOnly, arginfo_class_SQLite3Stmt_readOnly, ZEND_ACC_PUBLIC) ZEND_ME(SQLite3Stmt, reset, arginfo_class_SQLite3Stmt_reset, ZEND_ACC_PUBLIC) + ZEND_ME(SQLite3Stmt, busy, arginfo_class_SQLite3Stmt_busy, ZEND_ACC_PUBLIC) ZEND_FE_END }; diff --git a/ext/sqlite3/tests/sqlite3_stmt_busy.phpt b/ext/sqlite3/tests/sqlite3_stmt_busy.phpt new file mode 100644 index 0000000000000..8110d374afe68 --- /dev/null +++ b/ext/sqlite3/tests/sqlite3_stmt_busy.phpt @@ -0,0 +1,24 @@ +--TEST-- +SQLite3_stmt::busy +--EXTENSIONS-- +sqlite3 +--SKIPIF-- + +--FILE-- +exec('CREATE TABLE test_busy (a string);'); +$db->exec('INSERT INTO test_busy VALUES ("interleaved"), ("statements")'); +$st = $db->prepare('SELECT a FROM test_busy'); +var_dump($st->busy()); +$r = $st->execute(); +$r->fetchArray(); +var_dump($st->busy()); +?> +--EXPECT-- +bool(false) +bool(true) From 43c18f3cfe33d5e06711a5b4a4464c7601527af6 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Tue, 10 Jun 2025 20:14:49 +0200 Subject: [PATCH 087/682] Fix GH-18823: setlocale's 2nd and 3rd argument ignores strict_types Closes GH-18828. --- NEWS | 2 ++ ext/standard/string.c | 29 +++++++++++++---- .../tests/strings/gh18823_strict.phpt | 19 +++++++++++ ext/standard/tests/strings/gh18823_weak.phpt | 32 +++++++++++++++++++ .../tests/strings/setlocale_variation4.phpt | 2 +- 5 files changed, 76 insertions(+), 8 deletions(-) create mode 100644 ext/standard/tests/strings/gh18823_strict.phpt create mode 100644 ext/standard/tests/strings/gh18823_weak.phpt diff --git a/NEWS b/NEWS index 01978f7e89e43..6c2d75d8a3070 100644 --- a/NEWS +++ b/NEWS @@ -250,6 +250,8 @@ PHP NEWS . Fixed bug GH-18062 (is_callable(func(...), callable_name: $name) for first class callables returns wrong name). (timwolla) . Added array_first() and array_last(). (nielsdos) + . Fixed bug GH-18823 (setlocale's 2nd and 3rd argument ignores strict_types). + (nielsdos) - Streams: . Fixed bug GH-16889 (stream_select() timeout useless for pipes on Windows). diff --git a/ext/standard/string.c b/ext/standard/string.c index f21c9be8a7bd2..36903b3c5c7b9 100644 --- a/ext/standard/string.c +++ b/ext/standard/string.c @@ -4926,37 +4926,52 @@ PHP_FUNCTION(setlocale) { zend_long cat; zval *args = NULL; - int num_args; + uint32_t num_args; + ALLOCA_FLAG(use_heap); ZEND_PARSE_PARAMETERS_START(2, -1) Z_PARAM_LONG(cat) Z_PARAM_VARIADIC('+', args, num_args) ZEND_PARSE_PARAMETERS_END(); + zend_string **strings = do_alloca(sizeof(zend_string *) * num_args, use_heap); + + for (uint32_t i = 0; i < num_args; i++) { + if (UNEXPECTED(Z_TYPE(args[i]) != IS_ARRAY && !zend_parse_arg_str(&args[i], &strings[i], false, i + 2))) { + zend_wrong_parameter_type_error(i + 2, Z_EXPECTED_ARRAY_OR_STRING, &args[i]); + goto out; + } + } + for (uint32_t i = 0; i < num_args; i++) { if (Z_TYPE(args[i]) == IS_ARRAY) { zval *elem; ZEND_HASH_FOREACH_VAL(Z_ARRVAL(args[i]), elem) { zend_string *result = try_setlocale_zval(cat, elem); if (EG(exception)) { - RETURN_THROWS(); + goto out; } if (result) { - RETURN_STR(result); + RETVAL_STR(result); + goto out; } } ZEND_HASH_FOREACH_END(); } else { - zend_string *result = try_setlocale_zval(cat, &args[i]); + zend_string *result = try_setlocale_str(cat, strings[i]); if (EG(exception)) { - RETURN_THROWS(); + goto out; } if (result) { - RETURN_STR(result); + RETVAL_STR(result); + goto out; } } } - RETURN_FALSE; + RETVAL_FALSE; + +out: + free_alloca(strings, use_heap); } /* }}} */ diff --git a/ext/standard/tests/strings/gh18823_strict.phpt b/ext/standard/tests/strings/gh18823_strict.phpt new file mode 100644 index 0000000000000..80b21d2093172 --- /dev/null +++ b/ext/standard/tests/strings/gh18823_strict.phpt @@ -0,0 +1,19 @@ +--TEST-- +GH-18823 (setlocale's 2nd and 3rd argument ignores strict_types) - strict mode +--FILE-- +getMessage(), "\n"; +} +try { + setlocale(LC_ALL, "0", 0); +} catch (TypeError $e) { + echo $e->getMessage(), "\n"; +} +?> +--EXPECT-- +setlocale(): Argument #2 ($locales) must be of type array|string, int given +setlocale(): Argument #3 must be of type array|string, int given diff --git a/ext/standard/tests/strings/gh18823_weak.phpt b/ext/standard/tests/strings/gh18823_weak.phpt new file mode 100644 index 0000000000000..bc9611098a8db --- /dev/null +++ b/ext/standard/tests/strings/gh18823_weak.phpt @@ -0,0 +1,32 @@ +--TEST-- +GH-18823 (setlocale's 2nd and 3rd argument ignores strict_types) - weak mode +--INI-- +error_reporting=E_ALL +--FILE-- +getMessage(), "\n"; +} +?> +--EXPECTF-- +Deprecated: setlocale(): Passing null to parameter #2 ($locales) of type string is deprecated in %s on line %d +no diff --git a/ext/standard/tests/strings/setlocale_variation4.phpt b/ext/standard/tests/strings/setlocale_variation4.phpt index ca469759dbd15..a2b236c146c8c 100644 --- a/ext/standard/tests/strings/setlocale_variation4.phpt +++ b/ext/standard/tests/strings/setlocale_variation4.phpt @@ -28,7 +28,7 @@ var_dump($locale_info_before); //Testing setlocale() by giving locale = null echo "Setting system locale, category = LC_ALL and locale = null\n"; -setlocale(LC_ALL, null); +@setlocale(LC_ALL, null); echo "Locale info, after setting the locale\n"; //Returns Current locale,after executing setlocale(). From 7361a1206d28810800d9ecf191d11b08dce7d03f Mon Sep 17 00:00:00 2001 From: David CARLIER Date: Fri, 13 Jun 2025 19:19:13 +0100 Subject: [PATCH 088/682] ext/pdo_sqlite: explain statement prefixing with its class for errors. (#18846) --- ext/pdo_sqlite/sqlite_statement.c | 2 +- .../tests/subclasses/pdo_sqlite_getsetattr_explain.phpt | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ext/pdo_sqlite/sqlite_statement.c b/ext/pdo_sqlite/sqlite_statement.c index e683cc5ed51bf..31fb98f887e04 100644 --- a/ext/pdo_sqlite/sqlite_statement.c +++ b/ext/pdo_sqlite/sqlite_statement.c @@ -431,7 +431,7 @@ static int pdo_sqlite_stmt_set_attribute(pdo_stmt_t *stmt, zend_long attr, zval return 0; } if (Z_LVAL_P(zval) < 0 || Z_LVAL_P(zval) > 2) { - zend_value_error("explain mode must be one of the EXPLAIN_MODE_* constants"); + zend_value_error("explain mode must be one of the Pdo\\Sqlite::EXPLAIN_MODE_* constants"); return 0; } if (sqlite3_stmt_explain(S->stmt, (int)Z_LVAL_P(zval)) != SQLITE_OK) { diff --git a/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_getsetattr_explain.phpt b/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_getsetattr_explain.phpt index c91fb892477b3..73a160e1d8134 100644 --- a/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_getsetattr_explain.phpt +++ b/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_getsetattr_explain.phpt @@ -395,6 +395,6 @@ array(2) { } explain mode must be of type int, string given explain mode must be of type int, Duh given -explain mode must be one of the EXPLAIN_MODE_* constants -explain mode must be one of the EXPLAIN_MODE_* constants +explain mode must be one of the Pdo\Sqlite::EXPLAIN_MODE_* constants +explain mode must be one of the Pdo\Sqlite::EXPLAIN_MODE_* constants bool(true) From 7e59769b1fb34335401033cac11e49ba95692c6c Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Sat, 14 Jun 2025 15:00:49 +0200 Subject: [PATCH 089/682] Move pipe test with opcache dump to ext/opcache This test breaks under file cache (because the opcodes are not dumped when ran with a primed cache). run-tests.php --file-cache-* automatically skips all ext/opcache tests, so move it there. --- .../opcache/tests/pipe_optimizations.phpt | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Zend/tests/pipe_operator/optimizations.phpt => ext/opcache/tests/pipe_optimizations.phpt (100%) diff --git a/Zend/tests/pipe_operator/optimizations.phpt b/ext/opcache/tests/pipe_optimizations.phpt similarity index 100% rename from Zend/tests/pipe_operator/optimizations.phpt rename to ext/opcache/tests/pipe_optimizations.phpt From 0a9697f5e1361825696f11ee72557640b172b838 Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Sat, 14 Jun 2025 15:51:18 +0200 Subject: [PATCH 090/682] Fix unused variable in pdo_sqlite_stmt_set_attribute() (GH-18851) The indentation is also wrong (using spaces instead of tabs), but this should be fixed in a separate commit. --- ext/pdo_sqlite/sqlite_statement.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/pdo_sqlite/sqlite_statement.c b/ext/pdo_sqlite/sqlite_statement.c index 31fb98f887e04..24d3f280a97a3 100644 --- a/ext/pdo_sqlite/sqlite_statement.c +++ b/ext/pdo_sqlite/sqlite_statement.c @@ -418,8 +418,6 @@ static int pdo_sqlite_stmt_get_attribute(pdo_stmt_t *stmt, zend_long attr, zval static int pdo_sqlite_stmt_set_attribute(pdo_stmt_t *stmt, zend_long attr, zval *zval) { - pdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data; - switch (attr) { case PDO_SQLITE_ATTR_EXPLAIN_STATEMENT: #if SQLITE_VERSION_NUMBER >= 3041000 @@ -434,6 +432,8 @@ static int pdo_sqlite_stmt_set_attribute(pdo_stmt_t *stmt, zend_long attr, zval zend_value_error("explain mode must be one of the Pdo\\Sqlite::EXPLAIN_MODE_* constants"); return 0; } + + pdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data; if (sqlite3_stmt_explain(S->stmt, (int)Z_LVAL_P(zval)) != SQLITE_OK) { return 0; } From 2e2494fbef842171257b0ae2b6d4392ba303f43f Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Sat, 14 Jun 2025 16:00:13 +0200 Subject: [PATCH 091/682] [skip ci] Fix whitespace in ext/pdo_sqlite/sqlite_statement.c --- ext/pdo_sqlite/sqlite_statement.c | 70 +++++++++++++++---------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/ext/pdo_sqlite/sqlite_statement.c b/ext/pdo_sqlite/sqlite_statement.c index 24d3f280a97a3..381dab82af62f 100644 --- a/ext/pdo_sqlite/sqlite_statement.c +++ b/ext/pdo_sqlite/sqlite_statement.c @@ -392,22 +392,22 @@ static int pdo_sqlite_stmt_get_attribute(pdo_stmt_t *stmt, zend_long attr, zval ZVAL_TRUE(val); } break; - case PDO_SQLITE_ATTR_EXPLAIN_STATEMENT: + case PDO_SQLITE_ATTR_EXPLAIN_STATEMENT: #if SQLITE_VERSION_NUMBER >= 3041000 #if defined(__APPLE__) - if (__builtin_available(macOS 14.2, *)) { + if (__builtin_available(macOS 14.2, *)) { #endif - ZVAL_LONG(val, (zend_long)sqlite3_stmt_isexplain(S->stmt)); - return 1; + ZVAL_LONG(val, (zend_long)sqlite3_stmt_isexplain(S->stmt)); + return 1; #if defined(__APPLE__) - } else { - zend_value_error("explain statement unsupported"); - return 0; - } + } else { + zend_value_error("explain statement unsupported"); + return 0; + } #endif #else - zend_value_error("explain statement unsupported"); - return 0; + zend_value_error("explain statement unsupported"); + return 0; #endif default: return 0; @@ -419,41 +419,41 @@ static int pdo_sqlite_stmt_get_attribute(pdo_stmt_t *stmt, zend_long attr, zval static int pdo_sqlite_stmt_set_attribute(pdo_stmt_t *stmt, zend_long attr, zval *zval) { switch (attr) { - case PDO_SQLITE_ATTR_EXPLAIN_STATEMENT: + case PDO_SQLITE_ATTR_EXPLAIN_STATEMENT: #if SQLITE_VERSION_NUMBER >= 3041000 #if defined(__APPLE__) - if (__builtin_available(macOS 14.2, *)) { + if (__builtin_available(macOS 14.2, *)) { #endif - if (Z_TYPE_P(zval) != IS_LONG) { - zend_type_error("explain mode must be of type int, %s given", zend_zval_value_name(zval)); - return 0; - } - if (Z_LVAL_P(zval) < 0 || Z_LVAL_P(zval) > 2) { - zend_value_error("explain mode must be one of the Pdo\\Sqlite::EXPLAIN_MODE_* constants"); - return 0; - } - - pdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data; - if (sqlite3_stmt_explain(S->stmt, (int)Z_LVAL_P(zval)) != SQLITE_OK) { - return 0; - } - - return 1; + if (Z_TYPE_P(zval) != IS_LONG) { + zend_type_error("explain mode must be of type int, %s given", zend_zval_value_name(zval)); + return 0; + } + if (Z_LVAL_P(zval) < 0 || Z_LVAL_P(zval) > 2) { + zend_value_error("explain mode must be one of the Pdo\\Sqlite::EXPLAIN_MODE_* constants"); + return 0; + } + + pdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data; + if (sqlite3_stmt_explain(S->stmt, (int)Z_LVAL_P(zval)) != SQLITE_OK) { + return 0; + } + + return 1; #if defined(__APPLE__) - } else { - zend_value_error("explain statement unsupported"); - return 0; - } + } else { + zend_value_error("explain statement unsupported"); + return 0; + } #endif #else - zend_value_error("explain statement unsupported"); - return 0; + zend_value_error("explain statement unsupported"); + return 0; #endif default: return 0; - } + } - return 1; + return 1; } const struct pdo_stmt_methods sqlite_stmt_methods = { From a6749046f6c5fd043919cb9090452165542ab612 Mon Sep 17 00:00:00 2001 From: Saki Takamachi Date: Tue, 17 Jun 2025 21:05:02 +0900 Subject: [PATCH 092/682] PHP-8.4 is now for PHP 8.4.10-dev --- NEWS | 5 ++++- Zend/zend.h | 2 +- configure.ac | 2 +- main/php_version.h | 6 +++--- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/NEWS b/NEWS index c8a8197d1417e..5d8334fdf78fc 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,9 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -?? ??? ????, PHP 8.4.9 +?? ??? ????, PHP 8.4.10 + + +03 Jul 2025, PHP 8.4.9 - BcMath: . Fixed bug GH-18641 (Accessing a BcMath\Number property by ref crashes). diff --git a/Zend/zend.h b/Zend/zend.h index 34a6a0258a261..15a9b3d8189aa 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -20,7 +20,7 @@ #ifndef ZEND_H #define ZEND_H -#define ZEND_VERSION "4.4.9-dev" +#define ZEND_VERSION "4.4.10-dev" #define ZEND_ENGINE_3 diff --git a/configure.ac b/configure.ac index 3662d3e985b02..c94038e6ca5db 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.4.9-dev],[https://github.com/php/php-src/issues],[php],[https://www.php.net]) +AC_INIT([PHP],[8.4.10-dev],[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/main/php_version.h b/main/php_version.h index 7bd5e8c37f895..da01e82826df1 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 4 -#define PHP_RELEASE_VERSION 9 +#define PHP_RELEASE_VERSION 10 #define PHP_EXTRA_VERSION "-dev" -#define PHP_VERSION "8.4.9-dev" -#define PHP_VERSION_ID 80409 +#define PHP_VERSION "8.4.10-dev" +#define PHP_VERSION_ID 80410 From 50606f85696053f886ebd48e1b543bd09d3be3d8 Mon Sep 17 00:00:00 2001 From: Eric Mann Date: Tue, 17 Jun 2025 08:06:35 -0700 Subject: [PATCH 093/682] PHP 8.3 is now for PHP 8.3.24-dev --- NEWS | 5 ++++- Zend/zend.h | 2 +- configure.ac | 2 +- main/php_version.h | 6 +++--- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/NEWS b/NEWS index d32c60625ed23..c29118397857d 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,9 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -?? ??? ????, PHP 8.3.23 +?? ??? ????, PHP 8.3.24 + + +03 Jul 2025, PHP 8.3.23 - Core: . Fixed GH-18695 (zend_ast_export() - float number is not preserved). diff --git a/Zend/zend.h b/Zend/zend.h index 704df32e9c145..0d2c52e2505e2 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -20,7 +20,7 @@ #ifndef ZEND_H #define ZEND_H -#define ZEND_VERSION "4.3.23-dev" +#define ZEND_VERSION "4.3.24-dev" #define ZEND_ENGINE_3 diff --git a/configure.ac b/configure.ac index 965c0bdd853ee..e96c12486fa2c 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.3.23-dev],[https://github.com/php/php-src/issues],[php],[https://www.php.net]) +AC_INIT([PHP],[8.3.24-dev],[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/main/php_version.h b/main/php_version.h index 28e4162421ded..cf74940556ddc 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 3 -#define PHP_RELEASE_VERSION 23 +#define PHP_RELEASE_VERSION 24 #define PHP_EXTRA_VERSION "-dev" -#define PHP_VERSION "8.3.23-dev" -#define PHP_VERSION_ID 80323 +#define PHP_VERSION "8.3.24-dev" +#define PHP_VERSION_ID 80324 From 5f67bace1b915ca45cabb12bf0c1dc35a58ae8bc Mon Sep 17 00:00:00 2001 From: Bogdan Ungureanu Date: Wed, 18 Jun 2025 11:21:31 +0300 Subject: [PATCH 094/682] ext/intl: Fix compile issues with ICU versions lower than 67 (#18868) --- ext/intl/listformatter/listformatter.stub.php | 10 +++++++++ .../listformatter/listformatter_arginfo.h | 22 ++++++++++++++++++- ext/intl/listformatter/listformatter_class.c | 15 ++++++++----- ext/intl/listformatter/listformatter_class.h | 3 +++ 4 files changed, 44 insertions(+), 6 deletions(-) diff --git a/ext/intl/listformatter/listformatter.stub.php b/ext/intl/listformatter/listformatter.stub.php index b16ad5c270091..c97127240b43c 100644 --- a/ext/intl/listformatter/listformatter.stub.php +++ b/ext/intl/listformatter/listformatter.stub.php @@ -8,8 +8,13 @@ */ final class IntlListFormatter { +#if U_ICU_VERSION_MAJOR_NUM >= 67 /** @cvalue ULISTFMT_TYPE_AND */ public const int TYPE_AND = UNKNOWN; +#else + /** @cvalue INTL_LISTFORMATTER_FALLBACK_TYPE_AND */ + public const int TYPE_AND = UNKNOWN; +#endif #if U_ICU_VERSION_MAJOR_NUM >= 67 /** @cvalue ULISTFMT_TYPE_OR */ @@ -19,8 +24,13 @@ final class IntlListFormatter { public const int TYPE_UNITS = UNKNOWN; #endif +#if U_ICU_VERSION_MAJOR_NUM >= 67 /** @cvalue ULISTFMT_WIDTH_WIDE */ public const int WIDTH_WIDE = UNKNOWN; +#else + /** @cvalue INTL_LISTFORMATTER_FALLBACK_WIDTH_WIDE */ + public const int WIDTH_WIDE = UNKNOWN; +#endif #if U_ICU_VERSION_MAJOR_NUM >= 67 /** @cvalue ULISTFMT_WIDTH_SHORT */ diff --git a/ext/intl/listformatter/listformatter_arginfo.h b/ext/intl/listformatter/listformatter_arginfo.h index 3e18c1154ae76..d9a4c3fb84ddc 100644 --- a/ext/intl/listformatter/listformatter_arginfo.h +++ b/ext/intl/listformatter/listformatter_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: f64f4171cfe4f66f976b9350b0a0e22269301ce5 */ + * Stub hash: cdbbdb55d1e53f422c5854460c3c6cc3d01360d7 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_IntlListFormatter___construct, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, locale, IS_STRING, 0) @@ -36,12 +36,22 @@ static zend_class_entry *register_class_IntlListFormatter(void) INIT_CLASS_ENTRY(ce, "IntlListFormatter", class_IntlListFormatter_methods); class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_NOT_SERIALIZABLE); +#if U_ICU_VERSION_MAJOR_NUM >= 67 zval const_TYPE_AND_value; ZVAL_LONG(&const_TYPE_AND_value, ULISTFMT_TYPE_AND); zend_string *const_TYPE_AND_name = zend_string_init_interned("TYPE_AND", sizeof("TYPE_AND") - 1, 1); zend_declare_typed_class_constant(class_entry, const_TYPE_AND_name, &const_TYPE_AND_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release(const_TYPE_AND_name); +#endif +#if !(U_ICU_VERSION_MAJOR_NUM >= 67) + + zval const_TYPE_AND_value; + ZVAL_LONG(&const_TYPE_AND_value, INTL_LISTFORMATTER_FALLBACK_TYPE_AND); + zend_string *const_TYPE_AND_name = zend_string_init_interned("TYPE_AND", sizeof("TYPE_AND") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_TYPE_AND_name, &const_TYPE_AND_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_TYPE_AND_name); +#endif #if U_ICU_VERSION_MAJOR_NUM >= 67 zval const_TYPE_OR_value; @@ -58,12 +68,22 @@ static zend_class_entry *register_class_IntlListFormatter(void) zend_declare_typed_class_constant(class_entry, const_TYPE_UNITS_name, &const_TYPE_UNITS_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release(const_TYPE_UNITS_name); #endif +#if U_ICU_VERSION_MAJOR_NUM >= 67 zval const_WIDTH_WIDE_value; ZVAL_LONG(&const_WIDTH_WIDE_value, ULISTFMT_WIDTH_WIDE); zend_string *const_WIDTH_WIDE_name = zend_string_init_interned("WIDTH_WIDE", sizeof("WIDTH_WIDE") - 1, 1); zend_declare_typed_class_constant(class_entry, const_WIDTH_WIDE_name, &const_WIDTH_WIDE_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release(const_WIDTH_WIDE_name); +#endif +#if !(U_ICU_VERSION_MAJOR_NUM >= 67) + + zval const_WIDTH_WIDE_value; + ZVAL_LONG(&const_WIDTH_WIDE_value, INTL_LISTFORMATTER_FALLBACK_WIDTH_WIDE); + zend_string *const_WIDTH_WIDE_name = zend_string_init_interned("WIDTH_WIDE", sizeof("WIDTH_WIDE") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_WIDTH_WIDE_name, &const_WIDTH_WIDE_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_WIDTH_WIDE_name); +#endif #if U_ICU_VERSION_MAJOR_NUM >= 67 zval const_WIDTH_SHORT_value; diff --git a/ext/intl/listformatter/listformatter_class.c b/ext/intl/listformatter/listformatter_class.c index 522ecdd371357..1fe8da554a1ca 100644 --- a/ext/intl/listformatter/listformatter_class.c +++ b/ext/intl/listformatter/listformatter_class.c @@ -15,8 +15,8 @@ #include "php.h" #include "php_intl.h" #include -#include "listformatter_arginfo.h" #include "listformatter_class.h" +#include "listformatter_arginfo.h" #include "intl_convert.h" static zend_object_handlers listformatter_handlers; @@ -53,8 +53,13 @@ PHP_METHOD(IntlListFormatter, __construct) ListFormatter_object *obj = Z_INTL_LISTFORMATTER_P(ZEND_THIS); char* locale; size_t locale_len = 0; - zend_long type = ULISTFMT_TYPE_AND; - zend_long width = ULISTFMT_WIDTH_WIDE; + #if U_ICU_VERSION_MAJOR_NUM >= 67 + zend_long type = ULISTFMT_TYPE_AND; + zend_long width = ULISTFMT_WIDTH_WIDE; + #else + zend_long type = INTL_LISTFORMATTER_FALLBACK_TYPE_AND; + zend_long width = INTL_LISTFORMATTER_FALLBACK_WIDTH_WIDE; + #endif ZEND_PARSE_PARAMETERS_START(1, 3) Z_PARAM_STRING(locale, locale_len) Z_PARAM_OPTIONAL @@ -90,12 +95,12 @@ PHP_METHOD(IntlListFormatter, __construct) LISTFORMATTER_OBJECT(obj) = ulistfmt_openForType(locale, type, width, &status); #else - if (type != ULISTFMT_TYPE_AND) { + if (type != INTL_LISTFORMATTER_FALLBACK_TYPE_AND) { zend_argument_value_error(2, "contains an unsupported type. ICU 66 and below only support IntlListFormatter::TYPE_AND"); RETURN_THROWS(); } - if (width != ULISTFMT_WIDTH_WIDE) { + if (width != INTL_LISTFORMATTER_FALLBACK_WIDTH_WIDE) { zend_argument_value_error(3, "contains an unsupported width. ICU 66 and below only support IntlListFormatter::WIDTH_WIDE"); RETURN_THROWS(); } diff --git a/ext/intl/listformatter/listformatter_class.h b/ext/intl/listformatter/listformatter_class.h index 9dd708ca3dfbc..8fe8137796bfb 100644 --- a/ext/intl/listformatter/listformatter_class.h +++ b/ext/intl/listformatter/listformatter_class.h @@ -49,4 +49,7 @@ static inline ListFormatter_object *php_intl_listformatter_fetch_object(zend_obj void listformatter_register_class( void ); extern zend_class_entry *ListFormatter_ce_ptr; +#define INTL_LISTFORMATTER_FALLBACK_TYPE_AND 0 +#define INTL_LISTFORMATTER_FALLBACK_WIDTH_WIDE 0 + #endif // LISTFORMATTER_CLASS_H From 5ff5ee0698f47e87f65ca7daa3b338e804cd108f Mon Sep 17 00:00:00 2001 From: Demon Date: Sun, 8 Jun 2025 18:25:25 +0800 Subject: [PATCH 095/682] Fix iconv tests skipped on windows Closes GH-18802. --- ext/iconv/tests/translit-failure.phpt | 2 +- ext/iconv/tests/translit-utf8.phpt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/iconv/tests/translit-failure.phpt b/ext/iconv/tests/translit-failure.phpt index b639c26b4eb32..33f992300a184 100644 --- a/ext/iconv/tests/translit-failure.phpt +++ b/ext/iconv/tests/translit-failure.phpt @@ -4,7 +4,7 @@ Translit failure iconv --SKIPIF-- --INI-- error_reporting=2039 diff --git a/ext/iconv/tests/translit-utf8.phpt b/ext/iconv/tests/translit-utf8.phpt index 14b5d7a05e1b6..5a308820ca2be 100644 --- a/ext/iconv/tests/translit-utf8.phpt +++ b/ext/iconv/tests/translit-utf8.phpt @@ -4,7 +4,7 @@ Translit UTF-8 quotes iconv --SKIPIF-- --INI-- error_reporting=2047 From 5cf3c2663ba53049fc6326c0b401542429992215 Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Wed, 11 Jun 2025 13:22:02 +0200 Subject: [PATCH 096/682] Fix use after free during shutdown destruction Closes GH-18834. --- NEWS | 3 +++ Zend/tests/gh18833.phpt | 24 ++++++++++++++++++++++++ Zend/zend_objects_API.c | 4 +++- 3 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 Zend/tests/gh18833.phpt diff --git a/NEWS b/NEWS index c29118397857d..af154569e2843 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,9 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? ????, PHP 8.3.24 +- Core: + . Fixed bug GH-18833 (Use after free with weakmaps dependent on destruction + order). (Daniil Gentili) 03 Jul 2025, PHP 8.3.23 diff --git a/Zend/tests/gh18833.phpt b/Zend/tests/gh18833.phpt new file mode 100644 index 0000000000000..d00f860ee43aa --- /dev/null +++ b/Zend/tests/gh18833.phpt @@ -0,0 +1,24 @@ +--TEST-- +GH-18833 (Use after free with weakmaps dependent on destruction order) +--FILE-- +current(); + +echo "ok\n"; +?> +--EXPECT-- +ok diff --git a/Zend/zend_objects_API.c b/Zend/zend_objects_API.c index 80f5b747db710..ec4c88d6aa513 100644 --- a/Zend/zend_objects_API.c +++ b/Zend/zend_objects_API.c @@ -104,7 +104,9 @@ ZEND_API void ZEND_FASTCALL zend_objects_store_free_object_storage(zend_objects_ if (IS_OBJ_VALID(obj)) { if (!(OBJ_FLAGS(obj) & IS_OBJ_FREE_CALLED)) { GC_ADD_FLAGS(obj, IS_OBJ_FREE_CALLED); - if (obj->handlers->free_obj != zend_object_std_dtor) { + if (obj->handlers->free_obj != zend_object_std_dtor + || (OBJ_FLAGS(obj) & IS_OBJ_WEAKLY_REFERENCED) + ) { GC_ADDREF(obj); obj->handlers->free_obj(obj); } From f1295864597a2e46ee2694c5bcdc0132427e9c3f Mon Sep 17 00:00:00 2001 From: David CARLIER Date: Wed, 18 Jun 2025 21:06:03 +0100 Subject: [PATCH 097/682] ext/pdo_sqlite: fix minimal version for EXPLAIN feature support. (#18854) --- ext/pdo_sqlite/pdo_sqlite.stub.php | 2 +- ext/pdo_sqlite/pdo_sqlite_arginfo.h | 8 +- ext/pdo_sqlite/sqlite_statement.c | 4 +- .../pdo_sqlite_getsetattr_explain.phpt | 102 +++++++++--------- 4 files changed, 58 insertions(+), 58 deletions(-) diff --git a/ext/pdo_sqlite/pdo_sqlite.stub.php b/ext/pdo_sqlite/pdo_sqlite.stub.php index 596029524dea0..4af2d8c55260b 100644 --- a/ext/pdo_sqlite/pdo_sqlite.stub.php +++ b/ext/pdo_sqlite/pdo_sqlite.stub.php @@ -39,7 +39,7 @@ class Sqlite extends \PDO /** @cvalue PDO_SQLITE_ATTR_EXPLAIN_STATEMENT */ public const int ATTR_EXPLAIN_STATEMENT = UNKNOWN; -#if SQLITE_VERSION_NUMBER >= 3041000 +#if SQLITE_VERSION_NUMBER >= 3043000 public const int EXPLAIN_MODE_PREPARED = 0; public const int EXPLAIN_MODE_EXPLAIN = 1; public const int EXPLAIN_MODE_EXPLAIN_QUERY_PLAN = 2; diff --git a/ext/pdo_sqlite/pdo_sqlite_arginfo.h b/ext/pdo_sqlite/pdo_sqlite_arginfo.h index b81790fa1df1d..02cf12673ce53 100644 --- a/ext/pdo_sqlite/pdo_sqlite_arginfo.h +++ b/ext/pdo_sqlite/pdo_sqlite_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: fa489a46c586ae935036f76a992163aeb67246d3 */ + * Stub hash: c1d4ef325ecb8c8cb312910e8091ca003dc2603a */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Pdo_Sqlite_createAggregate, 0, 3, _IS_BOOL, 0) ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 0) @@ -121,7 +121,7 @@ static zend_class_entry *register_class_Pdo_Sqlite(zend_class_entry *class_entry zend_string *const_ATTR_EXPLAIN_STATEMENT_name = zend_string_init_interned("ATTR_EXPLAIN_STATEMENT", sizeof("ATTR_EXPLAIN_STATEMENT") - 1, 1); zend_declare_typed_class_constant(class_entry, const_ATTR_EXPLAIN_STATEMENT_name, &const_ATTR_EXPLAIN_STATEMENT_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release(const_ATTR_EXPLAIN_STATEMENT_name); -#if SQLITE_VERSION_NUMBER >= 3041000 +#if SQLITE_VERSION_NUMBER >= 3043000 zval const_EXPLAIN_MODE_PREPARED_value; ZVAL_LONG(&const_EXPLAIN_MODE_PREPARED_value, 0); @@ -129,7 +129,7 @@ static zend_class_entry *register_class_Pdo_Sqlite(zend_class_entry *class_entry zend_declare_typed_class_constant(class_entry, const_EXPLAIN_MODE_PREPARED_name, &const_EXPLAIN_MODE_PREPARED_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release(const_EXPLAIN_MODE_PREPARED_name); #endif -#if SQLITE_VERSION_NUMBER >= 3041000 +#if SQLITE_VERSION_NUMBER >= 3043000 zval const_EXPLAIN_MODE_EXPLAIN_value; ZVAL_LONG(&const_EXPLAIN_MODE_EXPLAIN_value, 1); @@ -137,7 +137,7 @@ static zend_class_entry *register_class_Pdo_Sqlite(zend_class_entry *class_entry zend_declare_typed_class_constant(class_entry, const_EXPLAIN_MODE_EXPLAIN_name, &const_EXPLAIN_MODE_EXPLAIN_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release(const_EXPLAIN_MODE_EXPLAIN_name); #endif -#if SQLITE_VERSION_NUMBER >= 3041000 +#if SQLITE_VERSION_NUMBER >= 3043000 zval const_EXPLAIN_MODE_EXPLAIN_QUERY_PLAN_value; ZVAL_LONG(&const_EXPLAIN_MODE_EXPLAIN_QUERY_PLAN_value, 2); diff --git a/ext/pdo_sqlite/sqlite_statement.c b/ext/pdo_sqlite/sqlite_statement.c index 381dab82af62f..e9e7a0cc78609 100644 --- a/ext/pdo_sqlite/sqlite_statement.c +++ b/ext/pdo_sqlite/sqlite_statement.c @@ -393,7 +393,7 @@ static int pdo_sqlite_stmt_get_attribute(pdo_stmt_t *stmt, zend_long attr, zval } break; case PDO_SQLITE_ATTR_EXPLAIN_STATEMENT: -#if SQLITE_VERSION_NUMBER >= 3041000 +#if SQLITE_VERSION_NUMBER >= 3043000 #if defined(__APPLE__) if (__builtin_available(macOS 14.2, *)) { #endif @@ -420,7 +420,7 @@ static int pdo_sqlite_stmt_set_attribute(pdo_stmt_t *stmt, zend_long attr, zval { switch (attr) { case PDO_SQLITE_ATTR_EXPLAIN_STATEMENT: -#if SQLITE_VERSION_NUMBER >= 3041000 +#if SQLITE_VERSION_NUMBER >= 3043000 #if defined(__APPLE__) if (__builtin_available(macOS 14.2, *)) { #endif diff --git a/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_getsetattr_explain.phpt b/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_getsetattr_explain.phpt index 73a160e1d8134..383457f3a79e8 100644 --- a/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_getsetattr_explain.phpt +++ b/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_getsetattr_explain.phpt @@ -59,9 +59,9 @@ try { var_dump($stmts->getAttribute(Pdo\Sqlite::ATTR_EXPLAIN_STATEMENT) == Pdo\Sqlite::EXPLAIN_MODE_PREPARED); ?> ---EXPECT-- +--EXPECTF-- bool(true) -array(16) { +array(%d) { [0]=> array(8) { ["addr"]=> @@ -71,7 +71,7 @@ array(16) { ["p1"]=> int(0) ["p2"]=> - int(14) + int(%d) ["p3"]=> int(0) ["p4"]=> @@ -79,7 +79,7 @@ array(16) { ["p5"]=> int(0) ["comment"]=> - NULL + %a } [1]=> array(8) { @@ -90,7 +90,7 @@ array(16) { ["p1"]=> int(3) ["p2"]=> - int(7) + int(%d) ["p3"]=> int(2) ["p4"]=> @@ -98,12 +98,12 @@ array(16) { ["p5"]=> int(0) ["comment"]=> - NULL + %a } - [2]=> + %A array(8) { ["addr"]=> - int(2) + int(%d) ["opcode"]=> string(7) "String8" ["p1"]=> @@ -117,12 +117,12 @@ array(16) { ["p5"]=> int(0) ["comment"]=> - NULL + %a } - [3]=> + %A array(8) { ["addr"]=> - int(3) + int(%d) ["opcode"]=> string(5) "Yield" ["p1"]=> @@ -136,12 +136,12 @@ array(16) { ["p5"]=> int(0) ["comment"]=> - NULL + %a } - [4]=> + %A array(8) { ["addr"]=> - int(4) + int(%d) ["opcode"]=> string(7) "String8" ["p1"]=> @@ -155,12 +155,12 @@ array(16) { ["p5"]=> int(0) ["comment"]=> - NULL + %a } - [5]=> + [%d]=> array(8) { ["addr"]=> - int(5) + int(%d) ["opcode"]=> string(5) "Yield" ["p1"]=> @@ -174,12 +174,12 @@ array(16) { ["p5"]=> int(0) ["comment"]=> - NULL + %a } - [6]=> + [%d]=> array(8) { ["addr"]=> - int(6) + int(%d) ["opcode"]=> string(12) "EndCoroutine" ["p1"]=> @@ -193,12 +193,12 @@ array(16) { ["p5"]=> int(0) ["comment"]=> - NULL + %a } - [7]=> + [%d]=> array(8) { ["addr"]=> - int(7) + int(%d) ["opcode"]=> string(9) "OpenWrite" ["p1"]=> @@ -212,18 +212,18 @@ array(16) { ["p5"]=> int(0) ["comment"]=> - NULL + %a } - [8]=> + [%d]=> array(8) { ["addr"]=> - int(8) + int(%d) ["opcode"]=> string(5) "Yield" ["p1"]=> int(3) ["p2"]=> - int(13) + int(%d) ["p3"]=> int(0) ["p4"]=> @@ -231,12 +231,12 @@ array(16) { ["p5"]=> int(0) ["comment"]=> - NULL + %a } - [9]=> + [%d]=> array(8) { ["addr"]=> - int(9) + int(%d) ["opcode"]=> string(8) "NewRowid" ["p1"]=> @@ -250,12 +250,12 @@ array(16) { ["p5"]=> int(0) ["comment"]=> - NULL + %a } - [10]=> + [%d]=> array(8) { ["addr"]=> - int(10) + int(%d) ["opcode"]=> string(10) "MakeRecord" ["p1"]=> @@ -269,12 +269,12 @@ array(16) { ["p5"]=> int(0) ["comment"]=> - NULL + %a } - [11]=> + [%d]=> array(8) { ["addr"]=> - int(11) + int(%d) ["opcode"]=> string(6) "Insert" ["p1"]=> @@ -288,18 +288,18 @@ array(16) { ["p5"]=> int(57) ["comment"]=> - NULL + %a } - [12]=> + [%d]=> array(8) { ["addr"]=> - int(12) + int(%d) ["opcode"]=> string(4) "Goto" ["p1"]=> int(0) ["p2"]=> - int(8) + int(%d) ["p3"]=> int(0) ["p4"]=> @@ -307,12 +307,12 @@ array(16) { ["p5"]=> int(0) ["comment"]=> - NULL + %a } - [13]=> + [%d]=> array(8) { ["addr"]=> - int(13) + int(%d) ["opcode"]=> string(4) "Halt" ["p1"]=> @@ -326,12 +326,12 @@ array(16) { ["p5"]=> int(0) ["comment"]=> - NULL + %a } - [14]=> + [%d]=> array(8) { ["addr"]=> - int(14) + int(%d) ["opcode"]=> string(11) "Transaction" ["p1"]=> @@ -345,12 +345,12 @@ array(16) { ["p5"]=> int(1) ["comment"]=> - NULL + %a } - [15]=> + [%d]=> array(8) { ["addr"]=> - int(15) + int(%d) ["opcode"]=> string(4) "Goto" ["p1"]=> @@ -364,7 +364,7 @@ array(16) { ["p5"]=> int(0) ["comment"]=> - NULL + %a } } bool(false) @@ -376,7 +376,7 @@ array(1) { ["parent"]=> int(0) ["notused"]=> - int(0) + int(%d) ["detail"]=> string(17) "SCAN test_explain" } From 3ff6874658231669e1f1659ad7b6edbbe81eb90e Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Thu, 19 Jun 2025 14:07:40 +0200 Subject: [PATCH 098/682] Remove HAVE_GETLOGIN from win32/config.w32.h.in template (#18865) PHP once had getlogin() emulation implemented on Windows. This isn't the case anymore since 2006 (dc34d34230ec152abfe27bf492d206a294359320), neither Windows has getlogin() function. --- win32/build/config.w32.h.in | 1 - 1 file changed, 1 deletion(-) diff --git a/win32/build/config.w32.h.in b/win32/build/config.w32.h.in index 40a4264cf396f..df69e4622f90d 100644 --- a/win32/build/config.w32.h.in +++ b/win32/build/config.w32.h.in @@ -59,7 +59,6 @@ #undef HAVE_STRUCT_STAT_ST_BLKSIZE #undef HAVE_STRUCT_STAT_ST_BLOCKS #define HAVE_STRUCT_STAT_ST_RDEV 1 -#define HAVE_GETLOGIN 1 #define HAVE_SHUTDOWN 1 #define HAVE_STRCASECMP 1 #define HAVE_UTIME 1 From 49dd61906a009a687988b9e25002359ca0a3282a Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Thu, 19 Jun 2025 17:19:23 +0200 Subject: [PATCH 099/682] Autotools: Move PHP_ODBC_* defines to configuration header (#15708) The PHP_ODBC_* defines are remains of the PHP 2 and 3 era where the ODBC functionality was part of the PHP core and was later moved to an extension. This moves these defines to a regular PHP configuration header (php_config.h) as done in other extensions. --- UPGRADING.INTERNALS | 3 +++ ext/odbc/config.m4 | 13 +++++++++---- main/build-defs.h.in | 4 ---- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS index e4cf9e9c94b0d..432754528f09a 100644 --- a/UPGRADING.INTERNALS +++ b/UPGRADING.INTERNALS @@ -52,6 +52,9 @@ PHP 8.5 INTERNALS UPGRADE NOTES - Linux build system changes . libdir is properly set when --libdir (ex: /usr/lib64) and --with-libdir (ex lib64) configure options are used to ${libdir}/php (ex: /usr/lib64/php) + . PHP_ODBC_CFLAGS, PHP_ODBC_LFLAGS, PHP_ODBC_LIBS, PHP_ODBC_TYPE preprocessor + macros defined by ext/odbc are now defined in php_config.h instead of the + build-defs.h header. ======================== 3. Module changes diff --git a/ext/odbc/config.m4 b/ext/odbc/config.m4 index eb6572c2c5c2d..9baa8dbd8e646 100644 --- a/ext/odbc/config.m4 +++ b/ext/odbc/config.m4 @@ -428,11 +428,16 @@ if test -n "$ODBC_TYPE"; then AC_DEFINE([HAVE_UODBC], [1], [Define to 1 if the PHP extension 'odbc' is available.]) + AC_DEFINE_UNQUOTED([PHP_ODBC_CFLAGS], ["$ODBC_CFLAGS"], + [The compile options that PHP odbc extension was built with.]) + AC_DEFINE_UNQUOTED([PHP_ODBC_LIBS], ["$ODBC_LIBS"], + [The libraries linker flags that PHP odbc extension was built with.]) + AC_DEFINE_UNQUOTED([PHP_ODBC_LFLAGS], ["$ODBC_LFLAGS"], + [The linker flags that PHP odbc extension was built with.]) + AC_DEFINE_UNQUOTED([PHP_ODBC_TYPE], ["$ODBC_TYPE"], + [The ODBC library used in the PHP odbc extension.]) + PHP_SUBST([ODBC_SHARED_LIBADD]) - AC_SUBST([ODBC_CFLAGS]) - AC_SUBST([ODBC_LIBS]) - AC_SUBST([ODBC_LFLAGS]) - AC_SUBST([ODBC_TYPE]) PHP_NEW_EXTENSION([odbc], [php_odbc.c odbc_utils.c], diff --git a/main/build-defs.h.in b/main/build-defs.h.in index f0cbdb631c0d5..72cd9c30fb743 100644 --- a/main/build-defs.h.in +++ b/main/build-defs.h.in @@ -15,10 +15,6 @@ */ #define CONFIGURE_COMMAND "@CONFIGURE_COMMAND@" -#define PHP_ODBC_CFLAGS "@ODBC_CFLAGS@" -#define PHP_ODBC_LFLAGS "@ODBC_LFLAGS@" -#define PHP_ODBC_LIBS "@ODBC_LIBS@" -#define PHP_ODBC_TYPE "@ODBC_TYPE@" #define PHP_PROG_SENDMAIL "@PROG_SENDMAIL@" #define PEAR_INSTALLDIR "@EXPANDED_PEAR_INSTALLDIR@" #define PHP_INCLUDE_PATH "@INCLUDE_PATH@" From 2f55291cebcee390507a4762c8ca1f4333076713 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Thu, 19 Jun 2025 21:18:18 +0200 Subject: [PATCH 100/682] Sync to lexbor/lexbor@0eac579f (#18882) --- ext/lexbor/lexbor/core/sbst.h | 1 - ext/lexbor/lexbor/css/syntax/state.c | 158 ++++++++++----------------- ext/lexbor/lexbor/css/unit.h | 2 +- ext/lexbor/lexbor/css/unit/const.h | 6 +- ext/lexbor/lexbor/css/unit/res.h | 2 +- ext/lexbor/lexbor/css/value.h | 2 +- 6 files changed, 66 insertions(+), 105 deletions(-) diff --git a/ext/lexbor/lexbor/core/sbst.h b/ext/lexbor/lexbor/core/sbst.h index 03444afdc747d..15a1d4053ce56 100644 --- a/ext/lexbor/lexbor/core/sbst.h +++ b/ext/lexbor/lexbor/core/sbst.h @@ -25,7 +25,6 @@ extern "C" { # define LXB_NONSTRING #endif - typedef struct { lxb_char_t key; diff --git a/ext/lexbor/lexbor/css/syntax/state.c b/ext/lexbor/lexbor/css/syntax/state.c index 81c88e8c093f1..99cd30c1868f3 100644 --- a/ext/lexbor/lexbor/css/syntax/state.c +++ b/ext/lexbor/lexbor/css/syntax/state.c @@ -99,13 +99,6 @@ lxb_css_syntax_state_consume_numeric(lxb_css_syntax_tokenizer_t *tkz, const lxb_char_t *data, const lxb_char_t *end); -static const lxb_char_t * -lxb_css_syntax_state_decimal(lxb_css_syntax_tokenizer_t *tkz, - lxb_css_syntax_token_t *token, - const lxb_char_t *data, const lxb_char_t *end, - lxb_char_t *buf, lxb_char_t *buf_p, - const lxb_char_t *buf_end); - static const lxb_char_t * lxb_css_syntax_state_consume_numeric_name_start(lxb_css_syntax_tokenizer_t *tkz, lxb_css_syntax_token_t *token, @@ -706,8 +699,6 @@ lxb_css_syntax_state_plus(lxb_css_syntax_tokenizer_t *tkz, lxb_css_syntax_token_t *token, const lxb_char_t *data, const lxb_char_t *end) { - lxb_char_t buf[128]; - /* Skip U+002B PLUS SIGN (+). */ data += 1; @@ -732,8 +723,8 @@ lxb_css_syntax_state_plus(lxb_css_syntax_tokenizer_t *tkz, /* U+0030 DIGIT ZERO (0) and U+0039 DIGIT NINE (9) */ if (*data >= 0x30 && *data <= 0x39) { lxb_css_syntax_token_number(token)->have_sign = true; - return lxb_css_syntax_state_decimal(tkz, token, data, end, - buf, buf, buf + sizeof(buf)); + return lxb_css_syntax_state_consume_numeric(tkz, token, + data - 1, end); } data -= 1; @@ -804,6 +795,7 @@ lxb_css_syntax_state_full_stop(lxb_css_syntax_tokenizer_t *tkz, const lxb_char_t *data, const lxb_char_t *end) { if (lxb_css_syntax_state_start_number(data, end)) { + lxb_css_syntax_token_number(token)->have_sign = false; return lxb_css_syntax_state_consume_numeric(tkz, token, data, end); } @@ -935,37 +927,26 @@ lxb_css_syntax_state_rc_bracket(lxb_css_syntax_tokenizer_t *tkz, lxb_css_syntax_ * Numeric */ lxb_inline void -lxb_css_syntax_consume_numeric_set_int(lxb_css_syntax_tokenizer_t *tkz, - lxb_css_syntax_token_t *token, - const lxb_char_t *start, const lxb_char_t *end) -{ - double num = lexbor_strtod_internal(start, (end - start), 0); - - token->type = LXB_CSS_SYNTAX_TOKEN_NUMBER; - - lxb_css_syntax_token_number(token)->is_float = false; - lxb_css_syntax_token_number(token)->num = num; -} - -lxb_inline void -lxb_css_syntax_consume_numeric_set_float(lxb_css_syntax_tokenizer_t *tkz, - lxb_css_syntax_token_t *token, - const lxb_char_t *start, const lxb_char_t *end, - bool e_is_negative, int exponent, int e_digit) +lxb_css_syntax_consume_numeric_set(lxb_css_syntax_tokenizer_t *tkz, + lxb_css_syntax_token_t *token, + const lxb_char_t *start, const lxb_char_t *end, + bool is_float, bool e_is_negative, + int exponent, int e_digit) { if (e_is_negative) { - exponent -= e_digit; + exponent = e_digit - exponent; + exponent = -exponent; } else { - exponent += e_digit; + exponent = e_digit + exponent; } double num = lexbor_strtod_internal(start, (end - start), exponent); token->type = LXB_CSS_SYNTAX_TOKEN_NUMBER; + lxb_css_syntax_token_number(token)->is_float = is_float; lxb_css_syntax_token_number(token)->num = num; - lxb_css_syntax_token_number(token)->is_float = true; } const lxb_char_t * @@ -985,19 +966,22 @@ lxb_css_syntax_state_consume_numeric(lxb_css_syntax_tokenizer_t *tkz, const lxb_char_t *data, const lxb_char_t *end) { - lxb_char_t *buf_p; - const lxb_char_t *buf_end; + bool e_is_negative, is_float; + int exponent, e_digit; + lxb_char_t ch, *buf_p; + const lxb_char_t *begin, *buf_end; + lxb_css_syntax_token_t *t_str; + lxb_css_syntax_token_string_t *str; lxb_char_t buf[128]; buf_p = buf; buf_end = buf + sizeof(buf); - do { - /* U+0030 DIGIT ZERO (0) and U+0039 DIGIT NINE (9) */ - if (*data < 0x30 || *data > 0x39) { - break; - } + str = lxb_css_syntax_token_dimension_string(token); + t_str = (lxb_css_syntax_token_t *) (void *) str; + /* U+0030 DIGIT ZERO (0) and U+0039 DIGIT NINE (9) */ + while (*data >= 0x30 && *data <= 0x39) { if (buf_p != buf_end) { *buf_p++ = *data; } @@ -1005,75 +989,54 @@ lxb_css_syntax_state_consume_numeric(lxb_css_syntax_tokenizer_t *tkz, data += 1; if (data >= end) { - lxb_css_syntax_consume_numeric_set_int(tkz, token, buf, buf_p); + lxb_css_syntax_consume_numeric_set(tkz, token, buf, buf_p, + false, false, 0, 0); return data; } } - while (true); - - /* U+002E FULL STOP (.) */ - if (*data != 0x2E) { - lxb_css_syntax_consume_numeric_set_int(tkz, token, buf, buf_p); - - return lxb_css_syntax_state_consume_numeric_name_start(tkz, token, - data, end); - } - data += 1; + exponent = 0; + is_float = false; - if (data >= end || *data < 0x30 || *data > 0x39) { - lxb_css_syntax_consume_numeric_set_int(tkz, token, buf, buf_p); - return data - 1; - } - - return lxb_css_syntax_state_decimal(tkz, token, data, end, - buf, buf_p, buf_end); -} + /* U+002E FULL STOP (.) */ + if (*data == 0x2E) { + data += 1; -static const lxb_char_t * -lxb_css_syntax_state_decimal(lxb_css_syntax_tokenizer_t *tkz, - lxb_css_syntax_token_t *token, - const lxb_char_t *data, const lxb_char_t *end, - lxb_char_t *buf, lxb_char_t *buf_p, - const lxb_char_t *buf_end) -{ - bool e_is_negative; - int exponent, e_digit; - lxb_char_t ch; - const lxb_char_t *begin; - lxb_css_syntax_token_t *t_str; - lxb_css_syntax_token_string_t *str; + /* U+0030 DIGIT ZERO (0) and U+0039 DIGIT NINE (9) */ + if (data >= end || *data < 0x30 || *data > 0x39) { + lxb_css_syntax_consume_numeric_set(tkz, token, buf, buf_p, + false, false, 0, 0); + return data - 1; + } - begin = data; + begin = buf_p; - str = lxb_css_syntax_token_dimension_string(token); - t_str = (lxb_css_syntax_token_t *) (void *) str; + /* U+0030 DIGIT ZERO (0) and U+0039 DIGIT NINE (9) */ + do { + if (buf_p != buf_end) { + *buf_p++ = *data; + } - /* U+0030 DIGIT ZERO (0) and U+0039 DIGIT NINE (9) */ - do { - if (buf_p != buf_end) { - *buf_p++ = *data; + data += 1; } + while (data < end && *data >= 0x30 && *data <= 0x39); - data += 1; + exponent = -(int) (buf_p - begin); + is_float = true; if (data >= end) { - exponent = 0 - (int) (data - begin); - - lxb_css_syntax_consume_numeric_set_float(tkz, token, buf, - buf_p, 0, exponent, 0); + lxb_css_syntax_consume_numeric_set(tkz, token, buf, buf_p, + true, false, exponent, 0); return data; } } - while (*data >= 0x30 && *data <= 0x39); ch = *data; - exponent = 0 - (int) (data - begin); /* U+0045 Latin Capital Letter (E) or U+0065 Latin Small Letter (e) */ if (ch != 0x45 && ch != 0x65) { - lxb_css_syntax_consume_numeric_set_float(tkz, token, buf, - buf_p, 0, exponent, 0); + lxb_css_syntax_consume_numeric_set(tkz, token, buf, buf_p, + is_float, false, exponent, 0); return lxb_css_syntax_state_consume_numeric_name_start(tkz, token, data, end); @@ -1087,11 +1050,10 @@ lxb_css_syntax_state_decimal(lxb_css_syntax_tokenizer_t *tkz, data -= 1; lxb_css_syntax_token_base(t_str)->length = 1; - lxb_css_syntax_buffer_append_m(tkz, data, 1); - lxb_css_syntax_consume_numeric_set_float(tkz, token, buf, - buf_p, 0, exponent, 0); + lxb_css_syntax_consume_numeric_set(tkz, token, buf, buf_p, + is_float, false, exponent, 0); token->type = LXB_CSS_SYNTAX_TOKEN_DIMENSION; @@ -1122,8 +1084,8 @@ lxb_css_syntax_state_decimal(lxb_css_syntax_tokenizer_t *tkz, data -= 1; } - lxb_css_syntax_consume_numeric_set_float(tkz, token, buf, - buf_p, 0, exponent, 0); + lxb_css_syntax_consume_numeric_set(tkz, token, buf, buf_p, + is_float, false, exponent, 0); token->type = LXB_CSS_SYNTAX_TOKEN_DIMENSION; @@ -1135,7 +1097,6 @@ lxb_css_syntax_state_decimal(lxb_css_syntax_tokenizer_t *tkz, return begin; } - begin = data; e_digit = 0; /* U+0030 DIGIT ZERO (0) and U+0039 DIGIT NINE (9) */ @@ -1145,16 +1106,17 @@ lxb_css_syntax_state_decimal(lxb_css_syntax_tokenizer_t *tkz, data += 1; if (data >= end) { - lxb_css_syntax_consume_numeric_set_float(tkz, token, buf, buf_p, - e_is_negative, exponent, - e_digit); - return data; + lxb_css_syntax_consume_numeric_set(tkz, token, buf, buf_p, + true, e_is_negative, + exponent, e_digit); + return data; } } while(*data >= 0x30 && *data <= 0x39); - lxb_css_syntax_consume_numeric_set_float(tkz, token, buf, buf_p, - e_is_negative, exponent, e_digit); + lxb_css_syntax_consume_numeric_set(tkz, token, buf, buf_p, + true, e_is_negative, + exponent, e_digit); return lxb_css_syntax_state_consume_numeric_name_start(tkz, token, data, end); diff --git a/ext/lexbor/lexbor/css/unit.h b/ext/lexbor/lexbor/css/unit.h index dbaa08d1c6d28..06dba0dbca5de 100644 --- a/ext/lexbor/lexbor/css/unit.h +++ b/ext/lexbor/lexbor/css/unit.h @@ -24,7 +24,7 @@ LXB_API const lxb_css_data_t * lxb_css_unit_relative_by_name(const lxb_char_t *name, size_t length); LXB_API const lxb_css_data_t * -lxb_css_unit_angel_by_name(const lxb_char_t *name, size_t length); +lxb_css_unit_angle_by_name(const lxb_char_t *name, size_t length); LXB_API const lxb_css_data_t * lxb_css_unit_frequency_by_name(const lxb_char_t *name, size_t length); diff --git a/ext/lexbor/lexbor/css/unit/const.h b/ext/lexbor/lexbor/css/unit/const.h index 686ce37f94ffc..9b7ba1c311b17 100644 --- a/ext/lexbor/lexbor/css/unit/const.h +++ b/ext/lexbor/lexbor/css/unit/const.h @@ -58,14 +58,14 @@ typedef enum { lxb_css_unit_relative_t; typedef enum { - LXB_CSS_UNIT_ANGEL__BEGIN = 0x0016, + LXB_CSS_UNIT_ANGLE__BEGIN = 0x0016, LXB_CSS_UNIT_DEG = 0x0016, LXB_CSS_UNIT_GRAD = 0x0017, LXB_CSS_UNIT_RAD = 0x0018, LXB_CSS_UNIT_TURN = 0x0019, - LXB_CSS_UNIT_ANGEL__LAST_ENTRY = 0x001a + LXB_CSS_UNIT_ANGLE__LAST_ENTRY = 0x001a } -lxb_css_unit_angel_t; +lxb_css_unit_angle_t; typedef enum { LXB_CSS_UNIT_FREQUENCY__BEGIN = 0x001a, diff --git a/ext/lexbor/lexbor/css/unit/res.h b/ext/lexbor/lexbor/css/unit/res.h index e4b867983894a..4cca189b6f459 100644 --- a/ext/lexbor/lexbor/css/unit/res.h +++ b/ext/lexbor/lexbor/css/unit/res.h @@ -246,7 +246,7 @@ static const lexbor_shs_entry_t lxb_css_unit_relative_shs[64] = {NULL, NULL, 0, 0} }; -static const lexbor_shs_entry_t lxb_css_unit_angel_shs[7] = +static const lexbor_shs_entry_t lxb_css_unit_angle_shs[7] = { {NULL, NULL, 6, 0}, {"turn", (void *) &lxb_css_unit_data[LXB_CSS_UNIT_TURN], 4, 0}, diff --git a/ext/lexbor/lexbor/css/value.h b/ext/lexbor/lexbor/css/value.h index 7f74b6a6b458d..eb26aa8f8b95b 100644 --- a/ext/lexbor/lexbor/css/value.h +++ b/ext/lexbor/lexbor/css/value.h @@ -109,7 +109,7 @@ lxb_css_value_length_percentage_type_t; typedef struct { double num; bool is_float; - lxb_css_unit_angel_t unit; + lxb_css_unit_angle_t unit; } lxb_css_value_angle_t; From be70f42de7c9ec0e6158ce9ec9d21f0dac24897f Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Thu, 19 Jun 2025 21:49:36 +0200 Subject: [PATCH 101/682] Add HAVE_MEMMOVE to ext/pcre (#18862) The pcre2 library still needs HAVE_MEMMOVE defined to use the system (C99 standard) memmove() function, otherwise emulation is used. On Windows, this is already enabled. --- ext/pcre/config0.m4 | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/ext/pcre/config0.m4 b/ext/pcre/config0.m4 index bf48aa53130c5..d049cc538c0f5 100644 --- a/ext/pcre/config0.m4 +++ b/ext/pcre/config0.m4 @@ -99,7 +99,14 @@ else [PHP_PCRE_CFLAGS="$PHP_PCRE_CFLAGS -Wno-implicit-fallthrough"],, [-Werror]) - PHP_PCRE_CFLAGS="$PHP_PCRE_CFLAGS -DHAVE_CONFIG_H -I@ext_srcdir@/pcre2lib -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1" + PHP_PCRE_CFLAGS=m4_normalize([" + $PHP_PCRE_CFLAGS + -DHAVE_CONFIG_H + -DHAVE_MEMMOVE + -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1 + -I@ext_srcdir@/pcre2lib + "]) + AC_DEFINE([HAVE_BUNDLED_PCRE], [1], [Define to 1 if PHP uses the bundled PCRE library.]) AC_DEFINE([PCRE2_CODE_UNIT_WIDTH], [8]) From 9cacc57350e1bb2f070b4a7bda6803da1d71e140 Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Thu, 19 Jun 2025 16:28:45 +0200 Subject: [PATCH 102/682] Track heap->real_size for USE_TRACKED_ALLOC real_size is returned by memory_get_usage(true), which previously returned 0. Discovered in Symfony ConsumeMessagesCommandTest::testRunWithMemoryLimit() through nightly. Closes GH-18880 --- Zend/zend_alloc.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Zend/zend_alloc.c b/Zend/zend_alloc.c index c41f6118607e2..61792b37c9c50 100644 --- a/Zend/zend_alloc.c +++ b/Zend/zend_alloc.c @@ -2256,6 +2256,7 @@ void zend_mm_shutdown(zend_mm_heap *heap, bool full, bool silent) heap->custom_heap.std._free = free; } heap->size = 0; + heap->real_size = 0; } if (full) { @@ -2799,6 +2800,7 @@ static void *tracked_malloc(size_t size) void *ptr = __zend_malloc(size); tracked_add(heap, ptr, size); heap->size += size; + heap->real_size = heap->size; return ptr; } @@ -2810,6 +2812,7 @@ static void tracked_free(void *ptr) { zend_mm_heap *heap = AG(mm_heap); zval *size_zv = tracked_get_size_zv(heap, ptr); heap->size -= Z_LVAL_P(size_zv); + heap->real_size = heap->size; zend_hash_del_bucket(heap->tracked_allocs, (Bucket *) size_zv); free(ptr); } @@ -2835,6 +2838,7 @@ static void *tracked_realloc(void *ptr, size_t new_size) { ptr = __zend_realloc(ptr, new_size); tracked_add(heap, ptr, new_size); heap->size += new_size - old_size; + heap->real_size = heap->size; return ptr; } From edfd55c1970413b8513ebaab03a6169f67c4cf4c Mon Sep 17 00:00:00 2001 From: Saki Takamachi <34942839+SakiTakamachi@users.noreply.github.com> Date: Sat, 21 Jun 2025 01:34:42 +0900 Subject: [PATCH 103/682] ext/bcmath: use vector in compare (#18859) --- ext/bcmath/libbcmath/src/compare.c | 69 +++++++++++++++++------------- 1 file changed, 39 insertions(+), 30 deletions(-) diff --git a/ext/bcmath/libbcmath/src/compare.c b/ext/bcmath/libbcmath/src/compare.c index 06ef246782089..76ab794dc874c 100644 --- a/ext/bcmath/libbcmath/src/compare.c +++ b/ext/bcmath/libbcmath/src/compare.c @@ -39,6 +39,25 @@ than N2 and +1 if N1 is greater than N2. If USE_SIGN is false, just compare the magnitudes. */ +static inline bcmath_compare_result bc_compare_get_result_val(bool left_abs_greater, bool use_sign, sign left_sign) +{ + if (left_abs_greater) { + /* Magnitude of left > right. */ + if (!use_sign || left_sign == PLUS) { + return BCMATH_LEFT_GREATER; + } else { + return BCMATH_RIGHT_GREATER; + } + } else { + /* Magnitude of left < right. */ + if (!use_sign || left_sign == PLUS) { + return BCMATH_RIGHT_GREATER; + } else { + return BCMATH_LEFT_GREATER; + } + } +} + bcmath_compare_result _bc_do_compare(bc_num n1, bc_num n2, size_t scale, bool use_sign) { /* First, compare signs. */ @@ -66,21 +85,7 @@ bcmath_compare_result _bc_do_compare(bc_num n1, bc_num n2, size_t scale, bool us /* Now compare the magnitude. */ if (n1->n_len != n2->n_len) { - if (n1->n_len > n2->n_len) { - /* Magnitude of n1 > n2. */ - if (!use_sign || n1->n_sign == PLUS) { - return BCMATH_LEFT_GREATER; - } else { - return BCMATH_RIGHT_GREATER; - } - } else { - /* Magnitude of n1 < n2. */ - if (!use_sign || n1->n_sign == PLUS) { - return BCMATH_RIGHT_GREATER; - } else { - return BCMATH_LEFT_GREATER; - } - } + return bc_compare_get_result_val(n1->n_len > n2->n_len, use_sign, n1->n_sign); } size_t n1_scale = MIN(n1->n_scale, scale); @@ -92,6 +97,24 @@ bcmath_compare_result _bc_do_compare(bc_num n1, bc_num n2, size_t scale, bool us const char *n1ptr = n1->n_value; const char *n2ptr = n2->n_value; + while (count >= sizeof(BC_VECTOR)) { + BC_VECTOR n1bytes; + BC_VECTOR n2bytes; + memcpy(&n1bytes, n1ptr, sizeof(BC_VECTOR)); + memcpy(&n2bytes, n2ptr, sizeof(BC_VECTOR)); + + if (n1bytes != n2bytes) { +#if BC_LITTLE_ENDIAN + n1bytes = BC_BSWAP(n1bytes); + n2bytes = BC_BSWAP(n2bytes); +#endif + return bc_compare_get_result_val(n1bytes > n2bytes, use_sign, n1->n_sign); + } + count -= sizeof(BC_VECTOR); + n1ptr += sizeof(BC_VECTOR); + n2ptr += sizeof(BC_VECTOR); + } + while ((count > 0) && (*n1ptr == *n2ptr)) { n1ptr++; n2ptr++; @@ -99,21 +122,7 @@ bcmath_compare_result _bc_do_compare(bc_num n1, bc_num n2, size_t scale, bool us } if (count != 0) { - if (*n1ptr > *n2ptr) { - /* Magnitude of n1 > n2. */ - if (!use_sign || n1->n_sign == PLUS) { - return BCMATH_LEFT_GREATER; - } else { - return BCMATH_RIGHT_GREATER; - } - } else { - /* Magnitude of n1 < n2. */ - if (!use_sign || n1->n_sign == PLUS) { - return BCMATH_RIGHT_GREATER; - } else { - return BCMATH_LEFT_GREATER; - } - } + return bc_compare_get_result_val(*n1ptr > *n2ptr, use_sign, n1->n_sign); } /* They are equal up to the last part of the equal part of the fraction. */ From 940441106dda47a644510731cd4193704f70651a Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Fri, 20 Jun 2025 15:11:31 +0200 Subject: [PATCH 104/682] ext/dom: Fix new MSVC compiler warning Closes GH-18889 --- ext/dom/html_document.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/dom/html_document.c b/ext/dom/html_document.c index 1ba39870d39b9..248e85c74c396 100644 --- a/ext/dom/html_document.c +++ b/ext/dom/html_document.c @@ -752,7 +752,7 @@ static bool dom_parse_decode_encode_finish( static bool check_options_validity(uint32_t arg_num, zend_long options) { - const zend_long VALID_OPTIONS = XML_PARSE_NOERROR | XML_PARSE_COMPACT | HTML_PARSE_NOIMPLIED | DOM_HTML_NO_DEFAULT_NS; + const zend_long VALID_OPTIONS = HTML_PARSE_NOERROR | HTML_PARSE_COMPACT | HTML_PARSE_NOIMPLIED | DOM_HTML_NO_DEFAULT_NS; if ((options & ~VALID_OPTIONS) != 0) { zend_argument_value_error(arg_num, "contains invalid flags (allowed flags: " "LIBXML_NOERROR, " From 391bd2a48fb1e256ec7108166857997eea5da93a Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Fri, 20 Jun 2025 22:39:51 +0200 Subject: [PATCH 105/682] Remove bug61371 test These tests attempt to test that no memory is leaked for stream calls. However, it is incorrect to assume the memory will not increase for other reasons, e.g. when growing resource buffers, for the output buffer, etc. This was discovered through 9cacc57350e1bb2f070b4a7bda6803da1d71e140 with USE_TRACKED_ALLOC=1, but this can also fail with USE_ZEND_ALLOC=1 when increasing loop iterations. --- ext/standard/tests/streams/bug61371-unix.phpt | 45 ------------------- ext/standard/tests/streams/bug61371.phpt | 40 ----------------- 2 files changed, 85 deletions(-) delete mode 100644 ext/standard/tests/streams/bug61371-unix.phpt delete mode 100644 ext/standard/tests/streams/bug61371.phpt diff --git a/ext/standard/tests/streams/bug61371-unix.phpt b/ext/standard/tests/streams/bug61371-unix.phpt deleted file mode 100644 index e196c028cc941..0000000000000 --- a/ext/standard/tests/streams/bug61371-unix.phpt +++ /dev/null @@ -1,45 +0,0 @@ ---TEST-- -Bug #61371: stream_context_create() causes memory leaks on use streams_socket_create ---SKIPIF-- - ---EXPECTF-- -memory: %dkb -bool(true) -memory: %dkb -bool(true) -memory: %dkb -memory: %dkb -bool(true) -memory: %dkb -bool(true) -memory: %dkb diff --git a/ext/standard/tests/streams/bug61371.phpt b/ext/standard/tests/streams/bug61371.phpt deleted file mode 100644 index 00e6372e85a4f..0000000000000 --- a/ext/standard/tests/streams/bug61371.phpt +++ /dev/null @@ -1,40 +0,0 @@ ---TEST-- -Bug #61371: stream_context_create() causes memory leaks on use streams_socket_create ---FILE-- - ---EXPECTF-- -memory: %dkb -bool(true) -memory: %dkb -bool(true) -memory: %dkb -memory: %dkb -bool(true) -memory: %dkb -bool(true) -memory: %dkb From 9859d837caa4dd3baf055b27de069bc9b9cc7f49 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Thu, 29 May 2025 19:15:02 +0200 Subject: [PATCH 106/682] Implement request #61105: Support Soap 1.2 SoapFault Reason Text lang attribute This is on the border line of a bugfix and a new feature. Anyway, this is necessary to fix compatibility with .NET clients. Closes GH-18701. --- NEWS | 2 + UPGRADING | 5 + ext/soap/php_http.c | 24 ++--- ext/soap/php_packet_soap.c | 42 +++++---- ext/soap/php_soap.h | 3 +- ext/soap/soap.c | 146 ++++++++++++++++++----------- ext/soap/soap.stub.php | 5 +- ext/soap/soap_arginfo.h | 10 +- ext/soap/tests/bugs/bug38005.phpt | 2 +- ext/soap/tests/bugs/bug68996.phpt | 2 +- ext/soap/tests/bugs/bug73037.phpt | 5 +- ext/soap/tests/fault_language.phpt | 29 ++++++ ext/soap/tests/soap12/T12.phpt | 2 +- ext/soap/tests/soap12/T13.phpt | 2 +- ext/soap/tests/soap12/T14.phpt | 2 +- ext/soap/tests/soap12/T23.phpt | 2 +- ext/soap/tests/soap12/T24.phpt | 2 +- ext/soap/tests/soap12/T25.phpt | 2 +- ext/soap/tests/soap12/T27.phpt | 2 +- ext/soap/tests/soap12/T28.phpt | 2 +- ext/soap/tests/soap12/T33.phpt | 2 +- ext/soap/tests/soap12/T35.phpt | 2 +- ext/soap/tests/soap12/T36.phpt | 2 +- ext/soap/tests/soap12/T39.phpt | 2 +- ext/soap/tests/soap12/T56.phpt | 2 +- ext/soap/tests/soap12/T58.phpt | 2 +- ext/soap/tests/soap12/T59.phpt | 2 +- ext/soap/tests/soap12/T61.phpt | 2 +- ext/soap/tests/soap12/T63.phpt | 2 +- ext/soap/tests/soap12/T64.phpt | 2 +- ext/soap/tests/soap12/T65.phpt | 2 +- ext/soap/tests/soap12/T69.phpt | 2 +- ext/soap/tests/soap12/T70.phpt | 2 +- ext/soap/tests/soap12/T71.phpt | 2 +- ext/soap/tests/soap12/T72.phpt | 2 +- ext/soap/tests/soap12/T80.phpt | 2 +- 36 files changed, 205 insertions(+), 118 deletions(-) create mode 100644 ext/soap/tests/fault_language.phpt diff --git a/NEWS b/NEWS index 6c2d75d8a3070..fb117a221a37a 100644 --- a/NEWS +++ b/NEWS @@ -202,6 +202,8 @@ PHP NEWS . Fixed bug #70951 (Segmentation fault on invalid WSDL cache). (nielsdos) . Implement request #55503 (Extend __getTypes to support enumerations). (nielsdos, datibbaw) + . Implement request #61105 (Support Soap 1.2 SoapFault Reason Text lang + attribute). (nielsdos) - Sockets: . Added IPPROTO_ICMP/IPPROTO_ICMPV6 to create raw socket for ICMP usage. diff --git a/UPGRADING b/UPGRADING index a9bb61fddd86c..72bb5d76da936 100644 --- a/UPGRADING +++ b/UPGRADING @@ -204,6 +204,11 @@ PHP 8.5 UPGRADE NOTES - SOAP: . Enumeration cases are now dumped in __getTypes(). + . Implemented request #61105: + support for Soap 1.2 Reason Text xml:lang attribute. + The signature of SoapFault::__construct() and SoapServer::fault() therefore + now have an optional $lang parameter. + This support solves compatibility with .NET SOAP clients. - XSL: . The $namespace argument of XSLTProcessor::getParameter(), diff --git a/ext/soap/php_http.c b/ext/soap/php_http.c index a1bd7dff0c8a5..84a10368d22fe 100644 --- a/ext/soap/php_http.c +++ b/ext/soap/php_http.c @@ -455,7 +455,7 @@ int make_http_soap_request(zval *this_ptr, if (request != buf) { zend_string_release_ex(request, 0); } - add_soap_fault(this_ptr, "HTTP", "Unable to parse URL", NULL, NULL); + add_soap_fault(this_ptr, "HTTP", "Unable to parse URL", NULL, NULL, SOAP_GLOBAL(lang_en)); smart_str_free(&soap_headers_z); efree(http_msg); return FALSE; @@ -469,7 +469,7 @@ int make_http_soap_request(zval *this_ptr, if (request != buf) { zend_string_release_ex(request, 0); } - add_soap_fault(this_ptr, "HTTP", "Unknown protocol. Only http and https are allowed.", NULL, NULL); + add_soap_fault(this_ptr, "HTTP", "Unknown protocol. Only http and https are allowed.", NULL, NULL, SOAP_GLOBAL(lang_en)); smart_str_free(&soap_headers_z); efree(http_msg); return FALSE; @@ -482,7 +482,7 @@ int make_http_soap_request(zval *this_ptr, if (request != buf) { zend_string_release_ex(request, 0); } - add_soap_fault(this_ptr, "HTTP", "SSL support is not available in this build", NULL, NULL); + add_soap_fault(this_ptr, "HTTP", "SSL support is not available in this build", NULL, NULL, SOAP_GLOBAL(lang_en)); PG(allow_url_fopen) = old_allow_url_fopen; smart_str_free(&soap_headers_z); efree(http_msg); @@ -537,7 +537,7 @@ int make_http_soap_request(zval *this_ptr, if (request != buf) { zend_string_release_ex(request, 0); } - add_soap_fault(this_ptr, "HTTP", "Could not connect to host", NULL, NULL); + add_soap_fault(this_ptr, "HTTP", "Could not connect to host", NULL, NULL, SOAP_GLOBAL(lang_en)); PG(allow_url_fopen) = old_allow_url_fopen; smart_str_free(&soap_headers_z); efree(http_msg); @@ -908,14 +908,14 @@ int make_http_soap_request(zval *this_ptr, convert_to_null(Z_CLIENT_HTTPURL_P(this_ptr)); convert_to_null(Z_CLIENT_HTTPSOCKET_P(this_ptr)); convert_to_null(Z_CLIENT_USE_PROXY_P(this_ptr)); - add_soap_fault(this_ptr, "HTTP", "Failed Sending HTTP SOAP request", NULL, NULL); + add_soap_fault(this_ptr, "HTTP", "Failed Sending HTTP SOAP request", NULL, NULL, SOAP_GLOBAL(lang_en)); smart_str_free(&soap_headers_z); efree(http_msg); return FALSE; } smart_str_free(&soap_headers); } else { - add_soap_fault(this_ptr, "HTTP", "Failed to create stream??", NULL, NULL); + add_soap_fault(this_ptr, "HTTP", "Failed to create stream??", NULL, NULL, SOAP_GLOBAL(lang_en)); smart_str_free(&soap_headers_z); efree(http_msg); return FALSE; @@ -932,7 +932,7 @@ int make_http_soap_request(zval *this_ptr, php_stream_close(stream); convert_to_null(Z_CLIENT_HTTPSOCKET_P(this_ptr)); convert_to_null(Z_CLIENT_USE_PROXY_P(this_ptr)); - add_soap_fault(this_ptr, "HTTP", "Error Fetching http headers", NULL, NULL); + add_soap_fault(this_ptr, "HTTP", "Error Fetching http headers", NULL, NULL, SOAP_GLOBAL(lang_en)); smart_str_free(&soap_headers_z); efree(http_msg); return FALSE; @@ -1121,7 +1121,7 @@ int make_http_soap_request(zval *this_ptr, zend_string_release_ex(http_headers, 0); convert_to_null(Z_CLIENT_HTTPSOCKET_P(this_ptr)); convert_to_null(Z_CLIENT_USE_PROXY_P(this_ptr)); - add_soap_fault(this_ptr, "HTTP", "Error Fetching http body, No Content-Length, connection closed or chunked data", NULL, NULL); + add_soap_fault(this_ptr, "HTTP", "Error Fetching http body, No Content-Length, connection closed or chunked data", NULL, NULL, SOAP_GLOBAL(lang_en)); if (http_msg) { efree(http_msg); } @@ -1180,7 +1180,7 @@ int make_http_soap_request(zval *this_ptr, phpurl = new_url; if (--redirect_max < 1) { - add_soap_fault(this_ptr, "HTTP", "Redirection limit reached, aborting", NULL, NULL); + add_soap_fault(this_ptr, "HTTP", "Redirection limit reached, aborting", NULL, NULL, SOAP_GLOBAL(lang_en)); smart_str_free(&soap_headers_z); efree(http_msg); return FALSE; @@ -1318,7 +1318,7 @@ int make_http_soap_request(zval *this_ptr, if (http_msg) { efree(http_msg); } - add_soap_fault(this_ptr, "HTTP", "Unknown Content-Encoding", NULL, NULL); + add_soap_fault(this_ptr, "HTTP", "Unknown Content-Encoding", NULL, NULL, SOAP_GLOBAL(lang_en)); return FALSE; } if (call_user_function(CG(function_table), (zval*)NULL, &func, &retval, 1, params) == SUCCESS && @@ -1334,7 +1334,7 @@ int make_http_soap_request(zval *this_ptr, efree(content_encoding); zend_string_release_ex(http_headers, 0); zend_string_release_ex(http_body, 0); - add_soap_fault(this_ptr, "HTTP", "Can't uncompress compressed response", NULL, NULL); + add_soap_fault(this_ptr, "HTTP", "Can't uncompress compressed response", NULL, NULL, SOAP_GLOBAL(lang_en)); if (http_msg) { efree(http_msg); } @@ -1368,7 +1368,7 @@ int make_http_soap_request(zval *this_ptr, if (error) { zval_ptr_dtor(return_value); ZVAL_UNDEF(return_value); - add_soap_fault(this_ptr, "HTTP", http_msg, NULL, NULL); + add_soap_fault(this_ptr, "HTTP", http_msg, NULL, NULL, SOAP_GLOBAL(lang_en)); efree(http_msg); return FALSE; } diff --git a/ext/soap/php_packet_soap.c b/ext/soap/php_packet_soap.c index fddb6b63874d9..31ed094ef2fc7 100644 --- a/ext/soap/php_packet_soap.c +++ b/ext/soap/php_packet_soap.c @@ -40,11 +40,11 @@ bool parse_packet_soap(zval *this_ptr, char *buffer, int buffer_size, sdlFunctio response = soap_xmlParseMemory(buffer, buffer_size); if (!response) { - add_soap_fault(this_ptr, "Client", "looks like we got no XML document", NULL, NULL); + add_soap_fault(this_ptr, "Client", "looks like we got no XML document", NULL, NULL, SOAP_GLOBAL(lang_en)); return false; } if (xmlGetIntSubset(response) != NULL) { - add_soap_fault(this_ptr, "Client", "DTD are not supported by SOAP", NULL, NULL); + add_soap_fault(this_ptr, "Client", "DTD are not supported by SOAP", NULL, NULL, SOAP_GLOBAL(lang_en)); xmlFreeDoc(response); return false; } @@ -63,7 +63,7 @@ bool parse_packet_soap(zval *this_ptr, char *buffer, int buffer_size, sdlFunctio envelope_ns = SOAP_1_2_ENV_NAMESPACE; soap_version = SOAP_1_2; } else { - add_soap_fault(this_ptr, "VersionMismatch", "Wrong Version", NULL, NULL); + add_soap_fault(this_ptr, "VersionMismatch", "Wrong Version", NULL, NULL, SOAP_GLOBAL(lang_en)); xmlFreeDoc(response); return false; } @@ -71,7 +71,7 @@ bool parse_packet_soap(zval *this_ptr, char *buffer, int buffer_size, sdlFunctio trav = trav->next; } if (env == NULL) { - add_soap_fault(this_ptr, "Client", "looks like we got XML without \"Envelope\" element", NULL, NULL); + add_soap_fault(this_ptr, "Client", "looks like we got XML without \"Envelope\" element", NULL, NULL, SOAP_GLOBAL(lang_en)); xmlFreeDoc(response); return false; } @@ -79,16 +79,16 @@ bool parse_packet_soap(zval *this_ptr, char *buffer, int buffer_size, sdlFunctio attr = env->properties; while (attr != NULL) { if (attr->ns == NULL) { - add_soap_fault(this_ptr, "Client", "A SOAP Envelope element cannot have non Namespace qualified attributes", NULL, NULL); + add_soap_fault(this_ptr, "Client", "A SOAP Envelope element cannot have non Namespace qualified attributes", NULL, NULL, SOAP_GLOBAL(lang_en)); xmlFreeDoc(response); return false; } else if (attr_is_equal_ex(attr,"encodingStyle",SOAP_1_2_ENV_NAMESPACE)) { if (soap_version == SOAP_1_2) { - add_soap_fault(this_ptr, "Client", "encodingStyle cannot be specified on the Envelope", NULL, NULL); + add_soap_fault(this_ptr, "Client", "encodingStyle cannot be specified on the Envelope", NULL, NULL, SOAP_GLOBAL(lang_en)); xmlFreeDoc(response); return false; } else if (strcmp((char*)attr->children->content, SOAP_1_1_ENC_NAMESPACE) != 0) { - add_soap_fault(this_ptr, "Client", "Unknown data encoding style", NULL, NULL); + add_soap_fault(this_ptr, "Client", "Unknown data encoding style", NULL, NULL, SOAP_GLOBAL(lang_en)); xmlFreeDoc(response); return false; } @@ -120,7 +120,7 @@ bool parse_packet_soap(zval *this_ptr, char *buffer, int buffer_size, sdlFunctio trav = trav->next; } if (body == NULL) { - add_soap_fault(this_ptr, "Client", "Body must be present in a SOAP envelope", NULL, NULL); + add_soap_fault(this_ptr, "Client", "Body must be present in a SOAP envelope", NULL, NULL, SOAP_GLOBAL(lang_en)); xmlFreeDoc(response); return false; } @@ -128,17 +128,17 @@ bool parse_packet_soap(zval *this_ptr, char *buffer, int buffer_size, sdlFunctio while (attr != NULL) { if (attr->ns == NULL) { if (soap_version == SOAP_1_2) { - add_soap_fault(this_ptr, "Client", "A SOAP Body element cannot have non Namespace qualified attributes", NULL, NULL); + add_soap_fault(this_ptr, "Client", "A SOAP Body element cannot have non Namespace qualified attributes", NULL, NULL, SOAP_GLOBAL(lang_en)); xmlFreeDoc(response); return false; } } else if (attr_is_equal_ex(attr,"encodingStyle",SOAP_1_2_ENV_NAMESPACE)) { if (soap_version == SOAP_1_2) { - add_soap_fault(this_ptr, "Client", "encodingStyle cannot be specified on the Body", NULL, NULL); + add_soap_fault(this_ptr, "Client", "encodingStyle cannot be specified on the Body", NULL, NULL, SOAP_GLOBAL(lang_en)); xmlFreeDoc(response); return false; } else if (strcmp((char*)attr->children->content, SOAP_1_1_ENC_NAMESPACE) != 0) { - add_soap_fault(this_ptr, "Client", "Unknown data encoding style", NULL, NULL); + add_soap_fault(this_ptr, "Client", "Unknown data encoding style", NULL, NULL, SOAP_GLOBAL(lang_en)); xmlFreeDoc(response); return false; } @@ -146,7 +146,7 @@ bool parse_packet_soap(zval *this_ptr, char *buffer, int buffer_size, sdlFunctio attr = attr->next; } if (trav != NULL && soap_version == SOAP_1_2) { - add_soap_fault(this_ptr, "Client", "A SOAP 1.2 envelope can contain only Header and Body", NULL, NULL); + add_soap_fault(this_ptr, "Client", "A SOAP 1.2 envelope can contain only Header and Body", NULL, NULL, SOAP_GLOBAL(lang_en)); xmlFreeDoc(response); return false; } @@ -155,16 +155,16 @@ bool parse_packet_soap(zval *this_ptr, char *buffer, int buffer_size, sdlFunctio attr = head->properties; while (attr != NULL) { if (attr->ns == NULL) { - add_soap_fault(this_ptr, "Client", "A SOAP Header element cannot have non Namespace qualified attributes", NULL, NULL); + add_soap_fault(this_ptr, "Client", "A SOAP Header element cannot have non Namespace qualified attributes", NULL, NULL, SOAP_GLOBAL(lang_en)); xmlFreeDoc(response); return false; } else if (attr_is_equal_ex(attr,"encodingStyle",SOAP_1_2_ENV_NAMESPACE)) { if (soap_version == SOAP_1_2) { - add_soap_fault(this_ptr, "Client", "encodingStyle cannot be specified on the Header", NULL, NULL); + add_soap_fault(this_ptr, "Client", "encodingStyle cannot be specified on the Header", NULL, NULL, SOAP_GLOBAL(lang_en)); xmlFreeDoc(response); return false; } else if (strcmp((char*)attr->children->content, SOAP_1_1_ENC_NAMESPACE) != 0) { - add_soap_fault(this_ptr, "Client", "Unknown data encoding style", NULL, NULL); + add_soap_fault(this_ptr, "Client", "Unknown data encoding style", NULL, NULL, SOAP_GLOBAL(lang_en)); xmlFreeDoc(response); return false; } @@ -177,6 +177,7 @@ bool parse_packet_soap(zval *this_ptr, char *buffer, int buffer_size, sdlFunctio fault = get_node_ex(body->children,"Fault",envelope_ns); if (fault != NULL) { char *faultcode = NULL; + zend_string *lang = ZSTR_EMPTY_ALLOC(); zend_string *faultstring = NULL, *faultactor = NULL; zval details; xmlNodePtr tmp; @@ -219,13 +220,19 @@ bool parse_packet_soap(zval *this_ptr, char *buffer, int buffer_size, sdlFunctio tmp = get_node(fault->children,"Reason"); if (tmp != NULL && tmp->children != NULL) { - /* TODO: lang attribute */ tmp = get_node(tmp->children,"Text"); if (tmp != NULL && tmp->children != NULL) { zval zv; master_to_zval(&zv, get_conversion(IS_STRING), tmp); convert_to_string(&zv) faultstring = Z_STR(zv); + + /* xml:lang is required by SOAP 1.2, but for BC reasons we allow it to be missing */ + xmlAttrPtr lang_attr = get_attribute_ex(tmp->properties, "lang", (const char *) XML_XML_NAMESPACE); + if (lang_attr != NULL && lang_attr->children != NULL) { + const char *lang_str = (const char *) lang_attr->children->content; + lang = zend_string_init(lang_str, strlen(lang_str), false); + } } } @@ -234,13 +241,14 @@ bool parse_packet_soap(zval *this_ptr, char *buffer, int buffer_size, sdlFunctio master_to_zval(&details, NULL, tmp); } } - add_soap_fault(this_ptr, faultcode, faultstring ? ZSTR_VAL(faultstring) : NULL, faultactor ? ZSTR_VAL(faultactor) : NULL, &details); + add_soap_fault(this_ptr, faultcode, faultstring ? ZSTR_VAL(faultstring) : NULL, faultactor ? ZSTR_VAL(faultactor) : NULL, &details, lang); if (faultstring) { zend_string_release_ex(faultstring, 0); } if (faultactor) { zend_string_release_ex(faultactor, 0); } + zend_string_release_ex(lang, false); if (Z_REFCOUNTED(details)) { Z_DELREF(details); } diff --git a/ext/soap/php_soap.h b/ext/soap/php_soap.h index 89dbf4408be2f..98e3d4af6f19d 100644 --- a/ext/soap/php_soap.h +++ b/ext/soap/php_soap.h @@ -170,6 +170,7 @@ ZEND_BEGIN_MODULE_GLOBALS(soap) HashTable wsdl_cache; int cur_uniq_ref; HashTable *ref_map; + zend_string *lang_en; ZEND_END_MODULE_GLOBALS(soap) #ifdef ZTS @@ -194,7 +195,7 @@ extern zend_class_entry* soap_sdl_class_entry; extern HashTable php_soap_defEncNs, php_soap_defEnc, php_soap_defEncIndex; -void add_soap_fault(zval *obj, char *fault_code, char *fault_string, char *fault_actor, zval *fault_detail); +void add_soap_fault(zval *obj, char *fault_code, char *fault_string, char *fault_actor, zval *fault_detail, zend_string *lang); #define soap_error0(severity, format) \ php_error(severity, "SOAP-ERROR: " format) diff --git a/ext/soap/soap.c b/ext/soap/soap.c index e0577ac648cae..cdb0216ebcf83 100644 --- a/ext/soap/soap.c +++ b/ext/soap/soap.c @@ -49,10 +49,13 @@ static void function_to_string(sdlFunctionPtr function, smart_str *buf); static void type_to_string(sdlTypePtr type, smart_str *buf, int level); static void clear_soap_fault(zval *obj); -static void set_soap_fault(zval *obj, const char *fault_code_ns, const char *fault_code, const char *fault_string, const char *fault_actor, zval *fault_detail, zend_string *name); -static void add_soap_fault_ex(zval *fault, zval *obj, char *fault_code, char *fault_string, char *fault_actor, zval *fault_detail); -static ZEND_NORETURN void soap_server_fault(char* code, char* string, char *actor, zval* details, zend_string *name); +static void set_soap_fault(zval *obj, const char *fault_code_ns, const char *fault_code, const char *fault_string, const char *fault_actor, zval *fault_detail, zend_string *name, zend_string *lang); +static void add_soap_fault_en(zval *obj, char *fault_code, char *fault_string, char *fault_actor, zval *fault_detail); +static void add_soap_fault_ex(zval *fault, zval *obj, char *fault_code, char *fault_string, char *fault_actor, zval *fault_detail, zend_string *lang); +static void add_soap_fault_ex_en(zval *fault, zval *obj, char *fault_code, char *fault_string, char *fault_actor, zval *fault_detail); +static ZEND_NORETURN void soap_server_fault(char* code, char* string, char *actor, zval* details, zend_string *name, zend_string *lang); static void soap_server_fault_ex(sdlFunctionPtr function, zval* fault, soapHeader* hdr); +static ZEND_NORETURN void soap_server_fault_en(char* code, char* string, char *actor, zval* details, zend_string *name); static sdlParamPtr get_param(sdlFunctionPtr function, const char *param_name, zend_ulong index, int); static sdlFunctionPtr get_function(sdlPtr sdl, const char *function_name, size_t function_name_length); @@ -156,6 +159,7 @@ static void soap_error_handler(int error_num, zend_string *error_filename, uint3 #define Z_FAULT_DETAIL_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), FAULT_PROP_START_OFFSET + 4)) #define Z_FAULT_NAME_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), FAULT_PROP_START_OFFSET + 5)) #define Z_FAULT_HEADERFAULT_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), FAULT_PROP_START_OFFSET + 6)) +#define Z_FAULT_LANG_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), FAULT_PROP_START_OFFSET + 7)) #define FETCH_THIS_SERVICE_NO_BAILOUT(ss) \ { \ @@ -448,6 +452,7 @@ static void php_soap_init_globals(zend_soap_globals *soap_globals) soap_globals->soap_version = SOAP_1_1; soap_globals->mem_cache = NULL; soap_globals->ref_map = NULL; + soap_globals->lang_en = zend_string_init_interned(ZEND_STRL("en"), true); } PHP_MSHUTDOWN_FUNCTION(soap) @@ -467,6 +472,7 @@ PHP_MSHUTDOWN_FUNCTION(soap) zend_hash_destroy(SOAP_GLOBAL(mem_cache)); free(SOAP_GLOBAL(mem_cache)); } + zend_string_release_ex(SOAP_GLOBAL(lang_en), true); UNREGISTER_INI_ENTRIES(); return SUCCESS; } @@ -645,6 +651,7 @@ static void soap_fault_dtor_properties(zval *obj) zval_ptr_dtor(Z_FAULT_DETAIL_P(obj)); zval_ptr_dtor(Z_FAULT_NAME_P(obj)); zval_ptr_dtor(Z_FAULT_HEADERFAULT_P(obj)); + zval_ptr_dtor(Z_FAULT_LANG_P(obj)); ZVAL_EMPTY_STRING(Z_FAULT_STRING_P(obj)); ZVAL_NULL(Z_FAULT_CODE_P(obj)); ZVAL_NULL(Z_FAULT_CODENS_P(obj)); @@ -652,6 +659,7 @@ static void soap_fault_dtor_properties(zval *obj) ZVAL_NULL(Z_FAULT_DETAIL_P(obj)); ZVAL_NULL(Z_FAULT_NAME_P(obj)); ZVAL_NULL(Z_FAULT_HEADERFAULT_P(obj)); + ZVAL_EMPTY_STRING(Z_FAULT_LANG_P(obj)); } /* {{{ SoapFault constructor */ @@ -660,11 +668,12 @@ PHP_METHOD(SoapFault, __construct) char *fault_string = NULL, *fault_code = NULL, *fault_actor = NULL, *fault_code_ns = NULL; size_t fault_string_len, fault_actor_len = 0, fault_code_len = 0; zend_string *name = NULL; + zend_string *lang = ZSTR_EMPTY_ALLOC(); zval *details = NULL, *headerfault = NULL, *this_ptr; zend_string *code_str; HashTable *code_ht; - ZEND_PARSE_PARAMETERS_START(2, 6) + ZEND_PARSE_PARAMETERS_START(2, 7) Z_PARAM_ARRAY_HT_OR_STR_OR_NULL(code_ht, code_str) Z_PARAM_STRING(fault_string, fault_string_len) Z_PARAM_OPTIONAL @@ -672,6 +681,7 @@ PHP_METHOD(SoapFault, __construct) Z_PARAM_ZVAL_OR_NULL(details) Z_PARAM_STR_OR_NULL(name) Z_PARAM_ZVAL_OR_NULL(headerfault) + Z_PARAM_PATH_STR(lang) ZEND_PARSE_PARAMETERS_END(); if (code_str) { @@ -700,7 +710,7 @@ PHP_METHOD(SoapFault, __construct) } this_ptr = ZEND_THIS; - set_soap_fault(this_ptr, fault_code_ns, fault_code, fault_string, fault_actor, details, name); + set_soap_fault(this_ptr, fault_code_ns, fault_code, fault_string, fault_actor, details, name, lang); if (headerfault != NULL) { ZVAL_COPY(Z_FAULT_HEADERFAULT_P(this_ptr), headerfault); } @@ -1245,10 +1255,10 @@ static void _soap_server_exception(soapServicePtr service, sdlFunctionPtr functi if (service->send_errors) { zval rv; zend_string *msg = zval_get_string(zend_read_property_ex(zend_ce_error, Z_OBJ(exception_object), ZSTR_KNOWN(ZEND_STR_MESSAGE), /* silent */ false, &rv)); - add_soap_fault_ex(&exception_object, this_ptr, "Server", ZSTR_VAL(msg), NULL, NULL); + add_soap_fault_ex_en(&exception_object, this_ptr, "Server", ZSTR_VAL(msg), NULL, NULL); zend_string_release_ex(msg, 0); } else { - add_soap_fault_ex(&exception_object, this_ptr, "Server", "Internal Error", NULL, NULL); + add_soap_fault_ex_en(&exception_object, this_ptr, "Server", "Internal Error", NULL, NULL); } soap_server_fault_ex(function, &exception_object, NULL); } @@ -1288,7 +1298,7 @@ PHP_METHOD(SoapServer, handle) SOAP_GLOBAL(soap_version) = service->version; if (arg && ZEND_SIZE_T_INT_OVFL(arg_len)) { - soap_server_fault("Server", "Input string is too long", NULL, NULL, NULL); + soap_server_fault_en("Server", "Input string is too long", NULL, NULL, NULL); SOAP_SERVER_END_CODE(); return; } @@ -1314,13 +1324,13 @@ PHP_METHOD(SoapServer, handle) php_stream_passthru(stream); php_stream_close(stream); } else { - soap_server_fault("Server", "Couldn't find WSDL", NULL, NULL, NULL); + soap_server_fault_en("Server", "Couldn't find WSDL", NULL, NULL, NULL); } SOAP_SERVER_END_CODE(); return; } else { - soap_server_fault("Server", "WSDL generation is not supported yet", NULL, NULL, NULL); + soap_server_fault_en("Server", "WSDL generation is not supported yet", NULL, NULL, NULL); /* sapi_add_header("Content-Type: text/xml; charset=utf-8", sizeof("Content-Type: text/xml; charset=utf-8"), 1); PUTS("\nchildren,"Envelope"); @@ -1405,7 +1415,7 @@ PHP_METHOD(SoapServer, handle) } } xmlFreeDoc(doc_request); - soap_server_fault("Server", "DTD are not supported by SOAP", NULL, NULL, NULL); + soap_server_fault_en("Server", "DTD are not supported by SOAP", NULL, NULL, NULL); } old_sdl = SOAP_GLOBAL(sdl); @@ -1463,7 +1473,7 @@ PHP_METHOD(SoapServer, handle) soap_obj = tmp_soap_p; } else if (Z_OBJCE_P(tmp_soap_p) == php_ce_incomplete_class) { /* See #51561, communicate limitation to user */ - soap_server_fault("Server", "SoapServer class was deserialized from the session prior to loading the class passed to SoapServer::setClass(). Start the session after loading all classes to resolve this issue.", NULL, NULL, NULL); + soap_server_fault_en("Server", "SoapServer class was deserialized from the session prior to loading the class passed to SoapServer::setClass(). Start the session after loading all classes to resolve this issue.", NULL, NULL, NULL); } } } @@ -1522,7 +1532,7 @@ PHP_METHOD(SoapServer, handle) #if 0 if (service->sdl && !h->function && !h->hdr) { if (h->mustUnderstand) { - soap_server_fault("MustUnderstand","Header not understood", NULL, NULL, NULL); + soap_server_fault_en("MustUnderstand","Header not understood", NULL, NULL, NULL); } else { continue; } @@ -1553,7 +1563,7 @@ PHP_METHOD(SoapServer, handle) goto fail; } } else if (h->mustUnderstand) { - soap_server_fault("MustUnderstand","Header not understood", NULL, NULL, NULL); + soap_server_fault_en("MustUnderstand","Header not understood", NULL, NULL, NULL); } } } @@ -1716,12 +1726,13 @@ PHP_METHOD(SoapServer, fault) size_t code_len, string_len, actor_len = 0; zval* details = NULL; zend_string *name = NULL; + zend_string *lang = ZSTR_EMPTY_ALLOC(); soapServicePtr service; xmlCharEncodingHandlerPtr old_encoding; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss|szS", + if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss|szSP", &code, &code_len, &string, &string_len, &actor, &actor_len, &details, - &name) == FAILURE) { + &name, &lang) == FAILURE) { RETURN_THROWS(); } @@ -1730,7 +1741,7 @@ PHP_METHOD(SoapServer, fault) old_encoding = SOAP_GLOBAL(encoding); SOAP_GLOBAL(encoding) = service->encoding; - soap_server_fault(code, string, actor, details, name); + soap_server_fault(code, string, actor, details, name, lang); SOAP_GLOBAL(encoding) = old_encoding; SOAP_SERVER_END_CODE(); @@ -1826,18 +1837,23 @@ static void soap_server_fault_ex(sdlFunctionPtr function, zval* fault, soapHeade } /* }}} */ -static ZEND_NORETURN void soap_server_fault(char* code, char* string, char *actor, zval* details, zend_string* name) /* {{{ */ +static ZEND_NORETURN void soap_server_fault(char* code, char* string, char *actor, zval* details, zend_string* name, zend_string *lang) /* {{{ */ { zval ret; ZVAL_NULL(&ret); - set_soap_fault(&ret, NULL, code, string, actor, details, name); + set_soap_fault(&ret, NULL, code, string, actor, details, name, lang); /* TODO: Which function */ soap_server_fault_ex(NULL, &ret, NULL); zend_bailout(); } /* }}} */ +static ZEND_NORETURN void soap_server_fault_en(char* code, char* string, char *actor, zval* details, zend_string* name) +{ + soap_server_fault(code, string, actor, details, name, SOAP_GLOBAL(lang_en)); +} + static zend_never_inline ZEND_COLD void soap_real_error_handler(int error_num, zend_string *error_filename, const uint32_t error_lineno, zend_string *message) /* {{{ */ { bool _old_in_compilation; @@ -1861,7 +1877,7 @@ static zend_never_inline ZEND_COLD void soap_real_error_handler(int error_num, z code = "Client"; } - add_soap_fault_ex(&fault, &SOAP_GLOBAL(error_object), code, ZSTR_VAL(message), NULL, NULL); + add_soap_fault_ex_en(&fault, &SOAP_GLOBAL(error_object), code, ZSTR_VAL(message), NULL, NULL); Z_ADDREF(fault); zend_throw_exception_object(&fault); zend_bailout(); @@ -1904,7 +1920,7 @@ static zend_never_inline ZEND_COLD void soap_real_error_handler(int error_num, z } ZVAL_NULL(&fault_obj); - set_soap_fault(&fault_obj, NULL, code, ZSTR_VAL(buffer), NULL, &outbuf, NULL); + set_soap_fault(&fault_obj, NULL, code, ZSTR_VAL(buffer), NULL, &outbuf, NULL, SOAP_GLOBAL(lang_en)); zend_string_release(buffer); fault = 1; } @@ -2215,7 +2231,7 @@ static bool do_request(zval *this_ptr, xmlDoc *request, const char *location, co xmlDocDumpMemory(request, (xmlChar**)&buf, &buf_size); if (!buf) { - add_soap_fault(this_ptr, "HTTP", "Error build soap request", NULL, NULL); + add_soap_fault_en(this_ptr, "HTTP", "Error build soap request", NULL, NULL); return false; } @@ -2245,7 +2261,7 @@ static bool do_request(zval *this_ptr, xmlDoc *request, const char *location, co if (EG(exception) && instanceof_function(EG(exception)->ce, zend_ce_error)) { /* Programmer error in __doRequest() implementation, let it bubble up. */ } else if (Z_TYPE_P(Z_CLIENT_SOAP_FAULT_P(this_ptr)) != IS_OBJECT) { - add_soap_fault(this_ptr, "Client", "SoapClient::__doRequest() returned non string value", NULL, NULL); + add_soap_fault_en(this_ptr, "Client", "SoapClient::__doRequest() returned non string value", NULL, NULL); } ret = false; } else if (Z_TYPE_P(trace) == IS_TRUE) { @@ -2405,15 +2421,15 @@ static void do_soap_call(zend_execute_data *execute_data, smart_str_append(&error,function); smart_str_appends(&error,"\") is not a valid method for this service"); smart_str_0(&error); - add_soap_fault(this_ptr, "Client", ZSTR_VAL(error.s), NULL, NULL); + add_soap_fault_en(this_ptr, "Client", ZSTR_VAL(error.s), NULL, NULL); smart_str_free(&error); } } else { zval *uri = Z_CLIENT_URI_P(this_ptr); if (Z_TYPE_P(uri) != IS_STRING) { - add_soap_fault(this_ptr, "Client", "Error finding \"uri\" property", NULL, NULL); + add_soap_fault_en(this_ptr, "Client", "Error finding \"uri\" property", NULL, NULL); } else if (location == NULL) { - add_soap_fault(this_ptr, "Client", "Error could not find \"location\" property", NULL, NULL); + add_soap_fault_en(this_ptr, "Client", "Error could not find \"location\" property", NULL, NULL); } else { if (call_uri == NULL) { call_uri = Z_STR_P(uri); @@ -2450,7 +2466,7 @@ static void do_soap_call(zend_execute_data *execute_data, if (Z_TYPE_P(fault) == IS_OBJECT) { ZVAL_COPY(return_value, fault); } else { - add_soap_fault_ex(return_value, this_ptr, "Client", "Unknown Error", NULL, NULL); + add_soap_fault_ex_en(return_value, this_ptr, "Client", "Unknown Error", NULL, NULL); Z_ADDREF_P(return_value); } } else { @@ -2898,10 +2914,10 @@ static void clear_soap_fault(zval *obj) /* {{{ */ } /* }}} */ -static void add_soap_fault_ex(zval *fault, zval *obj, char *fault_code, char *fault_string, char *fault_actor, zval *fault_detail) /* {{{ */ +static void add_soap_fault_ex(zval *fault, zval *obj, char *fault_code, char *fault_string, char *fault_actor, zval *fault_detail, zend_string *lang) /* {{{ */ { ZVAL_NULL(fault); - set_soap_fault(fault, NULL, fault_code, fault_string, fault_actor, fault_detail, NULL); + set_soap_fault(fault, NULL, fault_code, fault_string, fault_actor, fault_detail, NULL, lang); zval *target; if (instanceof_function(Z_OBJCE_P(obj), soap_class_entry)) { target = Z_CLIENT_SOAP_FAULT_P(obj); @@ -2915,14 +2931,24 @@ static void add_soap_fault_ex(zval *fault, zval *obj, char *fault_code, char *fa } /* }}} */ -void add_soap_fault(zval *obj, char *fault_code, char *fault_string, char *fault_actor, zval *fault_detail) /* {{{ */ +static void add_soap_fault_ex_en(zval *fault, zval *obj, char *fault_code, char *fault_string, char *fault_actor, zval *fault_detail) +{ + add_soap_fault_ex(fault, obj, fault_code, fault_string, fault_actor, fault_detail, SOAP_GLOBAL(lang_en)); +} + +void add_soap_fault(zval *obj, char *fault_code, char *fault_string, char *fault_actor, zval *fault_detail, zend_string *lang) /* {{{ */ { zval fault; - add_soap_fault_ex(&fault, obj, fault_code, fault_string, fault_actor, fault_detail); + add_soap_fault_ex(&fault, obj, fault_code, fault_string, fault_actor, fault_detail, lang); } /* }}} */ -static void set_soap_fault(zval *obj, const char *fault_code_ns, const char *fault_code, const char *fault_string, const char *fault_actor, zval *fault_detail, zend_string *name) /* {{{ */ +static void add_soap_fault_en(zval *obj, char *fault_code, char *fault_string, char *fault_actor, zval *fault_detail) +{ + add_soap_fault(obj, fault_code, fault_string, fault_actor, fault_detail, SOAP_GLOBAL(lang_en)); +} + +static void set_soap_fault(zval *obj, const char *fault_code_ns, const char *fault_code, const char *fault_string, const char *fault_actor, zval *fault_detail, zend_string *name, zend_string *lang) /* {{{ */ { if (Z_TYPE_P(obj) != IS_OBJECT) { object_init_ex(obj, soap_fault_class_entry); @@ -2973,6 +2999,7 @@ static void set_soap_fault(zval *obj, const char *fault_code_ns, const char *fau if (name != NULL) { ZVAL_STR_COPY(Z_FAULT_NAME_P(obj), name); } + ZVAL_STR_COPY(Z_FAULT_LANG_P(obj), lang); } /* }}} */ @@ -3043,7 +3070,7 @@ static void deserialize_parameters(xmlNodePtr params, sdlFunctionPtr function, u sdlParamPtr param = NULL; if (function != NULL && (param = zend_hash_index_find_ptr(function->requestParameters, cur_param)) == NULL) { - soap_server_fault("Client", "Error cannot find parameter", NULL, NULL, NULL); + soap_server_fault_en("Client", "Error cannot find parameter", NULL, NULL, NULL); } if (param == NULL) { enc = NULL; @@ -3058,7 +3085,7 @@ static void deserialize_parameters(xmlNodePtr params, sdlFunctionPtr function, u } } if (num_of_params > cur_param) { - soap_server_fault("Client","Missing parameter", NULL, NULL, NULL); + soap_server_fault_en("Client","Missing parameter", NULL, NULL, NULL); } (*parameters) = tmp_parameters; (*num_params) = num_of_params; @@ -3145,7 +3172,7 @@ static xmlNodePtr get_envelope(xmlNodePtr trav, int *version, char **envelope_ns return trav; } - soap_server_fault("VersionMismatch", "Wrong Version", NULL, NULL, NULL); + soap_server_fault_en("VersionMismatch", "Wrong Version", NULL, NULL, NULL); } trav = trav->next; } @@ -3165,18 +3192,18 @@ static sdlFunctionPtr deserialize_function_call(sdlPtr sdl, xmlDocPtr request, c /* Get element */ env = get_envelope(request->children, version, &envelope_ns); if (!env) { - soap_server_fault("Client", "looks like we got XML without \"Envelope\" element", NULL, NULL, NULL); + soap_server_fault_en("Client", "looks like we got XML without \"Envelope\" element", NULL, NULL, NULL); } attr = env->properties; while (attr != NULL) { if (attr->ns == NULL) { - soap_server_fault("Client", "A SOAP Envelope element cannot have non Namespace qualified attributes", NULL, NULL, NULL); + soap_server_fault_en("Client", "A SOAP Envelope element cannot have non Namespace qualified attributes", NULL, NULL, NULL); } else if (attr_is_equal_ex(attr,"encodingStyle",SOAP_1_2_ENV_NAMESPACE)) { if (*version == SOAP_1_2) { - soap_server_fault("Client", "encodingStyle cannot be specified on the Envelope", NULL, NULL, NULL); + soap_server_fault_en("Client", "encodingStyle cannot be specified on the Envelope", NULL, NULL, NULL); } else if (strcmp((char*)attr->children->content,SOAP_1_1_ENC_NAMESPACE) != 0) { - soap_server_fault("Client", "Unknown data encoding style", NULL, NULL, NULL); + soap_server_fault_en("Client", "Unknown data encoding style", NULL, NULL, NULL); } } attr = attr->next; @@ -3206,26 +3233,26 @@ static sdlFunctionPtr deserialize_function_call(sdlPtr sdl, xmlDocPtr request, c trav = trav->next; } if (body == NULL) { - soap_server_fault("Client", "Body must be present in a SOAP envelope", NULL, NULL, NULL); + soap_server_fault_en("Client", "Body must be present in a SOAP envelope", NULL, NULL, NULL); } attr = body->properties; while (attr != NULL) { if (attr->ns == NULL) { if (*version == SOAP_1_2) { - soap_server_fault("Client", "A SOAP Body element cannot have non Namespace qualified attributes", NULL, NULL, NULL); + soap_server_fault_en("Client", "A SOAP Body element cannot have non Namespace qualified attributes", NULL, NULL, NULL); } } else if (attr_is_equal_ex(attr,"encodingStyle",SOAP_1_2_ENV_NAMESPACE)) { if (*version == SOAP_1_2) { - soap_server_fault("Client", "encodingStyle cannot be specified on the Body", NULL, NULL, NULL); + soap_server_fault_en("Client", "encodingStyle cannot be specified on the Body", NULL, NULL, NULL); } else if (strcmp((char*)attr->children->content,SOAP_1_1_ENC_NAMESPACE) != 0) { - soap_server_fault("Client", "Unknown data encoding style", NULL, NULL, NULL); + soap_server_fault_en("Client", "Unknown data encoding style", NULL, NULL, NULL); } } attr = attr->next; } if (trav != NULL && *version == SOAP_1_2) { - soap_server_fault("Client", "A SOAP 1.2 envelope can contain only Header and Body", NULL, NULL, NULL); + soap_server_fault_en("Client", "A SOAP 1.2 envelope can contain only Header and Body", NULL, NULL, NULL); } func = NULL; @@ -3234,7 +3261,7 @@ static sdlFunctionPtr deserialize_function_call(sdlPtr sdl, xmlDocPtr request, c if (trav->type == XML_ELEMENT_NODE) { /* if (func != NULL) { - soap_server_fault("Client", "looks like we got \"Body\" with several functions call", NULL, NULL, NULL); + soap_server_fault_en("Client", "looks like we got \"Body\" with several functions call", NULL, NULL, NULL); } */ func = trav; @@ -3251,19 +3278,19 @@ static sdlFunctionPtr deserialize_function_call(sdlPtr sdl, xmlDocPtr request, c if (function) { ZVAL_STRING(function_name, (char *)function->functionName); } else { - soap_server_fault("Client", "looks like we got \"Body\" without function call", NULL, NULL, NULL); + soap_server_fault_en("Client", "looks like we got \"Body\" without function call", NULL, NULL, NULL); } } } else { if (*version == SOAP_1_1) { attr = get_attribute_ex(func->properties,"encodingStyle",SOAP_1_1_ENV_NAMESPACE); if (attr && strcmp((char*)attr->children->content,SOAP_1_1_ENC_NAMESPACE) != 0) { - soap_server_fault("Client","Unknown Data Encoding Style", NULL, NULL, NULL); + soap_server_fault_en("Client","Unknown Data Encoding Style", NULL, NULL, NULL); } } else { attr = get_attribute_ex(func->properties,"encodingStyle",SOAP_1_2_ENV_NAMESPACE); if (attr && strcmp((char*)attr->children->content,SOAP_1_2_ENC_NAMESPACE) != 0) { - soap_server_fault("DataEncodingUnknown","Unknown Data Encoding Style", NULL, NULL, NULL); + soap_server_fault_en("DataEncodingUnknown","Unknown Data Encoding Style", NULL, NULL, NULL); } } if (!function) { @@ -3271,7 +3298,7 @@ static sdlFunctionPtr deserialize_function_call(sdlPtr sdl, xmlDocPtr request, c } if (sdl != NULL && function == NULL) { if (*version == SOAP_1_2) { - soap_server_fault("rpc:ProcedureNotPresent","Procedure not present", NULL, NULL, NULL); + soap_server_fault_en("rpc:ProcedureNotPresent","Procedure not present", NULL, NULL, NULL); } else { php_error(E_ERROR, "Procedure '%s' not present", func->name); } @@ -3285,12 +3312,12 @@ static sdlFunctionPtr deserialize_function_call(sdlPtr sdl, xmlDocPtr request, c attr = head->properties; while (attr != NULL) { if (attr->ns == NULL) { - soap_server_fault("Client", "A SOAP Header element cannot have non Namespace qualified attributes", NULL, NULL, NULL); + soap_server_fault_en("Client", "A SOAP Header element cannot have non Namespace qualified attributes", NULL, NULL, NULL); } else if (attr_is_equal_ex(attr,"encodingStyle",SOAP_1_2_ENV_NAMESPACE)) { if (*version == SOAP_1_2) { - soap_server_fault("Client", "encodingStyle cannot be specified on the Header", NULL, NULL, NULL); + soap_server_fault_en("Client", "encodingStyle cannot be specified on the Header", NULL, NULL, NULL); } else if (strcmp((char*)attr->children->content,SOAP_1_1_ENC_NAMESPACE) != 0) { - soap_server_fault("Client", "Unknown data encoding style", NULL, NULL, NULL); + soap_server_fault_en("Client", "Unknown data encoding style", NULL, NULL, NULL); } } attr = attr->next; @@ -3304,7 +3331,7 @@ static sdlFunctionPtr deserialize_function_call(sdlPtr sdl, xmlDocPtr request, c if (*version == SOAP_1_1) { attr = get_attribute_ex(hdr_func->properties,"encodingStyle",SOAP_1_1_ENV_NAMESPACE); if (attr && strcmp((char*)attr->children->content,SOAP_1_1_ENC_NAMESPACE) != 0) { - soap_server_fault("Client","Unknown Data Encoding Style", NULL, NULL, NULL); + soap_server_fault_en("Client","Unknown Data Encoding Style", NULL, NULL, NULL); } attr = get_attribute_ex(hdr_func->properties,"actor",envelope_ns); if (attr != NULL) { @@ -3316,7 +3343,7 @@ static sdlFunctionPtr deserialize_function_call(sdlPtr sdl, xmlDocPtr request, c } else if (*version == SOAP_1_2) { attr = get_attribute_ex(hdr_func->properties,"encodingStyle",SOAP_1_2_ENV_NAMESPACE); if (attr && strcmp((char*)attr->children->content,SOAP_1_2_ENC_NAMESPACE) != 0) { - soap_server_fault("DataEncodingUnknown","Unknown Data Encoding Style", NULL, NULL, NULL); + soap_server_fault_en("DataEncodingUnknown","Unknown Data Encoding Style", NULL, NULL, NULL); } attr = get_attribute_ex(hdr_func->properties,"role",envelope_ns); if (attr != NULL) { @@ -3336,7 +3363,7 @@ static sdlFunctionPtr deserialize_function_call(sdlPtr sdl, xmlDocPtr request, c strcmp((char*)attr->children->content,"false") == 0) { mustUnderstand = 0; } else { - soap_server_fault("Client","mustUnderstand value is not boolean", NULL, NULL, NULL); + soap_server_fault_en("Client","mustUnderstand value is not boolean", NULL, NULL, NULL); } } h = emalloc(sizeof(soapHeader)); @@ -3565,7 +3592,7 @@ static xmlDocPtr serialize_response_call(sdlFunctionPtr function, const char *fu ns = xmlNewNs(envelope, BAD_CAST(SOAP_1_2_ENV_NAMESPACE), BAD_CAST(SOAP_1_2_ENV_NS_PREFIX)); xmlSetNs(envelope,ns); } else { - soap_server_fault("Server", "Unknown SOAP version", NULL, NULL, NULL); + soap_server_fault_en("Server", "Unknown SOAP version", NULL, NULL, NULL); } xmlDocSetRootElement(doc, envelope); @@ -3734,6 +3761,11 @@ static xmlDocPtr serialize_response_call(sdlFunctionPtr function, const char *fu node = master_to_xml(get_conversion(IS_STRING), tmp, SOAP_LITERAL, node); xmlNodeSetName(node, BAD_CAST("Text")); xmlSetNs(node, ns); + + /* xml:lang attribute is required for in SOAP 1.2 */ + tmp = Z_FAULT_LANG_P(ret); + zend_string *lang = Z_STR_P(tmp); + xmlNodeSetLang(node, BAD_CAST ZSTR_VAL(lang)); } detail_name = SOAP_1_2_ENV_NS_PREFIX":Detail"; } diff --git a/ext/soap/soap.stub.php b/ext/soap/soap.stub.php index 82f884e24b308..2520bd1346963 100644 --- a/ext/soap/soap.stub.php +++ b/ext/soap/soap.stub.php @@ -477,8 +477,9 @@ class SoapFault extends Exception public mixed $detail = null; public ?string $_name = null; public mixed $headerfault = null; + public string $lang = ""; - public function __construct(array|string|null $code, string $string, ?string $actor = null, mixed $details = null, ?string $name = null, mixed $headerFault = null) {} + public function __construct(array|string|null $code, string $string, ?string $actor = null, mixed $details = null, ?string $name = null, mixed $headerFault = null, string $lang = "") {} public function __toString(): string {} } @@ -502,7 +503,7 @@ class SoapServer public function __construct(?string $wsdl, array $options = []) {} /** @tentative-return-type */ - public function fault(string $code, string $string, string $actor = "", mixed $details = null, string $name = ""): void {} + public function fault(string $code, string $string, string $actor = "", mixed $details = null, string $name = "", string $lang = ""): void {} /** @tentative-return-type */ public function addSoapHeader(SoapHeader $header): void {} diff --git a/ext/soap/soap_arginfo.h b/ext/soap/soap_arginfo.h index 9ec093c5c9cd2..e932f2af71093 100644 --- a/ext/soap/soap_arginfo.h +++ b/ext/soap/soap_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 7712aba90b16090fbe7c124c1e3f26b2cc3e2ab2 */ + * Stub hash: 78a27b18c6b4007494a6aed9acc5f6e99c6f0350 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_use_soap_error_handler, 0, 0, _IS_BOOL, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, enable, _IS_BOOL, 0, "true") @@ -29,6 +29,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_SoapFault___construct, 0, 0, 2) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, details, IS_MIXED, 0, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, name, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, headerFault, IS_MIXED, 0, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, lang, IS_STRING, 0, "\"\"") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_SoapFault___toString, 0, 0, IS_STRING, 0) @@ -54,6 +55,7 @@ ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_SoapServer_fault ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, actor, IS_STRING, 0, "\"\"") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, details, IS_MIXED, 0, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, name, IS_STRING, 0, "\"\"") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, lang, IS_STRING, 0, "\"\"") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_SoapServer_addSoapHeader, 0, 1, IS_VOID, 0) @@ -444,6 +446,12 @@ static zend_class_entry *register_class_SoapFault(zend_class_entry *class_entry_ zend_declare_typed_property(class_entry, property_headerfault_name, &property_headerfault_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ANY)); zend_string_release(property_headerfault_name); + zval property_lang_default_value; + ZVAL_EMPTY_STRING(&property_lang_default_value); + zend_string *property_lang_name = zend_string_init("lang", sizeof("lang") - 1, 1); + zend_declare_typed_property(class_entry, property_lang_name, &property_lang_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); + zend_string_release(property_lang_name); + return class_entry; } diff --git a/ext/soap/tests/bugs/bug38005.phpt b/ext/soap/tests/bugs/bug38005.phpt index e77278b1b6b64..ca3944ebc3db3 100644 --- a/ext/soap/tests/bugs/bug38005.phpt +++ b/ext/soap/tests/bugs/bug38005.phpt @@ -42,4 +42,4 @@ echo($client->__getLastResponse()); --EXPECT-- This is our fault: Ä -TestThis is our fault: Ä +TestThis is our fault: Ä diff --git a/ext/soap/tests/bugs/bug68996.phpt b/ext/soap/tests/bugs/bug68996.phpt index 618f5ec730e7e..bcc0e66cbebf9 100644 --- a/ext/soap/tests/bugs/bug68996.phpt +++ b/ext/soap/tests/bugs/bug68996.phpt @@ -56,4 +56,4 @@ handleFormatted($s, $HTTP_RAW_POST_DATA); some msg -some msg +some msg diff --git a/ext/soap/tests/bugs/bug73037.phpt b/ext/soap/tests/bugs/bug73037.phpt index da89382beb2c0..7a5b997767729 100644 --- a/ext/soap/tests/bugs/bug73037.phpt +++ b/ext/soap/tests/bugs/bug73037.phpt @@ -109,12 +109,13 @@ HDRS; $out .= fread($fp, 1024); } - $pos = strpos($out, ""); + $marker = ''; + $pos = strpos($out, $marker); if (false === $pos) { echo $out; goto cleanup; } - $pos0 = $pos + strlen(""); + $pos0 = $pos + strlen($marker); $pos = strpos($out, ""); if (false === $pos) { echo $out; diff --git a/ext/soap/tests/fault_language.phpt b/ext/soap/tests/fault_language.phpt new file mode 100644 index 0000000000000..9cadad2619f03 --- /dev/null +++ b/ext/soap/tests/fault_language.phpt @@ -0,0 +1,29 @@ +--TEST-- +SOAP Server: user fault with language +--EXTENSIONS-- +soap +--FILE-- +fault("MyFault", "My fault string", lang: "custom"); +} + +$server = new SoapServer(null, ['uri' => 'http://testuri.org', 'soap_version' => SOAP_1_2]); +$server->addFunction("test"); + +$HTTP_RAW_POST_DATA = << + + + + + +EOF; + +$server->handle($HTTP_RAW_POST_DATA); +?> +--EXPECT-- + +MyFaultMy fault string diff --git a/ext/soap/tests/soap12/T12.phpt b/ext/soap/tests/soap12/T12.phpt index d3f21ca601446..e659ba0e216dd 100644 --- a/ext/soap/tests/soap12/T12.phpt +++ b/ext/soap/tests/soap12/T12.phpt @@ -20,4 +20,4 @@ include "soap12-test.inc"; ?> --EXPECT-- -env:MustUnderstandHeader not understood +env:MustUnderstandHeader not understood diff --git a/ext/soap/tests/soap12/T13.phpt b/ext/soap/tests/soap12/T13.phpt index 011409b189a46..9a32367164273 100644 --- a/ext/soap/tests/soap12/T13.phpt +++ b/ext/soap/tests/soap12/T13.phpt @@ -20,4 +20,4 @@ include "soap12-test.inc"; ?> --EXPECT-- -env:MustUnderstandHeader not understood +env:MustUnderstandHeader not understood diff --git a/ext/soap/tests/soap12/T14.phpt b/ext/soap/tests/soap12/T14.phpt index 30916455a471d..4ed23550b099b 100644 --- a/ext/soap/tests/soap12/T14.phpt +++ b/ext/soap/tests/soap12/T14.phpt @@ -20,4 +20,4 @@ include "soap12-test.inc"; ?> --EXPECT-- -env:SendermustUnderstand value is not boolean +env:SendermustUnderstand value is not boolean diff --git a/ext/soap/tests/soap12/T23.phpt b/ext/soap/tests/soap12/T23.phpt index 1df101db6aa98..4f7f02893cf89 100644 --- a/ext/soap/tests/soap12/T23.phpt +++ b/ext/soap/tests/soap12/T23.phpt @@ -21,4 +21,4 @@ include "soap12-test.inc"; ?> --EXPECT-- -env:SendermustUnderstand value is not boolean +env:SendermustUnderstand value is not boolean diff --git a/ext/soap/tests/soap12/T24.phpt b/ext/soap/tests/soap12/T24.phpt index a5b304d9843c2..f170b66c704ad 100644 --- a/ext/soap/tests/soap12/T24.phpt +++ b/ext/soap/tests/soap12/T24.phpt @@ -18,4 +18,4 @@ include "soap12-test.inc"; ?> --EXPECT-- -env:VersionMismatchWrong Version +env:VersionMismatchWrong Version diff --git a/ext/soap/tests/soap12/T25.phpt b/ext/soap/tests/soap12/T25.phpt index 1993d6723a35f..f6e9bd8b4867c 100644 --- a/ext/soap/tests/soap12/T25.phpt +++ b/ext/soap/tests/soap12/T25.phpt @@ -19,4 +19,4 @@ include "soap12-test.inc"; ?> --EXPECT-- -env:ReceiverDTD are not supported by SOAP +env:ReceiverDTD are not supported by SOAP diff --git a/ext/soap/tests/soap12/T27.phpt b/ext/soap/tests/soap12/T27.phpt index 097a0f6353072..09591b7faebdd 100644 --- a/ext/soap/tests/soap12/T27.phpt +++ b/ext/soap/tests/soap12/T27.phpt @@ -25,4 +25,4 @@ include "soap12-test.inc"; ?> --EXPECT-- -env:ReceiverSOAP-ERROR: Encoding: Violation of encoding rules +env:ReceiverSOAP-ERROR: Encoding: Violation of encoding rules diff --git a/ext/soap/tests/soap12/T28.phpt b/ext/soap/tests/soap12/T28.phpt index 23deba54acd0a..01a3795e47094 100644 --- a/ext/soap/tests/soap12/T28.phpt +++ b/ext/soap/tests/soap12/T28.phpt @@ -18,4 +18,4 @@ include "soap12-test.inc"; ?> --EXPECT-- -env:SenderencodingStyle cannot be specified on the Body +env:SenderencodingStyle cannot be specified on the Body diff --git a/ext/soap/tests/soap12/T33.phpt b/ext/soap/tests/soap12/T33.phpt index bd9667186a062..eccb85234d1cc 100644 --- a/ext/soap/tests/soap12/T33.phpt +++ b/ext/soap/tests/soap12/T33.phpt @@ -17,4 +17,4 @@ include "soap12-test.inc"; ?> --EXPECT-- -rpc:ProcedureNotPresentProcedure not present +rpc:ProcedureNotPresentProcedure not present diff --git a/ext/soap/tests/soap12/T35.phpt b/ext/soap/tests/soap12/T35.phpt index a2d6436b7b28c..76d72be4372bc 100644 --- a/ext/soap/tests/soap12/T35.phpt +++ b/ext/soap/tests/soap12/T35.phpt @@ -21,4 +21,4 @@ include "soap12-test.inc"; ?> --EXPECT-- -env:MustUnderstandHeader not understood +env:MustUnderstandHeader not understood diff --git a/ext/soap/tests/soap12/T36.phpt b/ext/soap/tests/soap12/T36.phpt index 86501ff740427..44cac71747c61 100644 --- a/ext/soap/tests/soap12/T36.phpt +++ b/ext/soap/tests/soap12/T36.phpt @@ -20,4 +20,4 @@ include "soap12-test.inc"; ?> --EXPECT-- -env:MustUnderstandHeader not understood +env:MustUnderstandHeader not understood diff --git a/ext/soap/tests/soap12/T39.phpt b/ext/soap/tests/soap12/T39.phpt index 735e4882a6f5b..2d46655278aa5 100644 --- a/ext/soap/tests/soap12/T39.phpt +++ b/ext/soap/tests/soap12/T39.phpt @@ -19,4 +19,4 @@ include "soap12-test.inc"; ?> --EXPECT-- -env:SendermustUnderstand value is not boolean +env:SendermustUnderstand value is not boolean diff --git a/ext/soap/tests/soap12/T56.phpt b/ext/soap/tests/soap12/T56.phpt index 7922bf78d1828..8feddb651d42b 100644 --- a/ext/soap/tests/soap12/T56.phpt +++ b/ext/soap/tests/soap12/T56.phpt @@ -30,4 +30,4 @@ include "soap12-test.inc"; ?> --EXPECT-- -env:ReceiverSOAP-ERROR: Encoding: Unresolved reference '#data-2' +env:ReceiverSOAP-ERROR: Encoding: Unresolved reference '#data-2' diff --git a/ext/soap/tests/soap12/T58.phpt b/ext/soap/tests/soap12/T58.phpt index 59a9eba0b6116..69c20a4f8670e 100644 --- a/ext/soap/tests/soap12/T58.phpt +++ b/ext/soap/tests/soap12/T58.phpt @@ -24,4 +24,4 @@ include "soap12-test.inc"; ?> --EXPECT-- -env:ReceiverSOAP-ERROR: Encoding: Violation of encoding rules +env:ReceiverSOAP-ERROR: Encoding: Violation of encoding rules diff --git a/ext/soap/tests/soap12/T59.phpt b/ext/soap/tests/soap12/T59.phpt index 4413301730467..69d597d9f96e7 100644 --- a/ext/soap/tests/soap12/T59.phpt +++ b/ext/soap/tests/soap12/T59.phpt @@ -25,4 +25,4 @@ include "soap12-test.inc"; ?> --EXPECT-- -env:ReceiverSOAP-ERROR: Encoding: Violation of id and ref information items '#data' +env:ReceiverSOAP-ERROR: Encoding: Violation of id and ref information items '#data' diff --git a/ext/soap/tests/soap12/T61.phpt b/ext/soap/tests/soap12/T61.phpt index 79f5c1efff5ad..44fc42d0e09ab 100644 --- a/ext/soap/tests/soap12/T61.phpt +++ b/ext/soap/tests/soap12/T61.phpt @@ -25,4 +25,4 @@ include "soap12-test.inc"; ?> --EXPECT-- -env:ReceiverSOAP-ERROR: Encoding: '*' may only be first arraySize value in list +env:ReceiverSOAP-ERROR: Encoding: '*' may only be first arraySize value in list diff --git a/ext/soap/tests/soap12/T63.phpt b/ext/soap/tests/soap12/T63.phpt index 4706241668de7..a8cf3c431b24b 100644 --- a/ext/soap/tests/soap12/T63.phpt +++ b/ext/soap/tests/soap12/T63.phpt @@ -21,5 +21,5 @@ include "soap12-test.inc"; ?> --EXPECT-- -Country code must be 2 letters.env:SenderNot a valid country code +Country code must be 2 letters.env:SenderNot a valid country code ok diff --git a/ext/soap/tests/soap12/T64.phpt b/ext/soap/tests/soap12/T64.phpt index 8196644e0472d..aea1caab38cd4 100644 --- a/ext/soap/tests/soap12/T64.phpt +++ b/ext/soap/tests/soap12/T64.phpt @@ -21,4 +21,4 @@ include "soap12-test.inc"; ?> --EXPECT-- -env:ReceiverDTD are not supported by SOAP +env:ReceiverDTD are not supported by SOAP diff --git a/ext/soap/tests/soap12/T65.phpt b/ext/soap/tests/soap12/T65.phpt index 0c6f3b953e4f0..9ddd97f5d598a 100644 --- a/ext/soap/tests/soap12/T65.phpt +++ b/ext/soap/tests/soap12/T65.phpt @@ -23,4 +23,4 @@ include "soap12-test.inc"; ?> --EXPECT-- -env:ReceiverDTD are not supported by SOAP +env:ReceiverDTD are not supported by SOAP diff --git a/ext/soap/tests/soap12/T69.phpt b/ext/soap/tests/soap12/T69.phpt index 44097d982e74e..c0aed746618f6 100644 --- a/ext/soap/tests/soap12/T69.phpt +++ b/ext/soap/tests/soap12/T69.phpt @@ -16,4 +16,4 @@ include "soap12-test.inc"; ?> --EXPECT-- -env:SenderBody must be present in a SOAP envelope +env:SenderBody must be present in a SOAP envelope diff --git a/ext/soap/tests/soap12/T70.phpt b/ext/soap/tests/soap12/T70.phpt index 7a9be2d3ecc96..ee36184a4029c 100644 --- a/ext/soap/tests/soap12/T70.phpt +++ b/ext/soap/tests/soap12/T70.phpt @@ -20,4 +20,4 @@ include "soap12-test.inc"; ?> --EXPECT-- -env:SenderA SOAP 1.2 envelope can contain only Header and Body +env:SenderA SOAP 1.2 envelope can contain only Header and Body diff --git a/ext/soap/tests/soap12/T71.phpt b/ext/soap/tests/soap12/T71.phpt index 7bed55473d981..ef0eeb6a8c7aa 100644 --- a/ext/soap/tests/soap12/T71.phpt +++ b/ext/soap/tests/soap12/T71.phpt @@ -21,4 +21,4 @@ include "soap12-test.inc"; ?> --EXPECT-- -env:SenderA SOAP Envelope element cannot have non Namespace qualified attributes +env:SenderA SOAP Envelope element cannot have non Namespace qualified attributes diff --git a/ext/soap/tests/soap12/T72.phpt b/ext/soap/tests/soap12/T72.phpt index 76ae9d57bf1fb..8fc374901a401 100644 --- a/ext/soap/tests/soap12/T72.phpt +++ b/ext/soap/tests/soap12/T72.phpt @@ -19,4 +19,4 @@ include "soap12-test.inc"; ?> --EXPECT-- -env:SenderencodingStyle cannot be specified on the Envelope +env:SenderencodingStyle cannot be specified on the Envelope diff --git a/ext/soap/tests/soap12/T80.phpt b/ext/soap/tests/soap12/T80.phpt index 521c34651b638..c7c03c78e35d0 100644 --- a/ext/soap/tests/soap12/T80.phpt +++ b/ext/soap/tests/soap12/T80.phpt @@ -16,4 +16,4 @@ include "soap12-test.inc"; ?> --EXPECT-- -env:DataEncodingUnknownUnknown Data Encoding Style +env:DataEncodingUnknownUnknown Data Encoding Style From c6b058b7d26d2501c3e8e75f5642f746693d4a7b Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Thu, 19 Jun 2025 22:48:57 +0200 Subject: [PATCH 107/682] Fix memory leaks when returning refcounted value from curl callback Closes GH-18883. --- NEWS | 4 +++ ext/curl/curl_private.h | 3 +++ ext/curl/interface.c | 22 +++++++++++---- ext/curl/multi.c | 2 +- .../refcounted_return_must_not_leak.phpt | 27 +++++++++++++++++++ 5 files changed, 52 insertions(+), 6 deletions(-) create mode 100644 ext/curl/tests/refcounted_return_must_not_leak.phpt diff --git a/NEWS b/NEWS index af154569e2843..1d91c11862ff7 100644 --- a/NEWS +++ b/NEWS @@ -6,6 +6,10 @@ PHP NEWS . Fixed bug GH-18833 (Use after free with weakmaps dependent on destruction order). (Daniil Gentili) +- Curl: + . Fix memory leaks when returning refcounted value from curl callback. + (nielsdos) + 03 Jul 2025, PHP 8.3.23 - Core: diff --git a/ext/curl/curl_private.h b/ext/curl/curl_private.h index 3800d6533e555..66cba9d562adb 100644 --- a/ext/curl/curl_private.h +++ b/ext/curl/curl_private.h @@ -147,6 +147,9 @@ void _php_curl_multi_cleanup_list(void *data); void _php_curl_verify_handlers(php_curl *ch, bool reporterror); void _php_setup_easy_copy_handlers(php_curl *ch, php_curl *source); +/* Consumes `zv` */ +zend_long php_curl_get_long(zval *zv); + static inline php_curl *curl_from_obj(zend_object *obj) { return (php_curl *)((char *)(obj) - XtOffsetOf(php_curl, std)); } diff --git a/ext/curl/interface.c b/ext/curl/interface.c index 61d830e8abfe1..6c480907b7629 100644 --- a/ext/curl/interface.c +++ b/ext/curl/interface.c @@ -623,7 +623,7 @@ static size_t curl_write(char *data, size_t size, size_t nmemb, void *ctx) length = -1; } else if (!Z_ISUNDEF(retval)) { _php_curl_verify_handlers(ch, /* reporterror */ true); - length = zval_get_long(&retval); + length = php_curl_get_long(&retval); } zval_ptr_dtor(&argv[0]); @@ -667,7 +667,7 @@ static int curl_fnmatch(void *ctx, const char *pattern, const char *string) php_error_docref(NULL, E_WARNING, "Cannot call the CURLOPT_FNMATCH_FUNCTION"); } else if (!Z_ISUNDEF(retval)) { _php_curl_verify_handlers(ch, /* reporterror */ true); - rval = zval_get_long(&retval); + rval = php_curl_get_long(&retval); } zval_ptr_dtor(&argv[0]); zval_ptr_dtor(&argv[1]); @@ -715,7 +715,7 @@ static size_t curl_progress(void *clientp, double dltotal, double dlnow, double php_error_docref(NULL, E_WARNING, "Cannot call the CURLOPT_PROGRESSFUNCTION"); } else if (!Z_ISUNDEF(retval)) { _php_curl_verify_handlers(ch, /* reporterror */ true); - if (0 != zval_get_long(&retval)) { + if (0 != php_curl_get_long(&retval)) { rval = 1; } } @@ -764,7 +764,7 @@ static size_t curl_xferinfo(void *clientp, curl_off_t dltotal, curl_off_t dlnow, php_error_docref(NULL, E_WARNING, "Cannot call the CURLOPT_XFERINFOFUNCTION"); } else if (!Z_ISUNDEF(retval)) { _php_curl_verify_handlers(ch, /* reporterror */ true); - if (0 != zval_get_long(&retval)) { + if (0 != php_curl_get_long(&retval)) { rval = 1; } } @@ -821,6 +821,7 @@ static int curl_ssh_hostkeyfunction(void *clientp, int keytype, const char *key, } } else { zend_throw_error(NULL, "The CURLOPT_SSH_HOSTKEYFUNCTION callback must return either CURLKHMATCH_OK or CURLKHMATCH_MISMATCH"); + zval_ptr_dtor(&retval); } } zval_ptr_dtor(&argv[0]); @@ -938,7 +939,7 @@ static size_t curl_write_header(char *data, size_t size, size_t nmemb, void *ctx length = -1; } else if (!Z_ISUNDEF(retval)) { _php_curl_verify_handlers(ch, /* reporterror */ true); - length = zval_get_long(&retval); + length = php_curl_get_long(&retval); } zval_ptr_dtor(&argv[0]); zval_ptr_dtor(&argv[1]); @@ -1290,6 +1291,17 @@ void _php_setup_easy_copy_handlers(php_curl *ch, php_curl *source) (*source->clone)++; } +zend_long php_curl_get_long(zval *zv) +{ + if (EXPECTED(Z_TYPE_P(zv) == IS_LONG)) { + return Z_LVAL_P(zv); + } else { + zend_long ret = zval_get_long(zv); + zval_ptr_dtor(zv); + return ret; + } +} + #if LIBCURL_VERSION_NUM >= 0x073800 /* 7.56.0 */ static size_t read_cb(char *buffer, size_t size, size_t nitems, void *arg) /* {{{ */ { diff --git a/ext/curl/multi.c b/ext/curl/multi.c index be8b26cec9162..09802d7e37d50 100644 --- a/ext/curl/multi.c +++ b/ext/curl/multi.c @@ -430,7 +430,7 @@ static int _php_server_push_callback(CURL *parent_ch, CURL *easy, size_t num_hea if (error == FAILURE) { php_error_docref(NULL, E_WARNING, "Cannot call the CURLMOPT_PUSHFUNCTION"); } else if (!Z_ISUNDEF(retval)) { - if (CURL_PUSH_DENY != zval_get_long(&retval)) { + if (CURL_PUSH_DENY != php_curl_get_long(&retval)) { rval = CURL_PUSH_OK; zend_llist_add_element(&mh->easyh, &pz_ch); } else { diff --git a/ext/curl/tests/refcounted_return_must_not_leak.phpt b/ext/curl/tests/refcounted_return_must_not_leak.phpt new file mode 100644 index 0000000000000..b4b07a1a4fc88 --- /dev/null +++ b/ext/curl/tests/refcounted_return_must_not_leak.phpt @@ -0,0 +1,27 @@ +--TEST-- +Returning refcounted value from callback must not leak +--EXTENSIONS-- +curl +--FILE-- + +--EXPECT-- +ok From 5bd18e3fdc1abdedd5c418095fd8a41f77bae146 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Sat, 21 Jun 2025 18:03:50 +0100 Subject: [PATCH 108/682] ext/zlib: Refactor tests (#18887) - Use INI sections - Use CGI sections - Move data into a subfolder - Remove ZPP tests - Fix various bugs within tests - Simplify some Found while working on #18879 --- ext/zlib/tests/002.phpt | 4 +- ext/zlib/tests/003.phpt | 2 +- ext/zlib/tests/bug55544-win.phpt | Bin 564 -> 569 bytes ext/zlib/tests/bug60761.phpt | 7 +- ext/zlib/tests/bug61139.phpt | 2 +- ext/zlib/tests/bug71417.phpt | 2 +- ext/zlib/tests/bug74240.phpt | 4 +- ext/zlib/tests/compress_zlib_wrapper.phpt | 8 +- ext/zlib/tests/{ => data}/data.inc | 0 ext/zlib/tests/{ => data}/func.inc | 0 .../tests/{004.txt.gz => data/test.txt.gz} | Bin .../zlib/tests/data/windows-test.txt.gz | Bin ext/zlib/tests/deflate_init_reuse.phpt | 3 +- ext/zlib/tests/gh16883.phpt | 4 +- ext/zlib/tests/gzclose_basic.phpt | 2 +- ext/zlib/tests/gzcompress_basic1.phpt | 54 ++++----- ext/zlib/tests/gzcompress_error1.phpt | 1 - ext/zlib/tests/gzcompress_variation1.phpt | 2 +- ext/zlib/tests/gzdeflate_basic1.phpt | 54 ++++----- ext/zlib/tests/gzdeflate_variation1.phpt | 4 +- ext/zlib/tests/gzencode_basic1.phpt | 10 +- .../tests/{007.phpt => gzencode_invalid.phpt} | 0 ext/zlib/tests/gzencode_variation1-win32.phpt | 2 +- ext/zlib/tests/gzencode_variation1.phpt | 4 +- ext/zlib/tests/gzencode_variation2-win32.phpt | 6 +- ext/zlib/tests/gzencode_variation2.phpt | 4 - ext/zlib/tests/gzeof_basic.phpt | 2 +- ext/zlib/tests/{004.phpt => gzfile-mb.phpt} | 10 +- ...43\202\214\343\201\276\343\201\231.txt.gz" | Bin ext/zlib/tests/gzfile_basic.phpt | 9 +- ext/zlib/tests/gzfile_basic2.phpt | 9 +- .../{004-mb.phpt => gzfile_open_gz.phpt} | 12 +- ext/zlib/tests/gzfile_variation12.phpt | 104 ------------------ ext/zlib/tests/gzfile_variation4.phpt | 39 ------- ext/zlib/tests/gzfile_variation5.phpt | 35 ------ ext/zlib/tests/gzfile_variation7.phpt | 33 +----- ext/zlib/tests/gzfile_variation9.phpt | 103 ----------------- ext/zlib/tests/gzgetc_basic.phpt | 5 +- ext/zlib/tests/gzgetc_basic_1.phpt | 4 +- ext/zlib/tests/gzgets_basic.phpt | 2 +- ext/zlib/tests/gzinflate_error1.phpt | 2 +- ext/zlib/tests/gzinflate_length.phpt | 4 +- ext/zlib/tests/gzopen_basic.phpt | 2 +- ext/zlib/tests/gzopen_variation7.phpt | 2 +- ext/zlib/tests/gzopen_variation9.phpt | 4 +- ext/zlib/tests/gzpassthru_basic.phpt | 2 +- ext/zlib/tests/gzread_basic.phpt | 2 +- ext/zlib/tests/gzread_error2.phpt | 2 +- ext/zlib/tests/gzrewind_basic.phpt | 2 +- ext/zlib/tests/gzrewind_basic2.phpt | 2 +- ext/zlib/tests/gzseek_basic.phpt | 2 +- ext/zlib/tests/gzseek_variation2.phpt | 2 +- ext/zlib/tests/gzseek_variation3.phpt | 2 +- ext/zlib/tests/gzseek_variation6.phpt | 2 +- ext/zlib/tests/gztell_basic.phpt | 2 +- ext/zlib/tests/gzuncompress_basic1.phpt | 2 +- ext/zlib/tests/gzwrite_variation1.phpt | 2 +- ext/zlib/tests/ob_001.phpt | 4 +- ext/zlib/tests/ob_003.phpt | 4 +- ext/zlib/tests/ob_004.phpt | 4 +- ext/zlib/tests/ob_005.phpt | 5 +- ext/zlib/tests/ob_gzhandler_legacy_002.phpt | 5 +- ext/zlib/tests/readgzfile_variation12.phpt | 52 --------- ext/zlib/tests/readgzfile_variation4.phpt | 39 ------- ext/zlib/tests/readgzfile_variation5.phpt | 35 ------ ext/zlib/tests/readgzfile_variation7.phpt | 34 +----- ext/zlib/tests/readgzfile_variation9.phpt | 51 --------- .../tests/zlib_lock_mandatory_windows.phpt | 8 +- ext/zlib/tests/zlib_scheme_copy_basic.phpt | 2 +- .../tests/zlib_scheme_copy_variation1.phpt | 2 +- ext/zlib/tests/zlib_scheme_file_basic.phpt | 2 +- .../zlib_scheme_file_get_contents_basic.phpt | 2 +- .../zlib_scheme_file_read_file_basic.phpt | 2 +- ext/zlib/tests/zlib_scheme_fopen_basic.phpt | 2 +- .../tests/zlib_scheme_fopen_variation1.phpt | 4 +- ext/zlib/tests/zlib_scheme_rename_basic.phpt | 2 +- ext/zlib/tests/zlib_scheme_stat_basic2.phpt | 8 +- ext/zlib/tests/zlib_scheme_unlink_basic.phpt | 2 +- ext/zlib/tests/zlib_wrapper_flock_basic.phpt | 2 +- ext/zlib/tests/zlib_wrapper_fstat_basic.phpt | 2 +- .../tests/zlib_wrapper_ftruncate_basic.phpt | 2 +- ext/zlib/tests/zlib_wrapper_level.phpt | 2 +- .../tests/zlib_wrapper_meta_data_basic.phpt | 8 +- 83 files changed, 171 insertions(+), 698 deletions(-) rename ext/zlib/tests/{ => data}/data.inc (100%) rename ext/zlib/tests/{ => data}/func.inc (100%) rename ext/zlib/tests/{004.txt.gz => data/test.txt.gz} (100%) rename "ext/zlib/tests/004\347\247\201\343\201\257\343\202\254\343\203\251\343\202\271\343\202\222\351\243\237\343\201\271\343\202\211\343\202\214\343\201\276\343\201\231.txt.gz" => ext/zlib/tests/data/windows-test.txt.gz (100%) rename ext/zlib/tests/{007.phpt => gzencode_invalid.phpt} (100%) rename ext/zlib/tests/{004.phpt => gzfile-mb.phpt} (74%) rename ext/zlib/tests/005.txt.gz => "ext/zlib/tests/gzfile-mb\347\247\201\343\201\257\343\202\254\343\203\251\343\202\271\343\202\222\351\243\237\343\201\271\343\202\211\343\202\214\343\201\276\343\201\231.txt.gz" (100%) rename ext/zlib/tests/{004-mb.phpt => gzfile_open_gz.phpt} (66%) delete mode 100644 ext/zlib/tests/gzfile_variation12.phpt delete mode 100644 ext/zlib/tests/gzfile_variation4.phpt delete mode 100644 ext/zlib/tests/gzfile_variation5.phpt delete mode 100644 ext/zlib/tests/gzfile_variation9.phpt delete mode 100644 ext/zlib/tests/readgzfile_variation12.phpt delete mode 100644 ext/zlib/tests/readgzfile_variation4.phpt delete mode 100644 ext/zlib/tests/readgzfile_variation5.phpt delete mode 100644 ext/zlib/tests/readgzfile_variation9.phpt diff --git a/ext/zlib/tests/002.phpt b/ext/zlib/tests/002.phpt index 83f701e5aaf19..6564cc3f07388 100644 --- a/ext/zlib/tests/002.phpt +++ b/ext/zlib/tests/002.phpt @@ -8,14 +8,14 @@ $original = str_repeat("hallo php",4096); $packed=gzcompress($original); echo strlen($packed)." ".strlen($original)."\n"; $unpacked=gzuncompress($packed); -if (strcmp($original,$unpacked)==0) echo "Strings are equal\n"; +if ($original === $unpacked) echo "Strings are equal\n"; /* with explicit compression level, length */ $original = str_repeat("hallo php",4096); $packed=gzcompress($original, 9); echo strlen($packed)." ".strlen($original)."\n"; $unpacked=gzuncompress($packed, 40000); -if (strcmp($original,$unpacked)==0) echo "Strings are equal\n"; +if ($original === $unpacked) echo "Strings are equal\n"; ?> --EXPECT-- 106 36864 diff --git a/ext/zlib/tests/003.phpt b/ext/zlib/tests/003.phpt index 6fc5a71980ba6..0c731a0dfafba 100644 --- a/ext/zlib/tests/003.phpt +++ b/ext/zlib/tests/003.phpt @@ -7,7 +7,7 @@ zlib $original = str_repeat("hallo php",4096); $packed = gzencode($original); echo strlen($packed)." ".strlen($original). "\n"; -if (strcmp($original, gzdecode($packed)) == 0) echo "Strings are equal"; +if ($original === gzdecode($packed)) echo "Strings are equal\n"; ?> --EXPECT-- 118 36864 diff --git a/ext/zlib/tests/bug55544-win.phpt b/ext/zlib/tests/bug55544-win.phpt index 5c94236a2f21f457485fad58085a468f96ab2e83..11116da28b6d97cbd899cd876e23526963a35fc2 100644 GIT binary patch delta 16 XcmdnOvXf=P5!RH%l0^NDM+z7LI2Hze delta 10 RcmdnVvV~>Bk&Pz{7y%ks1hoJF diff --git a/ext/zlib/tests/bug60761.phpt b/ext/zlib/tests/bug60761.phpt index 735123869432d..16577f869081b 100644 --- a/ext/zlib/tests/bug60761.phpt +++ b/ext/zlib/tests/bug60761.phpt @@ -2,14 +2,13 @@ checks zlib compression output size is always the same --EXTENSIONS-- zlib +--INI-- +zlib.output_compression=4096 +zlib.output_compression_level=9 --CGI-- --FILE-- --CLEAN-- --EXPECTF-- Warning: gzopen(): gzopen failed in %s on line %d diff --git a/ext/zlib/tests/bug71417.phpt b/ext/zlib/tests/bug71417.phpt index 8d871a329e7fe..ee8b30f18e998 100644 --- a/ext/zlib/tests/bug71417.phpt +++ b/ext/zlib/tests/bug71417.phpt @@ -45,7 +45,7 @@ function test($case) { // The gzdecode() function applied to the corrupted compressed data always // detects the error: // --> gzdecode(): PHP Fatal error: Uncaught ErrorException: gzdecode(): data error in ... - echo "gzdecode(): ", rawurldecode(gzdecode($compressed)), "\n"; + echo "gzdecode(): ", rawurldecode((string) gzdecode($compressed)), "\n"; file_put_contents($fn, $compressed); diff --git a/ext/zlib/tests/bug74240.phpt b/ext/zlib/tests/bug74240.phpt index c3dbcc26e369e..f45cd04f71c0e 100644 --- a/ext/zlib/tests/bug74240.phpt +++ b/ext/zlib/tests/bug74240.phpt @@ -2,11 +2,11 @@ Bug #74240 (deflate_add can allocate too much memory) --EXTENSIONS-- zlib +--INI-- +memory_limit=64M --FILE-- ===DONE=== --EXPECT-- +bool(true) ===DONE=== diff --git a/ext/zlib/tests/gh16883.phpt b/ext/zlib/tests/gh16883.phpt index c3d81d4537938..8f22d1662278a 100644 --- a/ext/zlib/tests/gh16883.phpt +++ b/ext/zlib/tests/gh16883.phpt @@ -29,9 +29,9 @@ stream_context_set_default([ $f = gzopen('http://'.PHP_CLI_SERVER_HOSTNAME.':'.PHP_CLI_SERVER_PORT, 'r'); var_dump(stream_get_contents($f)); -var_dump(gzfile('http://'.PHP_CLI_SERVER_HOSTNAME.':'.PHP_CLI_SERVER_PORT, 'r')); +var_dump(gzfile('http://'.PHP_CLI_SERVER_HOSTNAME.':'.PHP_CLI_SERVER_PORT)); -var_dump(readgzfile('http://'.PHP_CLI_SERVER_HOSTNAME.':'.PHP_CLI_SERVER_PORT, 'r')); +var_dump(readgzfile('http://'.PHP_CLI_SERVER_HOSTNAME.':'.PHP_CLI_SERVER_PORT)); ?> --EXPECT-- diff --git a/ext/zlib/tests/gzclose_basic.phpt b/ext/zlib/tests/gzclose_basic.phpt index c1d6edf4d95e1..f2e03a579de5e 100644 --- a/ext/zlib/tests/gzclose_basic.phpt +++ b/ext/zlib/tests/gzclose_basic.phpt @@ -7,7 +7,7 @@ zlib // note that gzclose is an alias to fclose. parameter checking tests will be // the same as fclose -$f = __DIR__."/004.txt.gz"; +$f = __DIR__."/data/test.txt.gz"; $h = gzopen($f, 'r'); gzread($h, 20); var_dump(gzclose($h)); diff --git a/ext/zlib/tests/gzcompress_basic1.phpt b/ext/zlib/tests/gzcompress_basic1.phpt index 8899deaed0f72..606da46e5c873 100644 --- a/ext/zlib/tests/gzcompress_basic1.phpt +++ b/ext/zlib/tests/gzcompress_basic1.phpt @@ -8,7 +8,7 @@ zlib * add a comment here to say what the test is supposed to do */ -include(__DIR__ . '/data.inc'); +include(__DIR__ . '/data/data.inc'); echo "*** Testing gzcompress() : basic functionality ***\n"; @@ -23,68 +23,68 @@ $smallstring = "A small string to compress\n"; for($i = -1; $i < 10; $i++) { echo "-- Compression level $i --\n"; $output = gzcompress($data, $i); - var_dump(strcmp(gzuncompress($output), $data)); + var_dump(gzuncompress($output) === $data); } // Compressing a smaller string for($i = -1; $i < 10; $i++) { echo "-- Compression level $i --\n"; $output = gzcompress($smallstring, $i); - var_dump(strcmp(gzuncompress($output), $smallstring)); + var_dump(gzuncompress($output) === $smallstring); } // Calling gzcompress() with mandatory arguments echo "\n-- Testing with no specified compression level --\n"; $output = gzcompress($smallstring); - var_dump(strcmp(gzuncompress($output), $smallstring)); +var_dump(gzuncompress($output) === $smallstring); ?> --EXPECT-- *** Testing gzcompress() : basic functionality *** -- Compression level -1 -- -int(0) +bool(true) -- Compression level 0 -- -int(0) +bool(true) -- Compression level 1 -- -int(0) +bool(true) -- Compression level 2 -- -int(0) +bool(true) -- Compression level 3 -- -int(0) +bool(true) -- Compression level 4 -- -int(0) +bool(true) -- Compression level 5 -- -int(0) +bool(true) -- Compression level 6 -- -int(0) +bool(true) -- Compression level 7 -- -int(0) +bool(true) -- Compression level 8 -- -int(0) +bool(true) -- Compression level 9 -- -int(0) +bool(true) -- Compression level -1 -- -int(0) +bool(true) -- Compression level 0 -- -int(0) +bool(true) -- Compression level 1 -- -int(0) +bool(true) -- Compression level 2 -- -int(0) +bool(true) -- Compression level 3 -- -int(0) +bool(true) -- Compression level 4 -- -int(0) +bool(true) -- Compression level 5 -- -int(0) +bool(true) -- Compression level 6 -- -int(0) +bool(true) -- Compression level 7 -- -int(0) +bool(true) -- Compression level 8 -- -int(0) +bool(true) -- Compression level 9 -- -int(0) +bool(true) -- Testing with no specified compression level -- -int(0) +bool(true) diff --git a/ext/zlib/tests/gzcompress_error1.phpt b/ext/zlib/tests/gzcompress_error1.phpt index a34ceaf7248fd..b21743755417f 100644 --- a/ext/zlib/tests/gzcompress_error1.phpt +++ b/ext/zlib/tests/gzcompress_error1.phpt @@ -20,7 +20,6 @@ try { } echo "\n-- Testing with invalid encoding --\n"; -$data = 'string_val'; $level = 2; $encoding = 99; try { diff --git a/ext/zlib/tests/gzcompress_variation1.phpt b/ext/zlib/tests/gzcompress_variation1.phpt index 81dafa737c305..032a77a1a55ab 100644 --- a/ext/zlib/tests/gzcompress_variation1.phpt +++ b/ext/zlib/tests/gzcompress_variation1.phpt @@ -4,7 +4,7 @@ Test gzcompress() function : variation zlib --FILE-- --EXPECT-- *** Testing gzdeflate() : basic functionality *** -- Compression level -1 -- -int(0) +bool(true) -- Compression level 0 -- -int(0) +bool(true) -- Compression level 1 -- -int(0) +bool(true) -- Compression level 2 -- -int(0) +bool(true) -- Compression level 3 -- -int(0) +bool(true) -- Compression level 4 -- -int(0) +bool(true) -- Compression level 5 -- -int(0) +bool(true) -- Compression level 6 -- -int(0) +bool(true) -- Compression level 7 -- -int(0) +bool(true) -- Compression level 8 -- -int(0) +bool(true) -- Compression level 9 -- -int(0) +bool(true) -- Compression level -1 -- -int(0) +bool(true) -- Compression level 0 -- -int(0) +bool(true) -- Compression level 1 -- -int(0) +bool(true) -- Compression level 2 -- -int(0) +bool(true) -- Compression level 3 -- -int(0) +bool(true) -- Compression level 4 -- -int(0) +bool(true) -- Compression level 5 -- -int(0) +bool(true) -- Compression level 6 -- -int(0) +bool(true) -- Compression level 7 -- -int(0) +bool(true) -- Compression level 8 -- -int(0) +bool(true) -- Compression level 9 -- -int(0) +bool(true) -- Testing with no specified compression level -- -int(0) +bool(true) diff --git a/ext/zlib/tests/gzdeflate_variation1.phpt b/ext/zlib/tests/gzdeflate_variation1.phpt index 21456a9887659..b2d98ce978d04 100644 --- a/ext/zlib/tests/gzdeflate_variation1.phpt +++ b/ext/zlib/tests/gzdeflate_variation1.phpt @@ -4,12 +4,10 @@ Test gzdeflate() function : variation zlib --FILE-- --EXPECTF-- *** Testing gzencode() : basic functionality *** diff --git a/ext/zlib/tests/007.phpt b/ext/zlib/tests/gzencode_invalid.phpt similarity index 100% rename from ext/zlib/tests/007.phpt rename to ext/zlib/tests/gzencode_invalid.phpt diff --git a/ext/zlib/tests/gzencode_variation1-win32.phpt b/ext/zlib/tests/gzencode_variation1-win32.phpt index 31329bb5ea9cc..c6d261567c90e 100644 --- a/ext/zlib/tests/gzencode_variation1-win32.phpt +++ b/ext/zlib/tests/gzencode_variation1-win32.phpt @@ -11,7 +11,7 @@ if (substr(PHP_OS, 0, 3) != "WIN") { --FILE-- --FILE-- ---EXPECTF-- -Warning: gzfile(nonexistent_file_gzfile): Failed to open stream: No such file or directory in %s on line %d -bool(false) +--EXPECT-- array(6) { [0]=> string(36) "When you're taught through feelings diff --git a/ext/zlib/tests/005.txt.gz "b/ext/zlib/tests/gzfile-mb\347\247\201\343\201\257\343\202\254\343\203\251\343\202\271\343\202\222\351\243\237\343\201\271\343\202\211\343\202\214\343\201\276\343\201\231.txt.gz" similarity index 100% rename from ext/zlib/tests/005.txt.gz rename to "ext/zlib/tests/gzfile-mb\347\247\201\343\201\257\343\202\254\343\203\251\343\202\271\343\202\222\351\243\237\343\201\271\343\202\211\343\202\214\343\201\276\343\201\231.txt.gz" diff --git a/ext/zlib/tests/gzfile_basic.phpt b/ext/zlib/tests/gzfile_basic.phpt index 26cde7397cea1..a993893c1dc6c 100644 --- a/ext/zlib/tests/gzfile_basic.phpt +++ b/ext/zlib/tests/gzfile_basic.phpt @@ -19,8 +19,13 @@ gzclose($h); var_dump(gzfile( $filename ) ); -unlink($filename); -rmdir($dirname); +?> +--CLEAN-- + --EXPECT-- array(3) { diff --git a/ext/zlib/tests/gzfile_basic2.phpt b/ext/zlib/tests/gzfile_basic2.phpt index 2b59656a6f314..b098692620a26 100644 --- a/ext/zlib/tests/gzfile_basic2.phpt +++ b/ext/zlib/tests/gzfile_basic2.phpt @@ -19,8 +19,13 @@ fclose($h); var_dump(gzfile( $filename ) ); -unlink($filename); -rmdir($dirname); +?> +--CLEAN-- + --EXPECT-- array(3) { diff --git a/ext/zlib/tests/004-mb.phpt b/ext/zlib/tests/gzfile_open_gz.phpt similarity index 66% rename from ext/zlib/tests/004-mb.phpt rename to ext/zlib/tests/gzfile_open_gz.phpt index 9cf6dd628c6a1..7614a3ddcac1c 100644 --- a/ext/zlib/tests/004-mb.phpt +++ b/ext/zlib/tests/gzfile_open_gz.phpt @@ -1,20 +1,16 @@ --TEST-- -gzfile() with various invalid params +gzfile() with a proper gz file --EXTENSIONS-- zlib --FILE-- ---EXPECTF-- -Warning: gzfile(nonexistent_file_gzfile): Failed to open stream: No such file or directory in %s on line %d -bool(false) +--EXPECT-- array(6) { [0]=> string(36) "When you're taught through feelings diff --git a/ext/zlib/tests/gzfile_variation12.phpt b/ext/zlib/tests/gzfile_variation12.phpt deleted file mode 100644 index 6985442d6e583..0000000000000 --- a/ext/zlib/tests/gzfile_variation12.phpt +++ /dev/null @@ -1,104 +0,0 @@ ---TEST-- -Test function gzfile() by substituting argument 2 with int values. ---EXTENSIONS-- -zlib ---FILE-- - 0, - 'int 1' => 1, - 'int 12345' => 12345, - 'int -12345' => -2345, - ); - - -foreach ( $variation as $var ) { - var_dump(gzfile( $filename, $var ) ); -} -?> ---EXPECT-- -array(6) { - [0]=> - string(36) "When you're taught through feelings -" - [1]=> - string(26) "Destiny flying high above -" - [2]=> - string(38) "all I know is that you can realize it -" - [3]=> - string(18) "Destiny who cares -" - [4]=> - string(19) "as it turns around -" - [5]=> - string(39) "and I know that it descends down on me -" -} -array(6) { - [0]=> - string(36) "When you're taught through feelings -" - [1]=> - string(26) "Destiny flying high above -" - [2]=> - string(38) "all I know is that you can realize it -" - [3]=> - string(18) "Destiny who cares -" - [4]=> - string(19) "as it turns around -" - [5]=> - string(39) "and I know that it descends down on me -" -} -array(6) { - [0]=> - string(36) "When you're taught through feelings -" - [1]=> - string(26) "Destiny flying high above -" - [2]=> - string(38) "all I know is that you can realize it -" - [3]=> - string(18) "Destiny who cares -" - [4]=> - string(19) "as it turns around -" - [5]=> - string(39) "and I know that it descends down on me -" -} -array(6) { - [0]=> - string(36) "When you're taught through feelings -" - [1]=> - string(26) "Destiny flying high above -" - [2]=> - string(38) "all I know is that you can realize it -" - [3]=> - string(18) "Destiny who cares -" - [4]=> - string(19) "as it turns around -" - [5]=> - string(39) "and I know that it descends down on me -" -} diff --git a/ext/zlib/tests/gzfile_variation4.phpt b/ext/zlib/tests/gzfile_variation4.phpt deleted file mode 100644 index 86d84899b86e9..0000000000000 --- a/ext/zlib/tests/gzfile_variation4.phpt +++ /dev/null @@ -1,39 +0,0 @@ ---TEST-- -Test function gzfile() by substituting argument 1 with float values. ---EXTENSIONS-- -zlib ---FILE-- - 10.5, - 'float -10.5' => -10.5, - 'float 12.3456789000e10' => 12.3456789000e10, - 'float -12.3456789000e10' => -12.3456789000e10, - 'float .5' => .5, - ); - - -foreach ( $variation as $var ) { - var_dump(gzfile( $var , $use_include_path ) ); -} -?> ---EXPECTF-- -Warning: gzfile(10.5): Failed to open stream: No such file or directory in %s on line %d -bool(false) - -Warning: gzfile(-10.5): Failed to open stream: No such file or directory in %s on line %d -bool(false) - -Warning: gzfile(123456789000): Failed to open stream: No such file or directory in %s on line %d -bool(false) - -Warning: gzfile(-123456789000): Failed to open stream: No such file or directory in %s on line %d -bool(false) - -Warning: gzfile(0.5): Failed to open stream: No such file or directory in %s on line %d -bool(false) diff --git a/ext/zlib/tests/gzfile_variation5.phpt b/ext/zlib/tests/gzfile_variation5.phpt deleted file mode 100644 index 75751ea12cc77..0000000000000 --- a/ext/zlib/tests/gzfile_variation5.phpt +++ /dev/null @@ -1,35 +0,0 @@ ---TEST-- -Test function gzfile() by substituting argument 1 with int values. ---EXTENSIONS-- -zlib ---FILE-- - 0, - 'int 1' => 1, - 'int 12345' => 12345, - 'int -12345' => -2345, - ); - - -foreach ( $variation as $var ) { - var_dump(gzfile( $var , $use_include_path ) ); -} -?> ---EXPECTF-- -Warning: gzfile(0): Failed to open stream: No such file or directory in %s on line %d -bool(false) - -Warning: gzfile(1): Failed to open stream: No such file or directory in %s on line %d -bool(false) - -Warning: gzfile(12345): Failed to open stream: No such file or directory in %s on line %d -bool(false) - -Warning: gzfile(-2345): Failed to open stream: No such file or directory in %s on line %d -bool(false) diff --git a/ext/zlib/tests/gzfile_variation7.phpt b/ext/zlib/tests/gzfile_variation7.phpt index 834f8937e823f..ca221c12cff3f 100644 --- a/ext/zlib/tests/gzfile_variation7.phpt +++ b/ext/zlib/tests/gzfile_variation7.phpt @@ -1,39 +1,18 @@ --TEST-- -Test function gzfile() by substituting argument 1 with string values. +gzfile() with unknown file --EXTENSIONS-- zlib --FILE-- "string", - 'string SQ' => 'string', - 'mixed case string' => "sTrInG", - 'heredoc' => $heredoc - ); - - -foreach ( $variation_array as $var ) { - var_dump(gzfile( $var , $use_include_path ) ); -} +var_dump(gzfile($filename, false)); +var_dump(gzfile($filename, true)); ?> --EXPECTF-- -Warning: gzfile(string): Failed to open stream: No such file or directory in %s on line %d -bool(false) - -Warning: gzfile(string): Failed to open stream: No such file or directory in %s on line %d -bool(false) - -Warning: gzfile(sTrInG): Failed to open stream: No such file or directory in %s on line %d +Warning: gzfile(nonexistent_file_gzfile.txt.gz): Failed to open stream: No such file or directory in %s on line %d bool(false) -Warning: gzfile(hello world): Failed to open stream: No such file or directory in %s on line %d +Warning: gzfile(nonexistent_file_gzfile.txt.gz): Failed to open stream: No such file or directory in %s on line %d bool(false) diff --git a/ext/zlib/tests/gzfile_variation9.phpt b/ext/zlib/tests/gzfile_variation9.phpt deleted file mode 100644 index f2919d79be5c1..0000000000000 --- a/ext/zlib/tests/gzfile_variation9.phpt +++ /dev/null @@ -1,103 +0,0 @@ ---TEST-- -Test function gzfile() by substituting argument 2 with boolean values. ---EXTENSIONS-- -zlib ---FILE-- - true, - 'lowercase false' =>false, - 'uppercase TRUE' =>TRUE, - 'uppercase FALSE' =>FALSE, - ); - - -foreach ( $variation as $var ) { - var_dump(gzfile( $filename, $var ) ); -} -?> ---EXPECT-- -array(6) { - [0]=> - string(36) "When you're taught through feelings -" - [1]=> - string(26) "Destiny flying high above -" - [2]=> - string(38) "all I know is that you can realize it -" - [3]=> - string(18) "Destiny who cares -" - [4]=> - string(19) "as it turns around -" - [5]=> - string(39) "and I know that it descends down on me -" -} -array(6) { - [0]=> - string(36) "When you're taught through feelings -" - [1]=> - string(26) "Destiny flying high above -" - [2]=> - string(38) "all I know is that you can realize it -" - [3]=> - string(18) "Destiny who cares -" - [4]=> - string(19) "as it turns around -" - [5]=> - string(39) "and I know that it descends down on me -" -} -array(6) { - [0]=> - string(36) "When you're taught through feelings -" - [1]=> - string(26) "Destiny flying high above -" - [2]=> - string(38) "all I know is that you can realize it -" - [3]=> - string(18) "Destiny who cares -" - [4]=> - string(19) "as it turns around -" - [5]=> - string(39) "and I know that it descends down on me -" -} -array(6) { - [0]=> - string(36) "When you're taught through feelings -" - [1]=> - string(26) "Destiny flying high above -" - [2]=> - string(38) "all I know is that you can realize it -" - [3]=> - string(18) "Destiny who cares -" - [4]=> - string(19) "as it turns around -" - [5]=> - string(39) "and I know that it descends down on me -" -} diff --git a/ext/zlib/tests/gzgetc_basic.phpt b/ext/zlib/tests/gzgetc_basic.phpt index 7164c23098d92..ce366e4b6b31a 100644 --- a/ext/zlib/tests/gzgetc_basic.phpt +++ b/ext/zlib/tests/gzgetc_basic.phpt @@ -4,8 +4,7 @@ Test function gzgetc() by calling it with its expected arguments zlib 1.2.5 zlib --SKIPIF-- 0) { die('skip - only for zlib <= 1.2.5'); } @@ -16,7 +15,7 @@ if (version_compare(get_zlib_version(), '1.2.5') > 0) { // note that gzgets is an alias to fgets. parameter checking tests will be // the same as gzgets -$f = __DIR__."/004.txt.gz"; +$f = __DIR__."/data/test.txt.gz"; $h = gzopen($f, 'r'); $count = 0; diff --git a/ext/zlib/tests/gzgetc_basic_1.phpt b/ext/zlib/tests/gzgetc_basic_1.phpt index e70c5814fcbc8..cf6da96e5612f 100644 --- a/ext/zlib/tests/gzgetc_basic_1.phpt +++ b/ext/zlib/tests/gzgetc_basic_1.phpt @@ -5,7 +5,7 @@ zlib --SKIPIF-- = 1.2.7'); } @@ -16,7 +16,7 @@ if (version_compare(get_zlib_version(), '1.2.7') < 0) { // note that gzgets is an alias to fgets. parameter checking tests will be // the same as gzgets -$f = __DIR__."/004.txt.gz"; +$f = __DIR__."/data/test.txt.gz"; $h = gzopen($f, 'r'); if ($h) { $count = 0; diff --git a/ext/zlib/tests/gzgets_basic.phpt b/ext/zlib/tests/gzgets_basic.phpt index f43b3ad5342c3..a971d5e3a620c 100644 --- a/ext/zlib/tests/gzgets_basic.phpt +++ b/ext/zlib/tests/gzgets_basic.phpt @@ -8,7 +8,7 @@ zlib // note that gzgets is an alias to fgets. parameter checking tests will be // the same as fgets -$f = __DIR__."/004.txt.gz"; +$f = __DIR__."/data/test.txt.gz"; $h = gzopen($f, 'r'); $lengths = array(10, 14, 7, 99); foreach ($lengths as $length) { diff --git a/ext/zlib/tests/gzinflate_error1.phpt b/ext/zlib/tests/gzinflate_error1.phpt index 8d4abdf529b02..4d02c2604e30e 100644 --- a/ext/zlib/tests/gzinflate_error1.phpt +++ b/ext/zlib/tests/gzinflate_error1.phpt @@ -4,7 +4,7 @@ Test gzinflate() function : error conditions zlib --FILE-- +--CGI-- --GET-- a=b --INI-- diff --git a/ext/zlib/tests/ob_003.phpt b/ext/zlib/tests/ob_003.phpt index 647a120627044..70bd209f3824c 100644 --- a/ext/zlib/tests/ob_003.phpt +++ b/ext/zlib/tests/ob_003.phpt @@ -3,9 +3,7 @@ zlib.output_compression --EXTENSIONS-- zlib --SKIPIF-- - +--CGI-- --INI-- zlib.output_compression=0 --ENV-- diff --git a/ext/zlib/tests/ob_004.phpt b/ext/zlib/tests/ob_004.phpt index ac4ec2d862db1..dc48c39aa1f20 100644 --- a/ext/zlib/tests/ob_004.phpt +++ b/ext/zlib/tests/ob_004.phpt @@ -3,9 +3,7 @@ ob_gzhandler --EXTENSIONS-- zlib --SKIPIF-- - +--CGI-- --INI-- zlib.output_compression=0 --ENV-- diff --git a/ext/zlib/tests/ob_005.phpt b/ext/zlib/tests/ob_005.phpt index aaa9856dbaab7..343d548161808 100644 --- a/ext/zlib/tests/ob_005.phpt +++ b/ext/zlib/tests/ob_005.phpt @@ -2,10 +2,7 @@ ob_gzhandler --EXTENSIONS-- zlib ---SKIPIF-- - +--CGI- --INI-- zlib.output_compression=0 --ENV-- diff --git a/ext/zlib/tests/ob_gzhandler_legacy_002.phpt b/ext/zlib/tests/ob_gzhandler_legacy_002.phpt index 6789ba2d53f90..41cdda1f8d671 100644 --- a/ext/zlib/tests/ob_gzhandler_legacy_002.phpt +++ b/ext/zlib/tests/ob_gzhandler_legacy_002.phpt @@ -2,10 +2,7 @@ ob_gzhandler legacy --EXTENSIONS-- zlib ---SKIPIF-- - +--CGI-- --INI-- zlib.output_compression=0 --ENV-- diff --git a/ext/zlib/tests/readgzfile_variation12.phpt b/ext/zlib/tests/readgzfile_variation12.phpt deleted file mode 100644 index 02c18fc41f8d1..0000000000000 --- a/ext/zlib/tests/readgzfile_variation12.phpt +++ /dev/null @@ -1,52 +0,0 @@ ---TEST-- -Test function readgzfile() by substituting argument 2 with int values. ---EXTENSIONS-- -zlib ---FILE-- - 0, - 'int 1' => 1, - 'int 12345' => 12345, - 'int -12345' => -2345, - ); - - -foreach ( $variation as $var ) { - var_dump(readgzfile( $filename, $var ) ); -} -?> ---EXPECT-- -When you're taught through feelings -Destiny flying high above -all I know is that you can realize it -Destiny who cares -as it turns around -and I know that it descends down on me -int(176) -When you're taught through feelings -Destiny flying high above -all I know is that you can realize it -Destiny who cares -as it turns around -and I know that it descends down on me -int(176) -When you're taught through feelings -Destiny flying high above -all I know is that you can realize it -Destiny who cares -as it turns around -and I know that it descends down on me -int(176) -When you're taught through feelings -Destiny flying high above -all I know is that you can realize it -Destiny who cares -as it turns around -and I know that it descends down on me -int(176) diff --git a/ext/zlib/tests/readgzfile_variation4.phpt b/ext/zlib/tests/readgzfile_variation4.phpt deleted file mode 100644 index 85854b024fc56..0000000000000 --- a/ext/zlib/tests/readgzfile_variation4.phpt +++ /dev/null @@ -1,39 +0,0 @@ ---TEST-- -Test function readgzfile() by substituting argument 1 with float values. ---EXTENSIONS-- -zlib ---FILE-- - 10.5, - 'float -10.5' => -10.5, - 'float 12.3456789000e10' => 12.3456789000e10, - 'float -12.3456789000e10' => -12.3456789000e10, - 'float .5' => .5, - ); - - -foreach ( $variation as $var ) { - var_dump(readgzfile( $var , $use_include_path ) ); -} -?> ---EXPECTF-- -Warning: readgzfile(10.5): Failed to open stream: No such file or directory in %s on line %d -bool(false) - -Warning: readgzfile(-10.5): Failed to open stream: No such file or directory in %s on line %d -bool(false) - -Warning: readgzfile(123456789000): Failed to open stream: No such file or directory in %s on line %d -bool(false) - -Warning: readgzfile(-123456789000): Failed to open stream: No such file or directory in %s on line %d -bool(false) - -Warning: readgzfile(0.5): Failed to open stream: No such file or directory in %s on line %d -bool(false) diff --git a/ext/zlib/tests/readgzfile_variation5.phpt b/ext/zlib/tests/readgzfile_variation5.phpt deleted file mode 100644 index 039812ba2e813..0000000000000 --- a/ext/zlib/tests/readgzfile_variation5.phpt +++ /dev/null @@ -1,35 +0,0 @@ ---TEST-- -Test function readgzfile() by substituting argument 1 with int values. ---EXTENSIONS-- -zlib ---FILE-- - 0, - 'int 1' => 1, - 'int 12345' => 12345, - 'int -12345' => -2345, - ); - - -foreach ( $variation as $var ) { - var_dump(readgzfile( $var , $use_include_path ) ); -} -?> ---EXPECTF-- -Warning: readgzfile(0): Failed to open stream: No such file or directory in %s on line %d -bool(false) - -Warning: readgzfile(1): Failed to open stream: No such file or directory in %s on line %d -bool(false) - -Warning: readgzfile(12345): Failed to open stream: No such file or directory in %s on line %d -bool(false) - -Warning: readgzfile(-2345): Failed to open stream: No such file or directory in %s on line %d -bool(false) diff --git a/ext/zlib/tests/readgzfile_variation7.phpt b/ext/zlib/tests/readgzfile_variation7.phpt index 0d357c6774b69..6f201abe2361b 100644 --- a/ext/zlib/tests/readgzfile_variation7.phpt +++ b/ext/zlib/tests/readgzfile_variation7.phpt @@ -1,39 +1,17 @@ --TEST-- -Test function readgzfile() by substituting argument 1 with string values. +readgzfile() with unknown file --EXTENSIONS-- zlib --FILE-- "string", - 'string SQ' => 'string', - 'mixed case string' => "sTrInG", - 'heredoc' => $heredoc - ); - - -foreach ( $variation_array as $var ) { - var_dump(readgzfile( $var , $use_include_path ) ); -} +$file = "unknown_file.txt.gz"; +var_dump(readgzfile($file, false)); +var_dump(readgzfile($file, true)); ?> --EXPECTF-- -Warning: readgzfile(string): Failed to open stream: No such file or directory in %s on line %d -bool(false) - -Warning: readgzfile(string): Failed to open stream: No such file or directory in %s on line %d -bool(false) - -Warning: readgzfile(sTrInG): Failed to open stream: No such file or directory in %s on line %d +Warning: readgzfile(unknown_file.txt.gz): Failed to open stream: No such file or directory in %s on line %d bool(false) -Warning: readgzfile(hello world): Failed to open stream: No such file or directory in %s on line %d +Warning: readgzfile(unknown_file.txt.gz): Failed to open stream: No such file or directory in %s on line %d bool(false) diff --git a/ext/zlib/tests/readgzfile_variation9.phpt b/ext/zlib/tests/readgzfile_variation9.phpt deleted file mode 100644 index 011ded054b780..0000000000000 --- a/ext/zlib/tests/readgzfile_variation9.phpt +++ /dev/null @@ -1,51 +0,0 @@ ---TEST-- -Test function readgzfile() by substituting argument 2 with boolean values. ---EXTENSIONS-- -zlib ---FILE-- - true, - 'lowercase false' =>false, - 'uppercase TRUE' =>TRUE, - 'uppercase FALSE' =>FALSE, - ); - - -foreach ( $variation as $var ) { - var_dump(readgzfile( $filename, $var ) ); -} -?> ---EXPECT-- -When you're taught through feelings -Destiny flying high above -all I know is that you can realize it -Destiny who cares -as it turns around -and I know that it descends down on me -int(176) -When you're taught through feelings -Destiny flying high above -all I know is that you can realize it -Destiny who cares -as it turns around -and I know that it descends down on me -int(176) -When you're taught through feelings -Destiny flying high above -all I know is that you can realize it -Destiny who cares -as it turns around -and I know that it descends down on me -int(176) -When you're taught through feelings -Destiny flying high above -all I know is that you can realize it -Destiny who cares -as it turns around -and I know that it descends down on me -int(176) diff --git a/ext/zlib/tests/zlib_lock_mandatory_windows.phpt b/ext/zlib/tests/zlib_lock_mandatory_windows.phpt index 04ed2ab251056..4cb8df4f2ad0f 100644 --- a/ext/zlib/tests/zlib_lock_mandatory_windows.phpt +++ b/ext/zlib/tests/zlib_lock_mandatory_windows.phpt @@ -10,7 +10,7 @@ if (PHP_OS_FAMILY !== "Windows") die("skip Only for Windows because it has manda diff --git a/ext/zlib/tests/zlib_scheme_fopen_basic.phpt b/ext/zlib/tests/zlib_scheme_fopen_basic.phpt index 9d366670f1ca8..fbac2f82ae637 100644 --- a/ext/zlib/tests/zlib_scheme_fopen_basic.phpt +++ b/ext/zlib/tests/zlib_scheme_fopen_basic.phpt @@ -4,7 +4,7 @@ Test compress.zlib:// scheme with the fopen zlib --FILE-- --EXPECTF-- -file=compress.zlib://file://%s/004.txt.gz +file=compress.zlib://file://%s/test.txt.gz When you're taught through feelings Destiny flying high above diff --git a/ext/zlib/tests/zlib_scheme_rename_basic.phpt b/ext/zlib/tests/zlib_scheme_rename_basic.phpt index be9df167220a5..506ac9cb92367 100644 --- a/ext/zlib/tests/zlib_scheme_rename_basic.phpt +++ b/ext/zlib/tests/zlib_scheme_rename_basic.phpt @@ -4,7 +4,7 @@ Test compress.zlib:// scheme with the unlink function zlib --FILE-- --FILE-- bool(true) ["uri"]=> - string(%d) "compress.zlib://%s/004.txt.gz" + string(%d) "compress.zlib://%s/test.txt.gz" } From a36b8fdc9423b015de005f724392e34a1cb21a62 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Thu, 5 Jun 2025 19:30:37 +0200 Subject: [PATCH 109/682] Fix GH-13264: fgets() and stream_get_line() do not return false on filter fatal error This happens because there are no checks in php_stream_fill_read_buffer calls. This should not fail always but only on fatal error so special flag is needed for that. Closes GH-18778 --- NEWS | 4 ++ ext/sqlite3/sqlite3.c | 4 ++ ext/standard/tests/filters/gh13264.phpt | 72 ++++++++++++++----------- main/php_streams.h | 3 ++ main/streams/memory.c | 5 ++ main/streams/streams.c | 16 +++++- 6 files changed, 70 insertions(+), 34 deletions(-) diff --git a/NEWS b/NEWS index 1d91c11862ff7..9881c36d4b012 100644 --- a/NEWS +++ b/NEWS @@ -10,6 +10,10 @@ PHP NEWS . Fix memory leaks when returning refcounted value from curl callback. (nielsdos) +- Streams: + . Fixed GH-13264 (fgets() and stream_get_line() do not return false on filter + fatal error). (Jakub Zelenka) + 03 Jul 2025, PHP 8.3.23 - Core: diff --git a/ext/sqlite3/sqlite3.c b/ext/sqlite3/sqlite3.c index 09cb57410c87f..9b3286b70220d 100644 --- a/ext/sqlite3/sqlite3.c +++ b/ext/sqlite3/sqlite3.c @@ -1165,6 +1165,7 @@ static int php_sqlite3_stream_seek(php_stream *stream, zend_off_t offset, int wh sqlite3_stream->position = sqlite3_stream->position + offset; *newoffs = sqlite3_stream->position; stream->eof = 0; + stream->fatal_error = 0; return 0; } } else { @@ -1176,6 +1177,7 @@ static int php_sqlite3_stream_seek(php_stream *stream, zend_off_t offset, int wh sqlite3_stream->position = sqlite3_stream->position + offset; *newoffs = sqlite3_stream->position; stream->eof = 0; + stream->fatal_error = 0; return 0; } } @@ -1188,6 +1190,7 @@ static int php_sqlite3_stream_seek(php_stream *stream, zend_off_t offset, int wh sqlite3_stream->position = offset; *newoffs = sqlite3_stream->position; stream->eof = 0; + stream->fatal_error = 0; return 0; } case SEEK_END: @@ -1203,6 +1206,7 @@ static int php_sqlite3_stream_seek(php_stream *stream, zend_off_t offset, int wh sqlite3_stream->position = sqlite3_stream->size + offset; *newoffs = sqlite3_stream->position; stream->eof = 0; + stream->fatal_error = 0; return 0; } default: diff --git a/ext/standard/tests/filters/gh13264.phpt b/ext/standard/tests/filters/gh13264.phpt index e992d0868898d..f778bbf915a7b 100644 --- a/ext/standard/tests/filters/gh13264.phpt +++ b/ext/standard/tests/filters/gh13264.phpt @@ -1,49 +1,57 @@ --TEST-- -GH-81475: Memory leak during stream filter failure +GH-13264: fgets() and stream_get_line() do not return false on filter fatal error --SKIPIF-- --FILE-- 15]); -// Rewind and add the zlib filter -rewind($stream); -stream_filter_append($stream, 'zlib.inflate', STREAM_FILTER_READ, ['window' => 15]); + return $stream; +} -// Read the filtered stream line by line. +// Read the filtered stream line by line using fgets. +$stream = create_stream(); while (($line = fgets($stream)) !== false) { - $error = error_get_last(); - if ($error !== null) { - // An error is thrown but fgets didn't return false - var_dump(error_get_last()); - var_dump($line); - } + $error = error_get_last(); + if ($error !== null) { + // An error is thrown but fgets didn't return false + var_dump(error_get_last()); + var_dump($line); + } } +fclose($stream); +error_clear_last(); +// Read the filtered stream line by line using stream_get_line. +$stream = create_stream(); +while (($line = stream_get_line($stream, 0, "\n")) !== false) { + $error = error_get_last(); + if ($error !== null) { + // An error is thrown but stream_get_line didn't return false + var_dump(error_get_last()); + var_dump($line); + } +} fclose($stream); ?> --EXPECTF-- Notice: fgets(): zlib: data error in %s on line %d -array(4) { - ["type"]=> - int(8) - ["message"]=> - string(25) "fgets(): zlib: data error" - ["file"]=> - string(%d) "%s" - ["line"]=> - int(%d) -} -string(%d) "Hello%s" +Notice: stream_get_line(): zlib: data error in %s on line %d diff --git a/main/php_streams.h b/main/php_streams.h index 31b80de986053..8996ee8bcb071 100644 --- a/main/php_streams.h +++ b/main/php_streams.h @@ -223,6 +223,9 @@ struct _php_stream { /* whether stdio cast flushing is in progress */ uint16_t fclose_stdiocast_flush_in_progress:1; + /* whether fatal error happened and all operations should terminates as soon as possible */ + uint16_t fatal_error:1; + char mode[16]; /* "rwb" etc. ala stdio */ uint32_t flags; /* PHP_STREAM_FLAG_XXX */ diff --git a/main/streams/memory.c b/main/streams/memory.c index ce11aec382bfe..785109db6582c 100644 --- a/main/streams/memory.c +++ b/main/streams/memory.c @@ -136,10 +136,12 @@ static int php_stream_memory_seek(php_stream *stream, zend_off_t offset, int whe ms->fpos = ms->fpos + offset; *newoffs = ms->fpos; stream->eof = 0; + stream->fatal_error = 0; return 0; } } else { stream->eof = 0; + stream->fatal_error = 0; ms->fpos = ms->fpos + offset; *newoffs = ms->fpos; return 0; @@ -153,6 +155,7 @@ static int php_stream_memory_seek(php_stream *stream, zend_off_t offset, int whe ms->fpos = offset; *newoffs = ms->fpos; stream->eof = 0; + stream->fatal_error = 0; return 0; } case SEEK_END: @@ -160,6 +163,7 @@ static int php_stream_memory_seek(php_stream *stream, zend_off_t offset, int whe ms->fpos = ZSTR_LEN(ms->data) + offset; *newoffs = ms->fpos; stream->eof = 0; + stream->fatal_error = 0; return 0; } else if (ZSTR_LEN(ms->data) < (size_t)(-offset)) { ms->fpos = 0; @@ -169,6 +173,7 @@ static int php_stream_memory_seek(php_stream *stream, zend_off_t offset, int whe ms->fpos = ZSTR_LEN(ms->data) + offset; *newoffs = ms->fpos; stream->eof = 0; + stream->fatal_error = 0; return 0; } default: diff --git a/main/streams/streams.c b/main/streams/streams.c index 4f9c88e4774c4..1471c98558e18 100644 --- a/main/streams/streams.c +++ b/main/streams/streams.c @@ -636,6 +636,7 @@ PHPAPI zend_result _php_stream_fill_read_buffer(php_stream *stream, size_t size) /* some fatal error. Theoretically, the stream is borked, so all * further reads should fail. */ stream->eof = 1; + stream->fatal_error = 1; /* free all data left in brigades */ while ((bucket = brig_inp->head)) { /* Remove unconsumed buckets from the input brigade */ @@ -1009,7 +1010,12 @@ PHPAPI char *_php_stream_get_line(php_stream *stream, char *buf, size_t maxlen, } } - php_stream_fill_read_buffer(stream, toread); + if (php_stream_fill_read_buffer(stream, toread) == FAILURE && stream->fatal_error) { + if (grow_mode) { + efree(bufstart); + } + return NULL; + } if (stream->writepos - stream->readpos == 0) { break; @@ -1084,7 +1090,9 @@ PHPAPI zend_string *php_stream_get_record(php_stream *stream, size_t maxlen, con to_read_now = MIN(maxlen - buffered_len, stream->chunk_size); - php_stream_fill_read_buffer(stream, buffered_len + to_read_now); + if (php_stream_fill_read_buffer(stream, buffered_len + to_read_now) == FAILURE && stream->fatal_error) { + return NULL; + } just_read = STREAM_BUFFERED_AMOUNT(stream) - buffered_len; @@ -1357,6 +1365,7 @@ PHPAPI int _php_stream_seek(php_stream *stream, zend_off_t offset, int whence) stream->readpos += offset; /* if offset = ..., then readpos = writepos */ stream->position += offset; stream->eof = 0; + stream->fatal_error = 0; return 0; } break; @@ -1366,6 +1375,7 @@ PHPAPI int _php_stream_seek(php_stream *stream, zend_off_t offset, int whence) stream->readpos += offset - stream->position; stream->position = offset; stream->eof = 0; + stream->fatal_error = 0; return 0; } break; @@ -1400,6 +1410,7 @@ PHPAPI int _php_stream_seek(php_stream *stream, zend_off_t offset, int whence) if (((stream->flags & PHP_STREAM_FLAG_NO_SEEK) == 0) || ret == 0) { if (ret == 0) { stream->eof = 0; + stream->fatal_error = 0; } /* invalidate the buffer contents */ @@ -1422,6 +1433,7 @@ PHPAPI int _php_stream_seek(php_stream *stream, zend_off_t offset, int whence) offset -= didread; } stream->eof = 0; + stream->fatal_error = 0; return 0; } From 4fadf647d27118e34cc83fc98fcd4ead436c23cb Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sat, 21 Jun 2025 00:47:40 +0200 Subject: [PATCH 110/682] Refactor dom_nnodemap_objects_new() - Use ecalloc() to not miss initializing any field. - Merge declarations and assignments. --- ext/dom/php_dom.c | 22 +++------------------- 1 file changed, 3 insertions(+), 19 deletions(-) diff --git a/ext/dom/php_dom.c b/ext/dom/php_dom.c index f2c1ec2c62b8b..b1f7eca80f104 100644 --- a/ext/dom/php_dom.c +++ b/ext/dom/php_dom.c @@ -1603,26 +1603,10 @@ void dom_nnodemap_objects_free_storage(zend_object *object) /* {{{ */ zend_object *dom_nnodemap_objects_new(zend_class_entry *class_type) { - dom_object *intern; - dom_nnodemap_object *objmap; - - intern = dom_objects_set_class(class_type); - intern->ptr = emalloc(sizeof(dom_nnodemap_object)); - objmap = (dom_nnodemap_object *)intern->ptr; - ZVAL_UNDEF(&objmap->baseobj_zv); - objmap->baseobj = NULL; - objmap->nodetype = 0; - objmap->ht = NULL; - objmap->local = NULL; - objmap->local_lower = NULL; - objmap->release_local = false; - objmap->ns = NULL; - objmap->release_ns = false; - objmap->cache_tag.modification_nr = 0; + dom_object *intern = dom_objects_set_class(class_type); + intern->ptr = ecalloc(1, sizeof(dom_nnodemap_object)); + dom_nnodemap_object *objmap = intern->ptr; objmap->cached_length = -1; - objmap->cached_obj = NULL; - objmap->cached_obj_index = 0; - objmap->dict = NULL; return &intern->std; } From ff0a2cff05ca5319fcd1367245cbebbe65042ab4 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sat, 21 Jun 2025 15:37:45 +0200 Subject: [PATCH 111/682] Refactor implementation of DOM nodelists, named maps, and iterators The code was really messy with lots of checks and inconsistencies. This splits everything up into different functions and now everything is relayed to a handler vtable. --- ext/dom/config.m4 | 1 + ext/dom/config.w32 | 2 +- ext/dom/documenttype.c | 4 +- ext/dom/dom_iterators.c | 137 +----- ext/dom/element.c | 4 +- ext/dom/namednodemap.c | 126 +----- ext/dom/node.c | 4 +- ext/dom/nodelist.c | 163 +------ ext/dom/obj_map.c | 413 ++++++++++++++++++ ext/dom/obj_map.h | 39 ++ ext/dom/parentnode/css_selectors.c | 2 +- ext/dom/php_dom.c | 24 +- ext/dom/php_dom.h | 13 +- .../spec/Document_importLegacyNode.phpt | 6 +- .../modern/spec/NamedNodeMap_dimensions.phpt | 2 +- ext/dom/xpath.c | 2 +- 16 files changed, 521 insertions(+), 421 deletions(-) create mode 100644 ext/dom/obj_map.c create mode 100644 ext/dom/obj_map.h diff --git a/ext/dom/config.m4 b/ext/dom/config.m4 index 00934ceb55966..d51b1c2ac5202 100644 --- a/ext/dom/config.m4 +++ b/ext/dom/config.m4 @@ -33,6 +33,7 @@ if test "$PHP_DOM" != "no"; then node.c nodelist.c notation.c + obj_map.c parentnode/css_selectors.c parentnode/tree.c php_dom.c diff --git a/ext/dom/config.w32 b/ext/dom/config.w32 index d9e969a3845c4..2d8f3f3519c4a 100644 --- a/ext/dom/config.w32 +++ b/ext/dom/config.w32 @@ -15,7 +15,7 @@ if (PHP_DOM == "yes") { entity.c nodelist.c html_collection.c text.c comment.c \ entityreference.c \ token_list.c \ - notation.c xpath.c dom_iterators.c \ + notation.c obj_map.c xpath.c dom_iterators.c \ namednodemap.c xpath_callbacks.c", null, "/I ext/lexbor"); ADD_EXTENSION_DEP('dom', 'lexbor'); diff --git a/ext/dom/documenttype.c b/ext/dom/documenttype.c index 32bf556295161..895a34cebf650 100644 --- a/ext/dom/documenttype.c +++ b/ext/dom/documenttype.c @@ -53,7 +53,7 @@ zend_result dom_documenttype_entities_read(dom_object *obj, zval *retval) xmlHashTable *entityht = (xmlHashTable *) dtdptr->entities; dom_object *intern = Z_DOMOBJ_P(retval); - dom_namednode_iter(obj, XML_ENTITY_NODE, intern, entityht, NULL, NULL); + dom_namednode_iter(obj, intern, entityht, NULL, NULL, &php_dom_obj_map_entities); return SUCCESS; } @@ -74,7 +74,7 @@ zend_result dom_documenttype_notations_read(dom_object *obj, zval *retval) xmlHashTable *notationht = (xmlHashTable *) dtdptr->notations; dom_object *intern = Z_DOMOBJ_P(retval); - dom_namednode_iter(obj, XML_NOTATION_NODE, intern, notationht, NULL, NULL); + dom_namednode_iter(obj, intern, notationht, NULL, NULL, &php_dom_obj_map_notations); return SUCCESS; } diff --git a/ext/dom/dom_iterators.c b/ext/dom/dom_iterators.c index a0797967d1e76..19c4ea41adadb 100644 --- a/ext/dom/dom_iterators.c +++ b/ext/dom/dom_iterators.c @@ -68,7 +68,7 @@ xmlNodePtr create_notation(const xmlChar *name, const xmlChar *ExternalID, const } /* }}} */ -static xmlNode *php_dom_libxml_hash_iter_ex(xmlHashTable *ht, int index) +xmlNodePtr php_dom_libxml_hash_iter(xmlHashTable *ht, int index) { int htsize; @@ -84,18 +84,6 @@ static xmlNode *php_dom_libxml_hash_iter_ex(xmlHashTable *ht, int index) } } -xmlNode *php_dom_libxml_hash_iter(dom_nnodemap_object *objmap, int index) -{ - xmlNode *curnode = php_dom_libxml_hash_iter_ex(objmap->ht, index); - - if (curnode != NULL && objmap->nodetype != XML_ENTITY_NODE) { - xmlNotation *notation = (xmlNotation *) curnode; - curnode = create_notation(notation->name, notation->PublicID, notation->SystemID); - } - - return curnode; -} - static void php_dom_iterator_dtor(zend_object_iterator *iter) /* {{{ */ { php_dom_iterator *iterator = (php_dom_iterator *)iter; @@ -109,7 +97,7 @@ static zend_result php_dom_iterator_valid(zend_object_iterator *iter) /* {{{ */ { php_dom_iterator *iterator = (php_dom_iterator *)iter; - if (Z_TYPE(iterator->curobj) != IS_UNDEF) { + if (!Z_ISNULL(iterator->curobj)) { return SUCCESS; } else { return FAILURE; @@ -120,7 +108,7 @@ static zend_result php_dom_iterator_valid(zend_object_iterator *iter) /* {{{ */ zval *php_dom_iterator_current_data(zend_object_iterator *iter) /* {{{ */ { php_dom_iterator *iterator = (php_dom_iterator *)iter; - return Z_ISUNDEF(iterator->curobj) ? NULL : &iterator->curobj; + return Z_ISNULL(iterator->curobj) ? NULL : &iterator->curobj; } /* }}} */ @@ -131,14 +119,14 @@ static void php_dom_iterator_current_key(zend_object_iterator *iter, zval *key) /* Only dtd named node maps, i.e. the ones based on a libxml hash table or attribute collections, * are keyed by the name because in that case the name is unique. */ - if (!objmap->ht && objmap->nodetype != XML_ATTRIBUTE_NODE) { + if (objmap->handler->nameless) { ZVAL_LONG(key, iterator->index); } else { dom_object *intern = Z_DOMOBJ_P(&iterator->curobj); - if (intern != NULL && intern->ptr != NULL) { + if (intern->ptr != NULL) { xmlNodePtr curnode = ((php_libxml_node_ptr *)intern->ptr)->node; - if (objmap->nodetype == XML_ATTRIBUTE_NODE && php_dom_follow_spec_intern(intern)) { + if (curnode->type == XML_ATTRIBUTE_NODE && php_dom_follow_spec_intern(intern)) { ZVAL_NEW_STR(key, dom_node_get_node_name_attribute_or_element(curnode, false)); } else { ZVAL_STRINGL(key, (const char *) curnode->name, xmlStrlen(curnode->name)); @@ -150,99 +138,36 @@ static void php_dom_iterator_current_key(zend_object_iterator *iter, zval *key) } /* }}} */ -static xmlNodePtr dom_fetch_first_iteration_item(dom_nnodemap_object *objmap) -{ - xmlNodePtr basep = dom_object_get_node(objmap->baseobj); - if (!basep) { - return NULL; - } - if (objmap->nodetype == XML_ATTRIBUTE_NODE || objmap->nodetype == XML_ELEMENT_NODE) { - if (objmap->nodetype == XML_ATTRIBUTE_NODE) { - return (xmlNodePtr) basep->properties; - } else { - return dom_nodelist_iter_start_first_child(basep); - } - } else { - zend_long curindex = 0; - xmlNodePtr nodep = php_dom_first_child_of_container_node(basep); - return dom_get_elements_by_tag_name_ns_raw( - basep, nodep, objmap->ns, objmap->local, objmap->local_lower, &curindex, 0); - } -} - static void php_dom_iterator_move_forward(zend_object_iterator *iter) /* {{{ */ { - xmlNodePtr curnode = NULL; - php_dom_iterator *iterator = (php_dom_iterator *)iter; - if (Z_ISUNDEF(iterator->curobj)) { + if (Z_ISNULL(iterator->curobj)) { return; } iterator->index++; + zval garbage; + ZVAL_COPY_VALUE(&garbage, &iterator->curobj); + ZVAL_NULL(&iterator->curobj); dom_object *intern = Z_DOMOBJ_P(&iterator->curobj); dom_nnodemap_object *objmap = php_dom_iterator_get_nnmap(iterator); - if (intern != NULL && intern->ptr != NULL) { - if (objmap->nodetype != XML_ENTITY_NODE && - objmap->nodetype != XML_NOTATION_NODE) { - if (objmap->nodetype == DOM_NODESET) { - HashTable *nodeht = Z_ARRVAL_P(&objmap->baseobj_zv); - zval *entry = zend_hash_index_find(nodeht, iterator->index); - if (entry) { - zval_ptr_dtor(&iterator->curobj); - ZVAL_COPY(&iterator->curobj, entry); - return; - } - } else { - if (objmap->nodetype == XML_ATTRIBUTE_NODE || - objmap->nodetype == XML_ELEMENT_NODE) { - - /* Note: keep legacy behaviour for non-spec mode. */ - if (php_dom_follow_spec_intern(intern) && php_dom_is_cache_tag_stale_from_doc_ptr(&iterator->cache_tag, intern->document)) { - php_dom_mark_cache_tag_up_to_date_from_doc_ref(&iterator->cache_tag, intern->document); - curnode = dom_fetch_first_iteration_item(objmap); - zend_ulong index = 0; - while (curnode != NULL && index++ < iterator->index) { - curnode = curnode->next; - } - } else { - curnode = (xmlNodePtr)((php_libxml_node_ptr *)intern->ptr)->node; - curnode = curnode->next; - } - } else { - /* The collection is live, we nav the tree from the base object if we cannot - * use the cache to restart from the last point. */ - xmlNodePtr basenode = dom_object_get_node(objmap->baseobj); - - /* We have a strong reference to the base node via baseobj_zv, this cannot become NULL */ - ZEND_ASSERT(basenode != NULL); - - zend_long previndex; - if (php_dom_is_cache_tag_stale_from_node(&iterator->cache_tag, basenode)) { - php_dom_mark_cache_tag_up_to_date_from_node(&iterator->cache_tag, basenode); - previndex = 0; - curnode = php_dom_first_child_of_container_node(basenode); - } else { - previndex = iterator->index - 1; - curnode = (xmlNodePtr)((php_libxml_node_ptr *)intern->ptr)->node; - } - curnode = dom_get_elements_by_tag_name_ns_raw( - basenode, curnode, objmap->ns, objmap->local, objmap->local_lower, &previndex, iterator->index); - } + if (intern->ptr != NULL) { + /* Note: keep legacy behaviour for non-spec mode. */ + /* TODO: make this prettier */ + if (!php_dom_follow_spec_intern(intern) && (objmap->handler == &php_dom_obj_map_attributes || objmap->handler == &php_dom_obj_map_child_nodes)) { + xmlNodePtr curnode = ((php_libxml_node_ptr *) intern->ptr)->node; + curnode = curnode->next; + if (curnode) { + php_dom_create_object(curnode, &iterator->curobj, objmap->baseobj); } } else { - curnode = php_dom_libxml_hash_iter(objmap, iterator->index); + objmap->handler->get_item(objmap, (zend_long) iterator->index, &iterator->curobj); } } - zval_ptr_dtor(&iterator->curobj); - ZVAL_UNDEF(&iterator->curobj); - - if (curnode) { - php_dom_create_object(curnode, &iterator->curobj, objmap->baseobj); - } + zval_ptr_dtor(&garbage); } /* }}} */ @@ -261,7 +186,6 @@ zend_object_iterator *php_dom_get_iterator(zend_class_entry *ce, zval *object, i { dom_object *intern; dom_nnodemap_object *objmap; - xmlNodePtr curnode=NULL; php_dom_iterator *iterator; if (by_ref) { @@ -277,26 +201,7 @@ zend_object_iterator *php_dom_get_iterator(zend_class_entry *ce, zval *object, i intern = Z_DOMOBJ_P(object); objmap = (dom_nnodemap_object *)intern->ptr; - if (objmap != NULL) { - if (objmap->nodetype != XML_ENTITY_NODE && - objmap->nodetype != XML_NOTATION_NODE) { - if (objmap->nodetype == DOM_NODESET) { - HashTable *nodeht = Z_ARRVAL_P(&objmap->baseobj_zv); - zval *entry = zend_hash_index_find(nodeht, 0); - if (entry) { - ZVAL_COPY(&iterator->curobj, entry); - } - } else { - curnode = dom_fetch_first_iteration_item(objmap); - } - } else { - curnode = php_dom_libxml_hash_iter(objmap, 0); - } - } - - if (curnode) { - php_dom_create_object(curnode, &iterator->curobj, objmap->baseobj); - } + objmap->handler->get_item(objmap, 0, &iterator->curobj); return &iterator->intern; } diff --git a/ext/dom/element.c b/ext/dom/element.c index 4aa56f3691b2f..d0fc0b4a4d3e2 100644 --- a/ext/dom/element.c +++ b/ext/dom/element.c @@ -825,7 +825,7 @@ static void dom_element_get_elements_by_tag_name(INTERNAL_FUNCTION_PARAMETERS, z object_init_ex(return_value, iter_ce); namednode = Z_DOMOBJ_P(return_value); - dom_namednode_iter(intern, 0, namednode, NULL, name, NULL); + dom_namednode_iter(intern, namednode, NULL, name, NULL, &php_dom_obj_map_by_tag_name); } PHP_METHOD(DOMElement, getElementsByTagName) @@ -1257,7 +1257,7 @@ static void dom_element_get_elements_by_tag_name_ns(INTERNAL_FUNCTION_PARAMETERS object_init_ex(return_value, iter_ce); namednode = Z_DOMOBJ_P(return_value); - dom_namednode_iter(intern, 0, namednode, NULL, name, uri); + dom_namednode_iter(intern, namednode, NULL, name, uri, &php_dom_obj_map_by_tag_name); } PHP_METHOD(DOMElement, getElementsByTagNameNS) diff --git a/ext/dom/namednodemap.c b/ext/dom/namednodemap.c index 73f1f09e9dae8..2f8f6d10132c2 100644 --- a/ext/dom/namednodemap.c +++ b/ext/dom/namednodemap.c @@ -33,24 +33,12 @@ zend_long php_dom_get_namednodemap_length(dom_object *obj) { - dom_nnodemap_object *objmap = (dom_nnodemap_object *) obj->ptr; + dom_nnodemap_object *objmap = obj->ptr; if (!objmap) { return 0; } - if (objmap->nodetype == XML_NOTATION_NODE || objmap->nodetype == XML_ENTITY_NODE) { - return objmap->ht ? xmlHashSize(objmap->ht) : 0; - } - - zend_long count = 0; - xmlNodePtr nodep = dom_object_get_node(objmap->baseobj); - if (nodep) { - for (xmlAttrPtr curnode = nodep->properties; curnode; curnode = curnode->next) { - count++; - } - } - - return count; + return objmap->handler->length(objmap); } /* {{{ length int @@ -66,43 +54,9 @@ zend_result dom_namednodemap_length_read(dom_object *obj, zval *retval) /* }}} */ -xmlNodePtr php_dom_named_node_map_get_named_item(dom_nnodemap_object *objmap, const zend_string *named, bool may_transform) -{ - xmlNodePtr itemnode = NULL; - if (objmap != NULL) { - if ((objmap->nodetype == XML_NOTATION_NODE) || - objmap->nodetype == XML_ENTITY_NODE) { - if (objmap->ht) { - if (objmap->nodetype == XML_ENTITY_NODE) { - itemnode = (xmlNodePtr)xmlHashLookup(objmap->ht, BAD_CAST ZSTR_VAL(named)); - } else { - xmlNotationPtr notep = xmlHashLookup(objmap->ht, BAD_CAST ZSTR_VAL(named)); - if (notep) { - if (may_transform) { - itemnode = create_notation(notep->name, notep->PublicID, notep->SystemID); - } else { - itemnode = (xmlNodePtr) notep; - } - } - } - } - } else { - xmlNodePtr nodep = dom_object_get_node(objmap->baseobj); - if (nodep) { - if (php_dom_follow_spec_intern(objmap->baseobj)) { - itemnode = (xmlNodePtr) php_dom_get_attribute_node(nodep, BAD_CAST ZSTR_VAL(named), ZSTR_LEN(named)); - } else { - itemnode = (xmlNodePtr) xmlHasProp(nodep, BAD_CAST ZSTR_VAL(named)); - } - } - } - } - return itemnode; -} - -void php_dom_named_node_map_get_named_item_into_zval(dom_nnodemap_object *objmap, const zend_string *named, zval *return_value) +void php_dom_named_node_map_get_named_item_into_zval(dom_nnodemap_object *objmap, const zend_string *named, const char *ns, zval *return_value) { - xmlNodePtr itemnode = php_dom_named_node_map_get_named_item(objmap, named, true); + xmlNodePtr itemnode = objmap->handler->get_named_item(objmap, named, ns); if (itemnode) { DOM_RET_OBJ(itemnode, objmap->baseobj); } else { @@ -122,45 +76,10 @@ PHP_METHOD(DOMNamedNodeMap, getNamedItem) } dom_nnodemap_object *objmap = Z_DOMOBJ_P(ZEND_THIS)->ptr; - php_dom_named_node_map_get_named_item_into_zval(objmap, named, return_value); + php_dom_named_node_map_get_named_item_into_zval(objmap, named, NULL, return_value); } /* }}} end dom_namednodemap_get_named_item */ -xmlNodePtr php_dom_named_node_map_get_item(dom_nnodemap_object *objmap, zend_long index) -{ - xmlNodePtr itemnode = NULL; - if (objmap != NULL) { - if ((objmap->nodetype == XML_NOTATION_NODE) || - objmap->nodetype == XML_ENTITY_NODE) { - if (objmap->ht) { - itemnode = php_dom_libxml_hash_iter(objmap, index); - } - } else { - xmlNodePtr nodep = dom_object_get_node(objmap->baseobj); - if (nodep) { - xmlNodePtr curnode = (xmlNodePtr)nodep->properties; - zend_long count = 0; - while (count < index && curnode != NULL) { - count++; - curnode = curnode->next; - } - itemnode = curnode; - } - } - } - return itemnode; -} - -void php_dom_named_node_map_get_item_into_zval(dom_nnodemap_object *objmap, zend_long index, zval *return_value) -{ - xmlNodePtr itemnode = php_dom_named_node_map_get_item(objmap, index); - if (itemnode) { - DOM_RET_OBJ(itemnode, objmap->baseobj); - } else { - RETURN_NULL(); - } -} - /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-349467F9 Since: */ @@ -177,7 +96,7 @@ PHP_METHOD(DOMNamedNodeMap, item) dom_object *intern = Z_DOMOBJ_P(ZEND_THIS); dom_nnodemap_object *objmap = intern->ptr; - php_dom_named_node_map_get_item_into_zval(objmap, index, return_value); + objmap->handler->get_item(objmap, index, return_value); } /* }}} end dom_namednodemap_item */ @@ -186,16 +105,14 @@ Since: DOM Level 2 */ PHP_METHOD(DOMNamedNodeMap, getNamedItemNS) { - size_t namedlen=0, urilen=0; + size_t urilen=0; dom_object *intern; - xmlNodePtr itemnode = NULL; - char *uri, *named; + char *uri; + zend_string *named; dom_nnodemap_object *objmap; - xmlNodePtr nodep; - xmlNotation *notep = NULL; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s!s", &uri, &urilen, &named, &namedlen) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "s!S", &uri, &urilen, &named) == FAILURE) { RETURN_THROWS(); } @@ -204,28 +121,7 @@ PHP_METHOD(DOMNamedNodeMap, getNamedItemNS) objmap = (dom_nnodemap_object *)intern->ptr; if (objmap != NULL) { - if ((objmap->nodetype == XML_NOTATION_NODE) || - objmap->nodetype == XML_ENTITY_NODE) { - if (objmap->ht) { - if (objmap->nodetype == XML_ENTITY_NODE) { - itemnode = (xmlNodePtr)xmlHashLookup(objmap->ht, BAD_CAST named); - } else { - notep = (xmlNotation *)xmlHashLookup(objmap->ht, BAD_CAST named); - if (notep) { - itemnode = create_notation(notep->name, notep->PublicID, notep->SystemID); - } - } - } - } else { - nodep = dom_object_get_node(objmap->baseobj); - if (nodep) { - itemnode = (xmlNodePtr)xmlHasNsProp(nodep, BAD_CAST named, BAD_CAST uri); - } - } - } - - if (itemnode) { - DOM_RET_OBJ(itemnode, objmap->baseobj); + php_dom_named_node_map_get_named_item_into_zval(objmap, named, uri, return_value); } } /* }}} end dom_namednodemap_get_named_item_ns */ diff --git a/ext/dom/node.c b/ext/dom/node.c index c9bf45e887db8..99069d778ebfe 100644 --- a/ext/dom/node.c +++ b/ext/dom/node.c @@ -288,7 +288,7 @@ zend_result dom_node_child_nodes_read(dom_object *obj, zval *retval) object_init_ex(retval, dom_get_nodelist_ce(php_dom_follow_spec_intern(obj))); dom_object *intern = Z_DOMOBJ_P(retval); - dom_namednode_iter(obj, XML_ELEMENT_NODE, intern, NULL, NULL, NULL); + dom_namednode_iter(obj, intern, NULL, NULL, NULL, &php_dom_obj_map_child_nodes); return SUCCESS; } @@ -422,7 +422,7 @@ zend_result dom_node_attributes_read(dom_object *obj, zval *retval) if (nodep->type == XML_ELEMENT_NODE) { object_init_ex(retval, dom_get_namednodemap_ce(php_dom_follow_spec_intern(obj))); dom_object *intern = Z_DOMOBJ_P(retval); - dom_namednode_iter(obj, XML_ATTRIBUTE_NODE, intern, NULL, NULL, NULL); + dom_namednode_iter(obj, intern, NULL, NULL, NULL, &php_dom_obj_map_attributes); } else { ZVAL_NULL(retval); } diff --git a/ext/dom/nodelist.c b/ext/dom/nodelist.c index 819b7396b69c7..46a10a362a071 100644 --- a/ext/dom/nodelist.c +++ b/ext/dom/nodelist.c @@ -32,24 +32,6 @@ * Since: */ -static zend_always_inline void objmap_cache_release_cached_obj(dom_nnodemap_object *objmap) -{ - if (objmap->cached_obj) { - /* Since the DOM is a tree there can be no cycles. */ - if (GC_DELREF(&objmap->cached_obj->std) == 0) { - zend_objects_store_del(&objmap->cached_obj->std); - } - objmap->cached_obj = NULL; - objmap->cached_obj_index = 0; - } -} - -static zend_always_inline void reset_objmap_cache(dom_nnodemap_object *objmap) -{ - objmap_cache_release_cached_obj(objmap); - objmap->cached_length = -1; -} - xmlNodePtr dom_nodelist_iter_start_first_child(xmlNodePtr nodep) { if (nodep->type == XML_ENTITY_REF_NODE) { @@ -60,60 +42,6 @@ xmlNodePtr dom_nodelist_iter_start_first_child(xmlNodePtr nodep) return nodep->children; } -zend_long php_dom_get_nodelist_length(dom_object *obj) -{ - dom_nnodemap_object *objmap = (dom_nnodemap_object *) obj->ptr; - if (!objmap) { - return 0; - } - - if (objmap->ht) { - return xmlHashSize(objmap->ht); - } - - if (objmap->nodetype == DOM_NODESET) { - HashTable *nodeht = Z_ARRVAL_P(&objmap->baseobj_zv); - return zend_hash_num_elements(nodeht); - } - - xmlNodePtr nodep = dom_object_get_node(objmap->baseobj); - if (!nodep) { - return 0; - } - - if (!php_dom_is_cache_tag_stale_from_node(&objmap->cache_tag, nodep)) { - if (objmap->cached_length >= 0) { - return objmap->cached_length; - } - /* Only the length is out-of-date, the cache tag is still valid. - * Therefore, only overwrite the length and keep the currently cached object. */ - } else { - php_dom_mark_cache_tag_up_to_date_from_node(&objmap->cache_tag, nodep); - reset_objmap_cache(objmap); - } - - zend_long count = 0; - if (objmap->nodetype == XML_ATTRIBUTE_NODE || objmap->nodetype == XML_ELEMENT_NODE) { - xmlNodePtr curnode = dom_nodelist_iter_start_first_child(nodep); - if (curnode) { - count++; - while (curnode->next != NULL) { - count++; - curnode = curnode->next; - } - } - } else { - xmlNodePtr basep = nodep; - nodep = php_dom_first_child_of_container_node(basep); - dom_get_elements_by_tag_name_ns_raw( - basep, nodep, objmap->ns, objmap->local, objmap->local_lower, &count, ZEND_LONG_MAX - 1 /* because of <= */); - } - - objmap->cached_length = count; - - return count; -} - /* {{{ length int readonly=yes URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#ID-203510337 @@ -137,94 +65,11 @@ PHP_METHOD(DOMNodeList, count) void php_dom_nodelist_get_item_into_zval(dom_nnodemap_object *objmap, zend_long index, zval *return_value) { - xmlNodePtr itemnode = NULL; - bool cache_itemnode = false; - if (index >= 0) { - if (objmap != NULL) { - if (objmap->ht) { - itemnode = php_dom_libxml_hash_iter(objmap, index); - } else { - if (objmap->nodetype == DOM_NODESET) { - HashTable *nodeht = Z_ARRVAL_P(&objmap->baseobj_zv); - zval *entry = zend_hash_index_find(nodeht, index); - if (entry) { - ZVAL_COPY(return_value, entry); - return; - } - } else if (objmap->baseobj) { - xmlNodePtr basep = dom_object_get_node(objmap->baseobj); - if (basep) { - xmlNodePtr nodep = basep; - /* For now we're only able to use cache for forward search. - * TODO: in the future we could extend the logic of the node list such that backwards searches - * are also possible. */ - bool restart = true; - zend_long relative_index = index; - if (index >= objmap->cached_obj_index && objmap->cached_obj && !php_dom_is_cache_tag_stale_from_node(&objmap->cache_tag, nodep)) { - xmlNodePtr cached_obj_xml_node = dom_object_get_node(objmap->cached_obj); - - /* The node cannot be NULL if the cache is valid. If it is NULL, then it means we - * forgot an invalidation somewhere. Take the defensive programming approach and invalidate - * it here if it's NULL (except in debug mode where we would want to catch this). */ - if (UNEXPECTED(cached_obj_xml_node == NULL)) { -#if ZEND_DEBUG - ZEND_UNREACHABLE(); -#endif - reset_objmap_cache(objmap); - } else { - restart = false; - relative_index -= objmap->cached_obj_index; - nodep = cached_obj_xml_node; - } - } - zend_long count = 0; - if (objmap->nodetype == XML_ATTRIBUTE_NODE || objmap->nodetype == XML_ELEMENT_NODE) { - if (restart) { - nodep = dom_nodelist_iter_start_first_child(nodep); - } - while (count < relative_index && nodep != NULL) { - count++; - nodep = nodep->next; - } - itemnode = nodep; - } else { - if (restart) { - nodep = php_dom_first_child_of_container_node(basep); - } - itemnode = dom_get_elements_by_tag_name_ns_raw(basep, nodep, objmap->ns, objmap->local, objmap->local_lower, &count, relative_index); - } - cache_itemnode = true; - } - } - } - } - - if (itemnode) { - DOM_RET_OBJ(itemnode, objmap->baseobj); - if (cache_itemnode) { - /* Hold additional reference for the cache, must happen before releasing the cache - * because we might be the last reference holder. - * Instead of storing and copying zvals, we store the object pointer directly. - * This saves us some bytes because a pointer is smaller than a zval. - * This also means we have to manually refcount the objects here, and remove the reference count - * in reset_objmap_cache() and the destructor. */ - dom_object *cached_obj = Z_DOMOBJ_P(return_value); - GC_ADDREF(&cached_obj->std); - /* If the tag is stale, all cached data is useless. Otherwise only the cached object is useless. */ - if (php_dom_is_cache_tag_stale_from_node(&objmap->cache_tag, itemnode)) { - php_dom_mark_cache_tag_up_to_date_from_node(&objmap->cache_tag, itemnode); - reset_objmap_cache(objmap); - } else { - objmap_cache_release_cached_obj(objmap); - } - objmap->cached_obj_index = index; - objmap->cached_obj = cached_obj; - } - return; - } + if (EXPECTED(objmap)) { + objmap->handler->get_item(objmap, index, return_value); + } else { + RETURN_NULL(); } - - RETVAL_NULL(); } /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#ID-844377136 diff --git a/ext/dom/obj_map.c b/ext/dom/obj_map.c new file mode 100644 index 0000000000000..f2a93fe3ef870 --- /dev/null +++ b/ext/dom/obj_map.c @@ -0,0 +1,413 @@ +/* + +----------------------------------------------------------------------+ + | Copyright (c) The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | https://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Christian Stocker | + | Rob Richards | + | Niels Dossche | + +----------------------------------------------------------------------+ +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "php.h" +#if defined(HAVE_LIBXML) && defined(HAVE_DOM) +#include "php_dom.h" +#include "obj_map.h" + +static zend_always_inline void objmap_cache_release_cached_obj(dom_nnodemap_object *objmap) +{ + if (objmap->cached_obj) { + /* Since the DOM is a tree there can be no cycles. */ + if (GC_DELREF(&objmap->cached_obj->std) == 0) { + zend_objects_store_del(&objmap->cached_obj->std); + } + objmap->cached_obj = NULL; + objmap->cached_obj_index = 0; + } +} + +static zend_always_inline void reset_objmap_cache(dom_nnodemap_object *objmap) +{ + objmap_cache_release_cached_obj(objmap); + objmap->cached_length = -1; +} + +/************************** + * === Length methods === * + **************************/ + +static zend_long dom_map_get_xmlht_length(dom_nnodemap_object *map) +{ + /* Note: if there are, for example, no entities or notations then the hash table can be NULL. */ + return map->ht ? xmlHashSize(map->ht) : 0; +} + +static zend_long dom_map_get_nodeset_length(dom_nnodemap_object *map) +{ + HashTable *nodeht = Z_ARRVAL(map->baseobj_zv); + return zend_hash_num_elements(nodeht); +} + +static zend_long dom_map_get_prop_length(dom_nnodemap_object *map) +{ + zend_long count = 0; + xmlNodePtr nodep = dom_object_get_node(map->baseobj); + if (nodep) { + for (xmlAttrPtr curnode = nodep->properties; curnode; curnode = curnode->next) { + count++; + } + } + return count; +} + +static zend_long dom_map_get_nodes_length(dom_nnodemap_object *map) +{ + zend_long count = 0; + xmlNodePtr nodep = dom_object_get_node(map->baseobj); + if (nodep) { + for (xmlNodePtr curnode = dom_nodelist_iter_start_first_child(nodep); curnode; curnode = curnode->next) { + count++; + } + } + return count; +} + +static zend_long dom_map_get_by_tag_name_length(dom_nnodemap_object *map) +{ + xmlNodePtr nodep = dom_object_get_node(map->baseobj); + zend_long count = 0; + if (nodep) { + xmlNodePtr basep = nodep; + nodep = php_dom_first_child_of_container_node(basep); + dom_get_elements_by_tag_name_ns_raw( + basep, nodep, map->ns, map->local, map->local_lower, &count, ZEND_LONG_MAX - 1 /* because of <= */); + } + return count; +} + +static zend_long dom_map_get_zero_length(dom_nnodemap_object *map) +{ + return 0; +} + +/************************ + * === Item lookups === * + ************************/ + +static void dom_ret_node_to_zobj(dom_nnodemap_object *map, xmlNodePtr node, zval *return_value) +{ + if (node) { + DOM_RET_OBJ(node, map->baseobj); + } else { + RETURN_NULL(); + } +} + +static void dom_map_get_entity_item(dom_nnodemap_object *map, zend_long index, zval *return_value) +{ + xmlNodePtr node = map->ht ? php_dom_libxml_hash_iter(map->ht, index) : NULL; + dom_ret_node_to_zobj(map, node, return_value); +} + +static void dom_map_get_notation_item(dom_nnodemap_object *map, zend_long index, zval *return_value) +{ + xmlNodePtr node = map->ht ? php_dom_libxml_hash_iter(map->ht, index) : NULL; + if (node) { + xmlNotation *notation = (xmlNotation *) node; + node = create_notation(notation->name, notation->PublicID, notation->SystemID); + } + dom_ret_node_to_zobj(map, node, return_value); +} + +static void dom_map_get_nodeset_item(dom_nnodemap_object *map, zend_long index, zval *return_value) +{ + HashTable *nodeht = Z_ARRVAL(map->baseobj_zv); + zval *entry = zend_hash_index_find(nodeht, index); + if (entry) { + RETURN_COPY(entry); + } else { + RETURN_NULL(); + } +} + +typedef struct dom_node_idx_pair { + xmlNodePtr node; + zend_long index; +} dom_node_idx_pair; + +static dom_node_idx_pair dom_obj_map_get_start_point(dom_nnodemap_object *map, xmlNodePtr basep, zend_long index) +{ + dom_node_idx_pair ret; + ZEND_ASSERT(basep != NULL); + /* For now we're only able to use cache for forward search. + * TODO: in the future we could extend the logic of the node list such that backwards searches + * are also possible. */ + bool restart = true; + zend_long relative_index = index; + if (index >= map->cached_obj_index && map->cached_obj && !php_dom_is_cache_tag_stale_from_node(&map->cache_tag, basep)) { + xmlNodePtr cached_obj_xml_node = dom_object_get_node(map->cached_obj); + + /* The node cannot be NULL if the cache is valid. If it is NULL, then it means we + * forgot an invalidation somewhere. Take the defensive programming approach and invalidate + * it here if it's NULL (except in debug mode where we would want to catch this). */ + if (UNEXPECTED(cached_obj_xml_node == NULL)) { +#if ZEND_DEBUG + ZEND_UNREACHABLE(); +#endif + reset_objmap_cache(map); + } else { + restart = false; + relative_index -= map->cached_obj_index; + basep = cached_obj_xml_node; + } + } + ret.node = restart ? NULL : basep; + ret.index = relative_index; + return ret; +} + +static void dom_map_cache_obj(dom_nnodemap_object *map, xmlNodePtr itemnode, zend_long index, zval *return_value) +{ + /* Hold additional reference for the cache, must happen before releasing the cache + * because we might be the last reference holder. + * Instead of storing and copying zvals, we store the object pointer directly. + * This saves us some bytes because a pointer is smaller than a zval. + * This also means we have to manually refcount the objects here, and remove the reference count + * in reset_objmap_cache() and the destructor. */ + dom_object *cached_obj = Z_DOMOBJ_P(return_value); + GC_ADDREF(&cached_obj->std); + /* If the tag is stale, all cached data is useless. Otherwise only the cached object is useless. */ + if (php_dom_is_cache_tag_stale_from_node(&map->cache_tag, itemnode)) { + php_dom_mark_cache_tag_up_to_date_from_node(&map->cache_tag, itemnode); + reset_objmap_cache(map); + } else { + objmap_cache_release_cached_obj(map); + } + map->cached_obj_index = index; + map->cached_obj = cached_obj; +} + +static xmlNodePtr dom_map_get_attr_start(xmlNodePtr node) +{ + ZEND_ASSERT(node->type == XML_ELEMENT_NODE); + return (xmlNodePtr) node->properties; +} + +static void dom_map_get_chain_item(dom_nnodemap_object *map, zend_long index, zval *return_value, xmlNodePtr (*get_start)(xmlNodePtr)) +{ + xmlNodePtr nodep = dom_object_get_node(map->baseobj); + xmlNodePtr itemnode = NULL; + if (nodep && index >= 0) { + dom_node_idx_pair start_point = dom_obj_map_get_start_point(map, nodep, index); + itemnode = start_point.node ? start_point.node : get_start(nodep); + for (; start_point.index > 0 && itemnode; itemnode = itemnode->next, start_point.index--); + } + dom_ret_node_to_zobj(map, itemnode, return_value); + if (itemnode) { + dom_map_cache_obj(map, itemnode, index, return_value); + } +} + +static void dom_map_get_attributes_item(dom_nnodemap_object *map, zend_long index, zval *return_value) +{ + dom_map_get_chain_item(map, index, return_value, dom_map_get_attr_start); +} + +static void dom_map_get_nodes_item(dom_nnodemap_object *map, zend_long index, zval *return_value) +{ + dom_map_get_chain_item(map, index, return_value, dom_nodelist_iter_start_first_child); +} + +static void dom_map_get_by_tag_name_item(dom_nnodemap_object *map, zend_long index, zval *return_value) +{ + xmlNodePtr nodep = dom_object_get_node(map->baseobj); + xmlNodePtr itemnode = NULL; + if (nodep && index >= 0) { + zend_long count = 0; + dom_node_idx_pair start_point = dom_obj_map_get_start_point(map, nodep, index); + itemnode = start_point.node ? start_point.node : php_dom_first_child_of_container_node(nodep); + itemnode = dom_get_elements_by_tag_name_ns_raw(nodep, itemnode, map->ns, map->local, map->local_lower, &count, start_point.index); + } + dom_ret_node_to_zobj(map, itemnode, return_value); + if (itemnode) { + dom_map_cache_obj(map, itemnode, index, return_value); + } +} + +static void dom_map_get_null_item(dom_nnodemap_object *map, zend_long index, zval *return_value) +{ + RETURN_NULL(); +} + +/*********************** + * === Common APIs === * + ***********************/ + +zend_long php_dom_get_nodelist_length(dom_object *obj) +{ + dom_nnodemap_object *objmap = obj->ptr; + if (!objmap) { + return 0; + } + + if (objmap->handler->use_cache) { + xmlNodePtr nodep = dom_object_get_node(objmap->baseobj); + if (!nodep) { + return 0; + } + + if (!php_dom_is_cache_tag_stale_from_node(&objmap->cache_tag, nodep)) { + if (objmap->cached_length >= 0) { + return objmap->cached_length; + } + /* Only the length is out-of-date, the cache tag is still valid. + * Therefore, only overwrite the length and keep the currently cached object. */ + } else { + php_dom_mark_cache_tag_up_to_date_from_node(&objmap->cache_tag, nodep); + reset_objmap_cache(objmap); + } + } + + zend_long count = objmap->handler->length(objmap); + + if (objmap->handler->use_cache) { + objmap->cached_length = count; + } + + return count; +} + +/********************** + * === Named item === * + **********************/ + +static xmlNodePtr dom_map_get_named_item_entity(dom_nnodemap_object *map, const zend_string *named, const char *ns) +{ + return xmlHashLookup(map->ht, BAD_CAST ZSTR_VAL(named)); +} + +static bool dom_map_has_named_item_xmlht(dom_nnodemap_object *map, const zend_string *named, const char *ns) +{ + return dom_map_get_named_item_entity(map, named, ns) != NULL; +} + +static xmlNodePtr dom_map_get_named_item_notation(dom_nnodemap_object *map, const zend_string *named, const char *ns) +{ + xmlNotationPtr notation = xmlHashLookup(map->ht, BAD_CAST ZSTR_VAL(named)); + if (notation) { + return create_notation(notation->name, notation->PublicID, notation->SystemID); + } + return NULL; +} + +static xmlNodePtr dom_map_get_named_item_prop(dom_nnodemap_object *map, const zend_string *named, const char *ns) +{ + xmlNodePtr nodep = dom_object_get_node(map->baseobj); + if (nodep) { + if (php_dom_follow_spec_intern(map->baseobj)) { + return (xmlNodePtr) php_dom_get_attribute_node(nodep, BAD_CAST ZSTR_VAL(named), ZSTR_LEN(named)); + } else { + if (ns) { + return (xmlNodePtr) xmlHasNsProp(nodep, BAD_CAST ZSTR_VAL(named), BAD_CAST ns); + } else { + return (xmlNodePtr) xmlHasProp(nodep, BAD_CAST ZSTR_VAL(named)); + } + } + } + return NULL; +} + +static bool dom_map_has_named_item_prop(dom_nnodemap_object *map, const zend_string *named, const char *ns) +{ + return dom_map_get_named_item_prop(map, named, ns) != NULL; +} + +static xmlNodePtr dom_map_get_named_item_null(dom_nnodemap_object *map, const zend_string *named, const char *ns) +{ + return NULL; +} + +static bool dom_map_has_named_item_null(dom_nnodemap_object *map, const zend_string *named, const char *ns) +{ + return false; +} + +/************************** + * === Handler tables === * + **************************/ + +const php_dom_obj_map_handler php_dom_obj_map_attributes = { + .length = dom_map_get_prop_length, + .get_item = dom_map_get_attributes_item, + .get_named_item = dom_map_get_named_item_prop, + .has_named_item = dom_map_has_named_item_prop, + .use_cache = true, + .nameless = false, +}; + +const php_dom_obj_map_handler php_dom_obj_map_by_tag_name = { + .length = dom_map_get_by_tag_name_length, + .get_item = dom_map_get_by_tag_name_item, + .get_named_item = dom_map_get_named_item_null, + .has_named_item = dom_map_has_named_item_null, + .use_cache = true, + .nameless = true, +}; + +const php_dom_obj_map_handler php_dom_obj_map_child_nodes = { + .length = dom_map_get_nodes_length, + .get_item = dom_map_get_nodes_item, + .get_named_item = dom_map_get_named_item_null, + .has_named_item = dom_map_has_named_item_null, + .use_cache = true, + .nameless = true, +}; + +const php_dom_obj_map_handler php_dom_obj_map_nodeset = { + .length = dom_map_get_nodeset_length, + .get_item = dom_map_get_nodeset_item, + .get_named_item = dom_map_get_named_item_null, + .has_named_item = dom_map_has_named_item_null, + .use_cache = false, + .nameless = true, +}; + +const php_dom_obj_map_handler php_dom_obj_map_entities = { + .length = dom_map_get_xmlht_length, + .get_item = dom_map_get_entity_item, + .get_named_item = dom_map_get_named_item_entity, + .has_named_item = dom_map_has_named_item_xmlht, + .use_cache = false, + .nameless = false, +}; + +const php_dom_obj_map_handler php_dom_obj_map_notations = { + .length = dom_map_get_xmlht_length, + .get_item = dom_map_get_notation_item, + .get_named_item = dom_map_get_named_item_notation, + .has_named_item = dom_map_has_named_item_xmlht, + .use_cache = false, + .nameless = false, +}; + +const php_dom_obj_map_handler php_dom_obj_map_noop = { + .length = dom_map_get_zero_length, + .get_item = dom_map_get_null_item, + .get_named_item = dom_map_get_named_item_null, + .has_named_item = dom_map_has_named_item_null, + .use_cache = false, + .nameless = true, +}; + +#endif diff --git a/ext/dom/obj_map.h b/ext/dom/obj_map.h new file mode 100644 index 0000000000000..511b71fcc7485 --- /dev/null +++ b/ext/dom/obj_map.h @@ -0,0 +1,39 @@ +/* + +----------------------------------------------------------------------+ + | Copyright (c) The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | https://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Niels Dossche | + +----------------------------------------------------------------------+ +*/ + +#ifndef PHP_DOM_OBJ_MAP_H +#define PHP_DOM_OBJ_MAP_H + +typedef struct dom_nnodemap_object dom_nnodemap_object; + +typedef struct php_dom_obj_map_handler { + zend_long (*length)(dom_nnodemap_object *); + void (*get_item)(dom_nnodemap_object *, zend_long, zval *); + xmlNodePtr (*get_named_item)(dom_nnodemap_object *, const zend_string *, const char *); + bool (*has_named_item)(dom_nnodemap_object *, const zend_string *, const char *); + bool use_cache; + bool nameless; +} php_dom_obj_map_handler; + +extern const php_dom_obj_map_handler php_dom_obj_map_attributes; +extern const php_dom_obj_map_handler php_dom_obj_map_by_tag_name; +extern const php_dom_obj_map_handler php_dom_obj_map_child_nodes; +extern const php_dom_obj_map_handler php_dom_obj_map_nodeset; +extern const php_dom_obj_map_handler php_dom_obj_map_entities; +extern const php_dom_obj_map_handler php_dom_obj_map_notations; +extern const php_dom_obj_map_handler php_dom_obj_map_noop; + +#endif diff --git a/ext/dom/parentnode/css_selectors.c b/ext/dom/parentnode/css_selectors.c index e681d2b9073fe..10b517a248d46 100644 --- a/ext/dom/parentnode/css_selectors.c +++ b/ext/dom/parentnode/css_selectors.c @@ -249,7 +249,7 @@ void dom_parent_node_query_selector_all(xmlNodePtr thisp, dom_object *intern, zv dom_object *ret_obj = Z_DOMOBJ_P(return_value); dom_nnodemap_object *mapptr = (dom_nnodemap_object *) ret_obj->ptr; ZVAL_ARR(&mapptr->baseobj_zv, list); - mapptr->nodetype = DOM_NODESET; + mapptr->handler = &php_dom_obj_map_nodeset; } } diff --git a/ext/dom/php_dom.c b/ext/dom/php_dom.c index b1f7eca80f104..d9ed01d2e7d6e 100644 --- a/ext/dom/php_dom.c +++ b/ext/dom/php_dom.c @@ -1456,7 +1456,8 @@ void dom_objects_free_storage(zend_object *object) } /* }}} */ -void dom_namednode_iter(dom_object *basenode, int ntype, dom_object *intern, xmlHashTablePtr ht, zend_string *local, zend_string *ns) /* {{{ */ +/* TODO: move me to obj_map.c */ +void dom_namednode_iter(dom_object *basenode, dom_object *intern, xmlHashTablePtr ht, zend_string *local, zend_string *ns, const php_dom_obj_map_handler *handler) /* {{{ */ { dom_nnodemap_object *mapptr = (dom_nnodemap_object *) intern->ptr; @@ -1466,8 +1467,8 @@ void dom_namednode_iter(dom_object *basenode, int ntype, dom_object *intern, xml xmlDocPtr doc = basenode->document ? basenode->document->ptr : NULL; + mapptr->handler = handler; mapptr->baseobj = basenode; - mapptr->nodetype = ntype; mapptr->ht = ht; if (EXPECTED(doc != NULL)) { mapptr->dict = doc->dict; @@ -1607,6 +1608,7 @@ zend_object *dom_nnodemap_objects_new(zend_class_entry *class_type) intern->ptr = ecalloc(1, sizeof(dom_nnodemap_object)); dom_nnodemap_object *objmap = intern->ptr; objmap->cached_length = -1; + objmap->handler = &php_dom_obj_map_noop; return &intern->std; } @@ -2380,7 +2382,7 @@ static zval *dom_nodemap_read_dimension(zend_object *object, zval *offset, int t zend_long lval; if (dom_nodemap_or_nodelist_process_offset_as_named(offset, &lval)) { /* exceptional case, switch to named lookup */ - php_dom_named_node_map_get_named_item_into_zval(php_dom_obj_from_obj(object)->ptr, Z_STR_P(offset), rv); + php_dom_named_node_map_get_named_item_into_zval(php_dom_obj_from_obj(object)->ptr, Z_STR_P(offset), NULL, rv); return rv; } @@ -2390,7 +2392,8 @@ static zval *dom_nodemap_read_dimension(zend_object *object, zval *offset, int t return NULL; } - php_dom_named_node_map_get_item_into_zval(php_dom_obj_from_obj(object)->ptr, lval, rv); + dom_nnodemap_object *map = php_dom_obj_from_obj(object)->ptr; + map->handler->get_item(map, lval, rv); return rv; } @@ -2404,7 +2407,8 @@ static int dom_nodemap_has_dimension(zend_object *object, zval *member, int chec zend_long offset; if (dom_nodemap_or_nodelist_process_offset_as_named(member, &offset)) { /* exceptional case, switch to named lookup */ - return php_dom_named_node_map_get_named_item(php_dom_obj_from_obj(object)->ptr, Z_STR_P(member), false) != NULL; + dom_nnodemap_object *map = php_dom_obj_from_obj(object)->ptr; + return map->handler->has_named_item(map, Z_STR_P(member), NULL); } return offset >= 0 && offset < php_dom_get_namednodemap_length(php_dom_obj_from_obj(object)); @@ -2423,14 +2427,14 @@ static zval *dom_modern_nodemap_read_dimension(zend_object *object, zval *offset if (Z_TYPE_P(offset) == IS_STRING) { zend_ulong lval; if (ZEND_HANDLE_NUMERIC(Z_STR_P(offset), lval)) { - php_dom_named_node_map_get_item_into_zval(map, (zend_long) lval, rv); + map->handler->get_item(map, (zend_long) lval, rv); } else { - php_dom_named_node_map_get_named_item_into_zval(map, Z_STR_P(offset), rv); + php_dom_named_node_map_get_named_item_into_zval(map, Z_STR_P(offset), NULL, rv); } } else if (Z_TYPE_P(offset) == IS_LONG) { - php_dom_named_node_map_get_item_into_zval(map, Z_LVAL_P(offset), rv); + map->handler->get_item(map, Z_LVAL_P(offset), rv); } else if (Z_TYPE_P(offset) == IS_DOUBLE) { - php_dom_named_node_map_get_item_into_zval(map, zend_dval_to_lval_safe(Z_DVAL_P(offset)), rv); + map->handler->get_item(map, zend_dval_to_lval_safe(Z_DVAL_P(offset)), rv); } else { zend_illegal_container_offset(object->ce->name, offset, type); return NULL; @@ -2453,7 +2457,7 @@ static int dom_modern_nodemap_has_dimension(zend_object *object, zval *member, i if (ZEND_HANDLE_NUMERIC(Z_STR_P(member), lval)) { return (zend_long) lval >= 0 && (zend_long) lval < php_dom_get_namednodemap_length(obj); } else { - return php_dom_named_node_map_get_named_item(map, Z_STR_P(member), false) != NULL; + return map->handler->has_named_item(map, Z_STR_P(member), NULL); } } else if (Z_TYPE_P(member) == IS_LONG) { zend_long offset = Z_LVAL_P(member); diff --git a/ext/dom/php_dom.h b/ext/dom/php_dom.h index ede77f08626cc..571f99cfe2be3 100644 --- a/ext/dom/php_dom.h +++ b/ext/dom/php_dom.h @@ -56,13 +56,12 @@ extern zend_module_entry dom_module_entry; #include "xpath_callbacks.h" #include "zend_exceptions.h" #include "dom_ce.h" +#include "obj_map.h" /* DOM API_VERSION, please bump it up, if you change anything in the API therefore it's easier for the script-programmers to check, what's working how Can be checked with phpversion("dom"); */ #define DOM_API_VERSION "20031129" -/* Define a custom type for iterating using an unused nodetype */ -#define DOM_NODESET XML_XINCLUDE_START typedef struct dom_xpath_object { php_dom_xpath_callbacks xpath_callbacks; @@ -80,7 +79,6 @@ static inline dom_xpath_object *php_xpath_obj_from_obj(zend_object *obj) { typedef struct dom_nnodemap_object { dom_object *baseobj; zval baseobj_zv; - int nodetype; int cached_length; xmlHashTable *ht; xmlChar *local; @@ -90,6 +88,7 @@ typedef struct dom_nnodemap_object { dom_object *cached_obj; zend_long cached_obj_index; xmlDictPtr dict; + const php_dom_obj_map_handler *handler; bool release_local : 1; bool release_ns : 1; } dom_nnodemap_object; @@ -146,9 +145,9 @@ int dom_hierarchy(xmlNodePtr parent, xmlNodePtr child); bool dom_has_feature(zend_string *feature, zend_string *version); bool dom_node_is_read_only(const xmlNode *node); bool dom_node_children_valid(const xmlNode *node); -void dom_namednode_iter(dom_object *basenode, int ntype, dom_object *intern, xmlHashTablePtr ht, zend_string *local, zend_string *ns); +void dom_namednode_iter(dom_object *basenode, dom_object *intern, xmlHashTablePtr ht, zend_string *local, zend_string *ns, const php_dom_obj_map_handler *handler); xmlNodePtr create_notation(const xmlChar *name, const xmlChar *ExternalID, const xmlChar *SystemID); -xmlNode *php_dom_libxml_hash_iter(dom_nnodemap_object *objmap, int index); +xmlNode *php_dom_libxml_hash_iter(xmlHashTable *ht, int index); zend_object_iterator *php_dom_get_iterator(zend_class_entry *ce, zval *object, int by_ref); void dom_set_doc_classmap(php_libxml_ref_obj *document, zend_class_entry *basece, zend_class_entry *ce); xmlNodePtr php_dom_create_fake_namespace_decl(xmlNodePtr nodep, xmlNsPtr original, zval *return_value, dom_object *parent_intern); @@ -211,10 +210,8 @@ void dom_element_closest(xmlNodePtr thisp, dom_object *intern, zval *return_valu xmlNodePtr dom_parse_fragment(dom_object *obj, xmlNodePtr context_node, const zend_string *input); /* nodemap and nodelist APIs */ -xmlNodePtr php_dom_named_node_map_get_named_item(dom_nnodemap_object *objmap, const zend_string *named, bool may_transform); -void php_dom_named_node_map_get_named_item_into_zval(dom_nnodemap_object *objmap, const zend_string *named, zval *return_value); +void php_dom_named_node_map_get_named_item_into_zval(dom_nnodemap_object *objmap, const zend_string *named, const char *ns, zval *return_value); xmlNodePtr php_dom_named_node_map_get_item(dom_nnodemap_object *objmap, zend_long index); -void php_dom_named_node_map_get_item_into_zval(dom_nnodemap_object *objmap, zend_long index, zval *return_value); zend_long php_dom_get_namednodemap_length(dom_object *obj); xmlNodePtr dom_nodelist_iter_start_first_child(xmlNodePtr nodep); diff --git a/ext/dom/tests/modern/spec/Document_importLegacyNode.phpt b/ext/dom/tests/modern/spec/Document_importLegacyNode.phpt index 8a6f324c3dc35..bc8c8fe050e4f 100644 --- a/ext/dom/tests/modern/spec/Document_importLegacyNode.phpt +++ b/ext/dom/tests/modern/spec/Document_importLegacyNode.phpt @@ -36,8 +36,8 @@ foreach ($new->getElementsByTagName('child') as $child) { echo $new->saveXml(), "\n"; ?> ---EXPECT-- -object(Dom\NamedNodeMap)#5 (1) { +--EXPECTF-- +object(Dom\NamedNodeMap)#%d (1) { ["length"]=> int(2) } @@ -47,7 +47,7 @@ namespaceURI: string(29) "http://www.w3.org/2000/xmlns/" name: string(1) "a" prefix: NULL namespaceURI: NULL -object(Dom\NamedNodeMap)#3 (1) { +object(Dom\NamedNodeMap)#%d (1) { ["length"]=> int(3) } diff --git a/ext/dom/tests/modern/spec/NamedNodeMap_dimensions.phpt b/ext/dom/tests/modern/spec/NamedNodeMap_dimensions.phpt index 38e00defe6223..cf446881559bc 100644 --- a/ext/dom/tests/modern/spec/NamedNodeMap_dimensions.phpt +++ b/ext/dom/tests/modern/spec/NamedNodeMap_dimensions.phpt @@ -22,7 +22,7 @@ foreach ($test_values as $value) { ?> --EXPECTF-- --- -1 --- -string(1) "a" +string(3) "N/A" bool(false) bool(true) --- 0 --- diff --git a/ext/dom/xpath.c b/ext/dom/xpath.c index 8707af1fa94cf..b410f7b264997 100644 --- a/ext/dom/xpath.c +++ b/ext/dom/xpath.c @@ -239,7 +239,7 @@ static void dom_xpath_iter(zval *baseobj, dom_object *intern) /* {{{ */ dom_nnodemap_object *mapptr = (dom_nnodemap_object *) intern->ptr; ZVAL_COPY_VALUE(&mapptr->baseobj_zv, baseobj); - mapptr->nodetype = DOM_NODESET; + mapptr->handler = &php_dom_obj_map_nodeset; } /* }}} */ From 0ab5f70b3cc9705873586657f9910a7dd7d466f4 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Sat, 21 Jun 2025 21:57:16 +0200 Subject: [PATCH 112/682] ext/spl: Remove bool type coercions in tests --- ext/spl/tests/bug36287.phpt | 2 +- ext/spl/tests/iterator_003.phpt | 2 +- ext/spl/tests/pqueue_001.phpt | 8 ++++---- ext/spl/tests/spl_003.phpt | 6 +++--- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/ext/spl/tests/bug36287.phpt b/ext/spl/tests/bug36287.phpt index 0fa6e1bc1eefd..8ea9123eb8a46 100644 --- a/ext/spl/tests/bug36287.phpt +++ b/ext/spl/tests/bug36287.phpt @@ -3,7 +3,7 @@ Bug #36287 (Segfault with SplFileInfo conversion) --FILE-- students->getIterator(), true); + return new CachingIterator($this->students->getIterator()); } } diff --git a/ext/spl/tests/pqueue_001.phpt b/ext/spl/tests/pqueue_001.phpt index 2b67ad4a2a247..a318487273eb7 100644 --- a/ext/spl/tests/pqueue_001.phpt +++ b/ext/spl/tests/pqueue_001.phpt @@ -16,7 +16,7 @@ $pq->insert("b", 2); $pq->insert("c", 0); foreach ($pq as $k=>$v) { - echo "$k=>".print_r($v, 1)."\n"; + echo "$k=>".print_r($v, true)."\n"; } echo "EXTR_BOTH\n"; @@ -29,7 +29,7 @@ $pq1->insert("b", 2); $pq1->insert("c", 0); foreach ($pq1 as $k=>$v) { - echo "$k=>".print_r($v, 1)."\n"; + echo "$k=>".print_r($v, true)."\n"; } echo "EXTR_DATA\n"; @@ -42,7 +42,7 @@ $pq2->insert("b", 2); $pq2->insert("c", 0); foreach ($pq2 as $k=>$v) { - echo "$k=>".print_r($v, 1)."\n"; + echo "$k=>".print_r($v, true)."\n"; } echo "EXTR_PRIORITY\n"; @@ -55,7 +55,7 @@ $pq3->insert("b", 2); $pq3->insert("c", 0); foreach ($pq3 as $k=>$v) { - echo "$k=>".print_r($v, 1)."\n"; + echo "$k=>".print_r($v, true)."\n"; } ?> diff --git a/ext/spl/tests/spl_003.phpt b/ext/spl/tests/spl_003.phpt index a9080c7298d25..f7e70db90f421 100644 --- a/ext/spl/tests/spl_003.phpt +++ b/ext/spl/tests/spl_003.phpt @@ -16,8 +16,8 @@ var_dump(class_parents(new c), class_parents(new b), class_parents("b"), class_parents("d"), - class_parents("foo", 0), - class_parents("foo", 1) + class_parents("foo", false), + class_parents("foo", true) ); interface iface1{} @@ -26,7 +26,7 @@ class f implements iface1, iface2{} var_dump(class_implements(new a), class_implements("a"), class_implements("aaa"), - class_implements("bbb", 0) + class_implements("bbb", false) ); ?> From 091308cb3e3ac8ddb4438d1c9ae1e67ca3492b9c Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Sat, 21 Jun 2025 22:01:49 +0200 Subject: [PATCH 113/682] ext/soap: Remove bool type coercions in tests --- .../tests/interop/Round4/GroupH/r4_groupH_soapfault_004w.phpt | 2 +- ext/soap/tests/interop/Round4/GroupI/r4_groupI_xsd_030w.phpt | 2 +- ext/soap/tests/interop/Round4/GroupI/r4_groupI_xsd_031w.phpt | 2 +- ext/soap/tests/interop/Round4/GroupI/r4_groupI_xsd_032w.phpt | 2 +- ext/soap/tests/interop/Round4/GroupI/r4_groupI_xsd_033w.phpt | 2 +- ext/soap/tests/interop/Round4/GroupI/r4_groupI_xsd_034w.phpt | 2 +- ext/soap/tests/interop/Round4/GroupI/r4_groupI_xsd_035w.phpt | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/ext/soap/tests/interop/Round4/GroupH/r4_groupH_soapfault_004w.phpt b/ext/soap/tests/interop/Round4/GroupH/r4_groupH_soapfault_004w.phpt index 5c50cbc840345..69ba8880ad581 100644 --- a/ext/soap/tests/interop/Round4/GroupH/r4_groupH_soapfault_004w.phpt +++ b/ext/soap/tests/interop/Round4/GroupH/r4_groupH_soapfault_004w.phpt @@ -6,7 +6,7 @@ soap soap.wsdl_cache_enabled=0 --FILE-- 1,"exceptions"=>0)); $client->__soapCall("echoVersionMismatchFault",array(), null, $hdr); echo $client->__getlastrequest(); diff --git a/ext/soap/tests/interop/Round4/GroupI/r4_groupI_xsd_030w.phpt b/ext/soap/tests/interop/Round4/GroupI/r4_groupI_xsd_030w.phpt index 1812b7751190b..cc5c5e608a784 100644 --- a/ext/soap/tests/interop/Round4/GroupI/r4_groupI_xsd_030w.phpt +++ b/ext/soap/tests/interop/Round4/GroupI/r4_groupI_xsd_030w.phpt @@ -6,7 +6,7 @@ soap soap.wsdl_cache_enabled=0 --FILE-- "Hello World"), 1); +$hdr = new SoapHeader("http://soapinterop.org/","echoMeStringRequest", array("varString"=>"Hello World"), true); $client = new SoapClient(__DIR__."/round4_groupI_xsd.wsdl",array("trace"=>1,"exceptions"=>0)); $client->__soapCall("echoVoidSoapHeader",array(),null,$hdr); echo $client->__getlastrequest(); diff --git a/ext/soap/tests/interop/Round4/GroupI/r4_groupI_xsd_031w.phpt b/ext/soap/tests/interop/Round4/GroupI/r4_groupI_xsd_031w.phpt index 2b89e057e99cc..d0255f1de5479 100644 --- a/ext/soap/tests/interop/Round4/GroupI/r4_groupI_xsd_031w.phpt +++ b/ext/soap/tests/interop/Round4/GroupI/r4_groupI_xsd_031w.phpt @@ -6,7 +6,7 @@ soap soap.wsdl_cache_enabled=0 --FILE-- 1,"exceptions"=>0)); $client->__soapCall("echoVoidSoapHeader",array(),null,$hdr); echo $client->__getlastrequest(); diff --git a/ext/soap/tests/interop/Round4/GroupI/r4_groupI_xsd_032w.phpt b/ext/soap/tests/interop/Round4/GroupI/r4_groupI_xsd_032w.phpt index 508f3c0a32553..57b3292764c16 100644 --- a/ext/soap/tests/interop/Round4/GroupI/r4_groupI_xsd_032w.phpt +++ b/ext/soap/tests/interop/Round4/GroupI/r4_groupI_xsd_032w.phpt @@ -7,7 +7,7 @@ precision=14 soap.wsdl_cache_enabled=0 --FILE-- 34,"varString"=>"arg","varFloat"=>12.345), 1); +$hdr = new SoapHeader("http://soapinterop.org/","echoMeComplexTypeRequest", array("varInt"=>34,"varString"=>"arg","varFloat"=>12.345), true); $client = new SoapClient(__DIR__."/round4_groupI_xsd.wsdl",array("trace"=>1,"exceptions"=>0)); $client->__soapCall("echoVoidSoapHeader",array(),null,$hdr); echo $client->__getlastrequest(); diff --git a/ext/soap/tests/interop/Round4/GroupI/r4_groupI_xsd_033w.phpt b/ext/soap/tests/interop/Round4/GroupI/r4_groupI_xsd_033w.phpt index c2b47eeaa6918..677641c98153e 100644 --- a/ext/soap/tests/interop/Round4/GroupI/r4_groupI_xsd_033w.phpt +++ b/ext/soap/tests/interop/Round4/GroupI/r4_groupI_xsd_033w.phpt @@ -7,7 +7,7 @@ precision=14 soap.wsdl_cache_enabled=0 --FILE-- 34,"varFloat"=>12.345), 1); +$hdr = new SoapHeader("http://soapinterop.org/","echoMeComplexTypeRequest", array("varInt"=>34,"varFloat"=>12.345), true); $client = new SoapClient(__DIR__."/round4_groupI_xsd.wsdl",array("trace"=>1,"exceptions"=>0)); $client->__soapCall("echoVoidSoapHeader",array(),null,$hdr); echo $client->__getlastrequest(); diff --git a/ext/soap/tests/interop/Round4/GroupI/r4_groupI_xsd_034w.phpt b/ext/soap/tests/interop/Round4/GroupI/r4_groupI_xsd_034w.phpt index 2804699a3891c..cbb3b4ed34870 100644 --- a/ext/soap/tests/interop/Round4/GroupI/r4_groupI_xsd_034w.phpt +++ b/ext/soap/tests/interop/Round4/GroupI/r4_groupI_xsd_034w.phpt @@ -6,7 +6,7 @@ soap soap.wsdl_cache_enabled=0 --FILE-- "Hello World"), 1, SOAP_ACTOR_NEXT); +$hdr = new SoapHeader("http://soapinterop.org/","echoMeStringRequest", array("varString"=>"Hello World"), true, SOAP_ACTOR_NEXT); $client = new SoapClient(__DIR__."/round4_groupI_xsd.wsdl",array("trace"=>1,"exceptions"=>0)); $client->__soapCall("echoVoidSoapHeader",array(),null,$hdr); echo $client->__getlastrequest(); diff --git a/ext/soap/tests/interop/Round4/GroupI/r4_groupI_xsd_035w.phpt b/ext/soap/tests/interop/Round4/GroupI/r4_groupI_xsd_035w.phpt index 75eb891533b34..0e2674c9bf4e8 100644 --- a/ext/soap/tests/interop/Round4/GroupI/r4_groupI_xsd_035w.phpt +++ b/ext/soap/tests/interop/Round4/GroupI/r4_groupI_xsd_035w.phpt @@ -7,7 +7,7 @@ precision=14 soap.wsdl_cache_enabled=0 --FILE-- 34,"varString"=>"arg","varFloat"=>12.345), 1, SOAP_ACTOR_NEXT); +$hdr = new SoapHeader("http://soapinterop.org/","echoMeComplexTypeRequest", array("varInt"=>34,"varString"=>"arg","varFloat"=>12.345), true, SOAP_ACTOR_NEXT); $client = new SoapClient(__DIR__."/round4_groupI_xsd.wsdl",array("trace"=>1,"exceptions"=>0)); $client->__soapCall("echoVoidSoapHeader",array(),null,$hdr); echo $client->__getlastrequest(); From 8316ff2b0a75cbb76f7fc4152d8f5b63f3873161 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Sat, 21 Jun 2025 22:05:53 +0200 Subject: [PATCH 114/682] ext/dba: Remove bool type coercions in tests --- ext/dba/tests/dba_handlers.phpt | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/ext/dba/tests/dba_handlers.phpt b/ext/dba/tests/dba_handlers.phpt index b0dd54242370a..bc00d2ec6d4ce 100644 --- a/ext/dba/tests/dba_handlers.phpt +++ b/ext/dba/tests/dba_handlers.phpt @@ -37,12 +37,8 @@ echo "Test 2\n"; check(dba_handlers(false)); -echo "Test 3\n"; - -check(dba_handlers(0)); - -echo "Test 4 - full info\n"; -$h = dba_handlers(1); +echo "Test 3 - full info\n"; +$h = dba_handlers(true); foreach ($h as $key => $val) { if ($key === "flatfile") { echo "Success: flatfile enabled\n"; @@ -60,7 +56,5 @@ Test 1 Success: flatfile enabled Test 2 Success: flatfile enabled -Test 3 -Success: flatfile enabled -Test 4 - full info +Test 3 - full info Success: flatfile enabled From 5f3e10de8bb4c0171a361e6e2565fbeaad282b6f Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Sat, 21 Jun 2025 22:07:16 +0200 Subject: [PATCH 115/682] ext/bz2: Remove bool type coercions in tests --- ext/bz2/tests/005.phpt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ext/bz2/tests/005.phpt b/ext/bz2/tests/005.phpt index d32d5b687f0f5..8a715787f31d0 100644 --- a/ext/bz2/tests/005.phpt +++ b/ext/bz2/tests/005.phpt @@ -18,12 +18,12 @@ $data2 = bzcompress($string, 1, 10); $data3 = $data2; $data3[3] = 0; -var_dump(bzdecompress(1,1)); +var_dump(bzdecompress(1, true)); var_dump(bzdecompress($data3)); -var_dump(bzdecompress($data3,1)); +var_dump(bzdecompress($data3, true)); -var_dump(bzdecompress($data, 0)); -var_dump(bzdecompress($data, 1)); +var_dump(bzdecompress($data, false)); +var_dump(bzdecompress($data, true)); var_dump(bzdecompress($data)); var_dump(bzdecompress($data2)); From fa81a231385495d440f879234c8110715cc82020 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Sat, 21 Jun 2025 22:07:54 +0200 Subject: [PATCH 116/682] ext/exif: Remove bool type coercions in tests --- ext/exif/tests/bug72627.phpt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/exif/tests/bug72627.phpt b/ext/exif/tests/bug72627.phpt index 179619ff72349..b0bc50777bba7 100644 --- a/ext/exif/tests/bug72627.phpt +++ b/ext/exif/tests/bug72627.phpt @@ -4,7 +4,7 @@ Bug #72627 (Memory Leakage In exif_process_IFD_in_TIFF) exif --FILE-- --EXPECTF-- From 37549e4563af40cf46484c3d9174403c85a94bb4 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Sat, 21 Jun 2025 22:11:42 +0200 Subject: [PATCH 117/682] ext/gmp: Remove bool type coercions in tests --- ext/gmp/tests/gmp_setbit.phpt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ext/gmp/tests/gmp_setbit.phpt b/ext/gmp/tests/gmp_setbit.phpt index 70b9e50694823..775042ef9737f 100644 --- a/ext/gmp/tests/gmp_setbit.phpt +++ b/ext/gmp/tests/gmp_setbit.phpt @@ -6,30 +6,30 @@ gmp getMessage() . \PHP_EOL; } var_dump(gmp_strval($n)); $n = gmp_init(5); -gmp_setbit($n, 2, 0); +gmp_setbit($n, 2, false); var_dump(gmp_strval($n)); $n = gmp_init(5); -gmp_setbit($n, 1, 1); +gmp_setbit($n, 1, true); var_dump(gmp_strval($n)); $n = gmp_init("100000000000"); -gmp_setbit($n, 23, 1); +gmp_setbit($n, 23, true); var_dump(gmp_strval($n)); -gmp_setbit($n, 23, 0); +gmp_setbit($n, 23, false); var_dump(gmp_strval($n)); gmp_setbit($n, 3); From 8526de84a504cc3fead9a1734ea663e8fd2669f3 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sat, 21 Jun 2025 22:32:11 +0200 Subject: [PATCH 118/682] Move common obj_map API functions to obj_map.c --- ext/dom/documenttype.c | 5 ++- ext/dom/dom_iterators.c | 2 +- ext/dom/element.c | 6 +-- ext/dom/html_collection.c | 3 +- ext/dom/namednodemap.c | 15 ++------ ext/dom/node.c | 5 ++- ext/dom/nodelist.c | 14 ++----- ext/dom/nodelist.h | 2 - ext/dom/obj_map.c | 61 ++++++++++++++++++++++++++++++ ext/dom/obj_map.h | 34 ++++++++++++++--- ext/dom/parentnode/css_selectors.c | 1 + ext/dom/php_dom.c | 51 ++----------------------- ext/dom/php_dom.h | 22 +---------- ext/dom/xpath.c | 2 +- 14 files changed, 114 insertions(+), 109 deletions(-) diff --git a/ext/dom/documenttype.c b/ext/dom/documenttype.c index 895a34cebf650..63da0306649a9 100644 --- a/ext/dom/documenttype.c +++ b/ext/dom/documenttype.c @@ -22,6 +22,7 @@ #include "php.h" #if defined(HAVE_LIBXML) && defined(HAVE_DOM) #include "php_dom.h" +#include "obj_map.h" #include "dom_properties.h" #include "internal_helpers.h" @@ -53,7 +54,7 @@ zend_result dom_documenttype_entities_read(dom_object *obj, zval *retval) xmlHashTable *entityht = (xmlHashTable *) dtdptr->entities; dom_object *intern = Z_DOMOBJ_P(retval); - dom_namednode_iter(obj, intern, entityht, NULL, NULL, &php_dom_obj_map_entities); + php_dom_create_obj_map(obj, intern, entityht, NULL, NULL, &php_dom_obj_map_entities); return SUCCESS; } @@ -74,7 +75,7 @@ zend_result dom_documenttype_notations_read(dom_object *obj, zval *retval) xmlHashTable *notationht = (xmlHashTable *) dtdptr->notations; dom_object *intern = Z_DOMOBJ_P(retval); - dom_namednode_iter(obj, intern, notationht, NULL, NULL, &php_dom_obj_map_notations); + php_dom_create_obj_map(obj, intern, notationht, NULL, NULL, &php_dom_obj_map_notations); return SUCCESS; } diff --git a/ext/dom/dom_iterators.c b/ext/dom/dom_iterators.c index 19c4ea41adadb..90e973723f6c6 100644 --- a/ext/dom/dom_iterators.c +++ b/ext/dom/dom_iterators.c @@ -22,7 +22,7 @@ #include "php.h" #if defined(HAVE_LIBXML) && defined(HAVE_DOM) #include "php_dom.h" -#include "dom_ce.h" +#include "obj_map.h" typedef struct nodeIterator { int cur; diff --git a/ext/dom/element.c b/ext/dom/element.c index d0fc0b4a4d3e2..27e2845c9f0cb 100644 --- a/ext/dom/element.c +++ b/ext/dom/element.c @@ -23,8 +23,8 @@ #if defined(HAVE_LIBXML) && defined(HAVE_DOM) #include "zend_enum.h" #include "php_dom.h" +#include "obj_map.h" #include "namespace_compat.h" -#include "private_data.h" #include "internal_helpers.h" #include "dom_properties.h" #include "token_list.h" @@ -825,7 +825,7 @@ static void dom_element_get_elements_by_tag_name(INTERNAL_FUNCTION_PARAMETERS, z object_init_ex(return_value, iter_ce); namednode = Z_DOMOBJ_P(return_value); - dom_namednode_iter(intern, namednode, NULL, name, NULL, &php_dom_obj_map_by_tag_name); + php_dom_create_obj_map(intern, namednode, NULL, name, NULL, &php_dom_obj_map_by_tag_name); } PHP_METHOD(DOMElement, getElementsByTagName) @@ -1257,7 +1257,7 @@ static void dom_element_get_elements_by_tag_name_ns(INTERNAL_FUNCTION_PARAMETERS object_init_ex(return_value, iter_ce); namednode = Z_DOMOBJ_P(return_value); - dom_namednode_iter(intern, namednode, NULL, name, uri, &php_dom_obj_map_by_tag_name); + php_dom_create_obj_map(intern, namednode, NULL, name, uri, &php_dom_obj_map_by_tag_name); } PHP_METHOD(DOMElement, getElementsByTagNameNS) diff --git a/ext/dom/html_collection.c b/ext/dom/html_collection.c index e4c0446016685..e5dca84de75ff 100644 --- a/ext/dom/html_collection.c +++ b/ext/dom/html_collection.c @@ -21,6 +21,7 @@ #include "php.h" #if defined(HAVE_LIBXML) && defined(HAVE_DOM) #include "php_dom.h" +#include "obj_map.h" #include "nodelist.h" #include "html_collection.h" #include "namespace_compat.h" @@ -115,7 +116,7 @@ zval *dom_html_collection_read_dimension(zend_object *object, zval *offset, int dom_html_collection_named_item_into_zval(rv, index.str, object); } else { ZEND_ASSERT(index.type == DOM_NODELIST_DIM_LONG); - php_dom_nodelist_get_item_into_zval(php_dom_obj_from_obj(object)->ptr, index.lval, rv); + php_dom_obj_map_get_item_into_zval(php_dom_obj_from_obj(object)->ptr, index.lval, rv); } return rv; diff --git a/ext/dom/namednodemap.c b/ext/dom/namednodemap.c index 2f8f6d10132c2..953731ad84487 100644 --- a/ext/dom/namednodemap.c +++ b/ext/dom/namednodemap.c @@ -22,6 +22,7 @@ #include "php.h" #if defined(HAVE_LIBXML) && defined(HAVE_DOM) #include "php_dom.h" +#include "obj_map.h" #include "zend_interfaces.h" /* @@ -54,16 +55,6 @@ zend_result dom_namednodemap_length_read(dom_object *obj, zval *retval) /* }}} */ -void php_dom_named_node_map_get_named_item_into_zval(dom_nnodemap_object *objmap, const zend_string *named, const char *ns, zval *return_value) -{ - xmlNodePtr itemnode = objmap->handler->get_named_item(objmap, named, ns); - if (itemnode) { - DOM_RET_OBJ(itemnode, objmap->baseobj); - } else { - RETURN_NULL(); - } -} - /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-1074577549 Since: */ @@ -76,7 +67,7 @@ PHP_METHOD(DOMNamedNodeMap, getNamedItem) } dom_nnodemap_object *objmap = Z_DOMOBJ_P(ZEND_THIS)->ptr; - php_dom_named_node_map_get_named_item_into_zval(objmap, named, NULL, return_value); + php_dom_obj_map_get_named_item_into_zval(objmap, named, NULL, return_value); } /* }}} end dom_namednodemap_get_named_item */ @@ -121,7 +112,7 @@ PHP_METHOD(DOMNamedNodeMap, getNamedItemNS) objmap = (dom_nnodemap_object *)intern->ptr; if (objmap != NULL) { - php_dom_named_node_map_get_named_item_into_zval(objmap, named, uri, return_value); + php_dom_obj_map_get_named_item_into_zval(objmap, named, uri, return_value); } } /* }}} end dom_namednodemap_get_named_item_ns */ diff --git a/ext/dom/node.c b/ext/dom/node.c index 99069d778ebfe..dba4bb8db87cd 100644 --- a/ext/dom/node.c +++ b/ext/dom/node.c @@ -22,6 +22,7 @@ #include "php.h" #if defined(HAVE_LIBXML) && defined(HAVE_DOM) #include "php_dom.h" +#include "obj_map.h" #include "namespace_compat.h" #include "private_data.h" #include "internal_helpers.h" @@ -288,7 +289,7 @@ zend_result dom_node_child_nodes_read(dom_object *obj, zval *retval) object_init_ex(retval, dom_get_nodelist_ce(php_dom_follow_spec_intern(obj))); dom_object *intern = Z_DOMOBJ_P(retval); - dom_namednode_iter(obj, intern, NULL, NULL, NULL, &php_dom_obj_map_child_nodes); + php_dom_create_obj_map(obj, intern, NULL, NULL, NULL, &php_dom_obj_map_child_nodes); return SUCCESS; } @@ -422,7 +423,7 @@ zend_result dom_node_attributes_read(dom_object *obj, zval *retval) if (nodep->type == XML_ELEMENT_NODE) { object_init_ex(retval, dom_get_namednodemap_ce(php_dom_follow_spec_intern(obj))); dom_object *intern = Z_DOMOBJ_P(retval); - dom_namednode_iter(obj, intern, NULL, NULL, NULL, &php_dom_obj_map_attributes); + php_dom_create_obj_map(obj, intern, NULL, NULL, NULL, &php_dom_obj_map_attributes); } else { ZVAL_NULL(retval); } diff --git a/ext/dom/nodelist.c b/ext/dom/nodelist.c index 46a10a362a071..5e3de728e4fae 100644 --- a/ext/dom/nodelist.c +++ b/ext/dom/nodelist.c @@ -22,6 +22,7 @@ #include "php.h" #if defined(HAVE_LIBXML) && defined(HAVE_DOM) #include "php_dom.h" +#include "obj_map.h" #include "nodelist.h" #include "zend_interfaces.h" @@ -63,15 +64,6 @@ PHP_METHOD(DOMNodeList, count) } /* }}} end dom_nodelist_count */ -void php_dom_nodelist_get_item_into_zval(dom_nnodemap_object *objmap, zend_long index, zval *return_value) -{ - if (EXPECTED(objmap)) { - objmap->handler->get_item(objmap, index, return_value); - } else { - RETURN_NULL(); - } -} - /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#ID-844377136 Since: */ @@ -85,7 +77,7 @@ PHP_METHOD(DOMNodeList, item) zval *id = ZEND_THIS; dom_object *intern = Z_DOMOBJ_P(id); dom_nnodemap_object *objmap = intern->ptr; - php_dom_nodelist_get_item_into_zval(objmap, index, return_value); + php_dom_obj_map_get_item_into_zval(objmap, index, return_value); } /* }}} end dom_nodelist_item */ @@ -136,7 +128,7 @@ zval *dom_modern_nodelist_read_dimension(zend_object *object, zval *offset, int return NULL; } - php_dom_nodelist_get_item_into_zval(php_dom_obj_from_obj(object)->ptr, index.lval, rv); + php_dom_obj_map_get_item_into_zval(php_dom_obj_from_obj(object)->ptr, index.lval, rv); return rv; } diff --git a/ext/dom/nodelist.h b/ext/dom/nodelist.h index 5ac3de1e46c44..5c5653eea6dce 100644 --- a/ext/dom/nodelist.h +++ b/ext/dom/nodelist.h @@ -31,8 +31,6 @@ typedef struct dom_nodelist_dimension_index { enum dom_nodelist_dimension_index_type type; } dom_nodelist_dimension_index; -void php_dom_nodelist_get_item_into_zval(dom_nnodemap_object *objmap, zend_long index, zval *return_value); -zend_long php_dom_get_nodelist_length(dom_object *obj); dom_nodelist_dimension_index dom_modern_nodelist_get_index(const zval *offset); zval *dom_modern_nodelist_read_dimension(zend_object *object, zval *offset, int type, zval *rv); int dom_modern_nodelist_has_dimension(zend_object *object, zval *member, int check_empty); diff --git a/ext/dom/obj_map.c b/ext/dom/obj_map.c index f2a93fe3ef870..c92a5834f3c9e 100644 --- a/ext/dom/obj_map.c +++ b/ext/dom/obj_map.c @@ -288,6 +288,67 @@ zend_long php_dom_get_nodelist_length(dom_object *obj) return count; } +void php_dom_create_obj_map(dom_object *basenode, dom_object *intern, xmlHashTablePtr ht, zend_string *local, zend_string *ns, const php_dom_obj_map_handler *handler) +{ + dom_nnodemap_object *mapptr = intern->ptr; + + ZEND_ASSERT(basenode != NULL); + + ZVAL_OBJ_COPY(&mapptr->baseobj_zv, &basenode->std); + + xmlDocPtr doc = basenode->document ? basenode->document->ptr : NULL; + + mapptr->handler = handler; + mapptr->baseobj = basenode; + mapptr->ht = ht; + if (EXPECTED(doc != NULL)) { + mapptr->dict = doc->dict; + xmlDictReference(doc->dict); + } + + const xmlChar* tmp; + + if (local) { + int len = (int) ZSTR_LEN(local); + if (doc != NULL && (tmp = xmlDictExists(doc->dict, (const xmlChar *)ZSTR_VAL(local), len)) != NULL) { + mapptr->local = BAD_CAST tmp; + } else { + mapptr->local = BAD_CAST ZSTR_VAL(zend_string_copy(local)); + mapptr->release_local = true; + } + mapptr->local_lower = zend_string_tolower(local); + } + + if (ns) { + int len = (int) ZSTR_LEN(ns); + if (doc != NULL && (tmp = xmlDictExists(doc->dict, (const xmlChar *)ZSTR_VAL(ns), len)) != NULL) { + mapptr->ns = BAD_CAST tmp; + } else { + mapptr->ns = BAD_CAST ZSTR_VAL(zend_string_copy(ns)); + mapptr->release_ns = true; + } + } +} + +void php_dom_obj_map_get_item_into_zval(dom_nnodemap_object *objmap, zend_long index, zval *return_value) +{ + if (EXPECTED(objmap)) { + objmap->handler->get_item(objmap, index, return_value); + } else { + RETURN_NULL(); + } +} + +void php_dom_obj_map_get_named_item_into_zval(dom_nnodemap_object *objmap, const zend_string *named, const char *ns, zval *return_value) +{ + xmlNodePtr itemnode = objmap->handler->get_named_item(objmap, named, ns); + if (itemnode) { + DOM_RET_OBJ(itemnode, objmap->baseobj); + } else { + RETURN_NULL(); + } +} + /********************** * === Named item === * **********************/ diff --git a/ext/dom/obj_map.h b/ext/dom/obj_map.h index 511b71fcc7485..70b6fdd107b49 100644 --- a/ext/dom/obj_map.h +++ b/ext/dom/obj_map.h @@ -20,14 +20,36 @@ typedef struct dom_nnodemap_object dom_nnodemap_object; typedef struct php_dom_obj_map_handler { - zend_long (*length)(dom_nnodemap_object *); - void (*get_item)(dom_nnodemap_object *, zend_long, zval *); - xmlNodePtr (*get_named_item)(dom_nnodemap_object *, const zend_string *, const char *); - bool (*has_named_item)(dom_nnodemap_object *, const zend_string *, const char *); - bool use_cache; - bool nameless; + zend_long (*length)(dom_nnodemap_object *); + void (*get_item)(dom_nnodemap_object *, zend_long, zval *); + xmlNodePtr (*get_named_item)(dom_nnodemap_object *, const zend_string *, const char *); + bool (*has_named_item)(dom_nnodemap_object *, const zend_string *, const char *); + bool use_cache; + bool nameless; } php_dom_obj_map_handler; +typedef struct dom_nnodemap_object { + dom_object *baseobj; + zval baseobj_zv; + int cached_length; + xmlHashTable *ht; + xmlChar *local; + zend_string *local_lower; + xmlChar *ns; + php_libxml_cache_tag cache_tag; + dom_object *cached_obj; + zend_long cached_obj_index; + xmlDictPtr dict; + const php_dom_obj_map_handler *handler; + bool release_local : 1; + bool release_ns : 1; +} dom_nnodemap_object; + +void php_dom_create_obj_map(dom_object *basenode, dom_object *intern, xmlHashTablePtr ht, zend_string *local, zend_string *ns, const php_dom_obj_map_handler *handler); +void php_dom_obj_map_get_named_item_into_zval(dom_nnodemap_object *objmap, const zend_string *named, const char *ns, zval *return_value); +void php_dom_obj_map_get_item_into_zval(dom_nnodemap_object *objmap, zend_long index, zval *return_value); +zend_long php_dom_get_nodelist_length(dom_object *obj); + extern const php_dom_obj_map_handler php_dom_obj_map_attributes; extern const php_dom_obj_map_handler php_dom_obj_map_by_tag_name; extern const php_dom_obj_map_handler php_dom_obj_map_child_nodes; diff --git a/ext/dom/parentnode/css_selectors.c b/ext/dom/parentnode/css_selectors.c index 10b517a248d46..b9d2edee723bc 100644 --- a/ext/dom/parentnode/css_selectors.c +++ b/ext/dom/parentnode/css_selectors.c @@ -21,6 +21,7 @@ #include "php.h" #if defined(HAVE_LIBXML) && defined(HAVE_DOM) #include "../php_dom.h" +#include "../obj_map.h" #include "ext/lexbor/lexbor/css/parser.h" #include "../lexbor/selectors-adapted/selectors.h" diff --git a/ext/dom/php_dom.c b/ext/dom/php_dom.c index d9ed01d2e7d6e..e71e4e259c304 100644 --- a/ext/dom/php_dom.c +++ b/ext/dom/php_dom.c @@ -24,6 +24,7 @@ #if defined(HAVE_LIBXML) && defined(HAVE_DOM) #include "zend_enum.h" #include "php_dom.h" +#include "obj_map.h" #include "nodelist.h" #include "html_collection.h" #include "namespace_compat.h" @@ -1456,50 +1457,6 @@ void dom_objects_free_storage(zend_object *object) } /* }}} */ -/* TODO: move me to obj_map.c */ -void dom_namednode_iter(dom_object *basenode, dom_object *intern, xmlHashTablePtr ht, zend_string *local, zend_string *ns, const php_dom_obj_map_handler *handler) /* {{{ */ -{ - dom_nnodemap_object *mapptr = (dom_nnodemap_object *) intern->ptr; - - ZEND_ASSERT(basenode != NULL); - - ZVAL_OBJ_COPY(&mapptr->baseobj_zv, &basenode->std); - - xmlDocPtr doc = basenode->document ? basenode->document->ptr : NULL; - - mapptr->handler = handler; - mapptr->baseobj = basenode; - mapptr->ht = ht; - if (EXPECTED(doc != NULL)) { - mapptr->dict = doc->dict; - xmlDictReference(doc->dict); - } - - const xmlChar* tmp; - - if (local) { - int len = (int) ZSTR_LEN(local); - if (doc != NULL && (tmp = xmlDictExists(doc->dict, (const xmlChar *)ZSTR_VAL(local), len)) != NULL) { - mapptr->local = BAD_CAST tmp; - } else { - mapptr->local = BAD_CAST ZSTR_VAL(zend_string_copy(local)); - mapptr->release_local = true; - } - mapptr->local_lower = zend_string_tolower(local); - } - - if (ns) { - int len = (int) ZSTR_LEN(ns); - if (doc != NULL && (tmp = xmlDictExists(doc->dict, (const xmlChar *)ZSTR_VAL(ns), len)) != NULL) { - mapptr->ns = BAD_CAST tmp; - } else { - mapptr->ns = BAD_CAST ZSTR_VAL(zend_string_copy(ns)); - mapptr->release_ns = true; - } - } -} -/* }}} */ - static void dom_objects_set_class_ex(zend_class_entry *class_type, dom_object *intern) { zend_class_entry *base_class = class_type; @@ -2294,7 +2251,7 @@ static zval *dom_nodelist_read_dimension(zend_object *object, zval *offset, int return rv; } - php_dom_nodelist_get_item_into_zval(php_dom_obj_from_obj(object)->ptr, lval, rv); + php_dom_obj_map_get_item_into_zval(php_dom_obj_from_obj(object)->ptr, lval, rv); return rv; } @@ -2382,7 +2339,7 @@ static zval *dom_nodemap_read_dimension(zend_object *object, zval *offset, int t zend_long lval; if (dom_nodemap_or_nodelist_process_offset_as_named(offset, &lval)) { /* exceptional case, switch to named lookup */ - php_dom_named_node_map_get_named_item_into_zval(php_dom_obj_from_obj(object)->ptr, Z_STR_P(offset), NULL, rv); + php_dom_obj_map_get_named_item_into_zval(php_dom_obj_from_obj(object)->ptr, Z_STR_P(offset), NULL, rv); return rv; } @@ -2429,7 +2386,7 @@ static zval *dom_modern_nodemap_read_dimension(zend_object *object, zval *offset if (ZEND_HANDLE_NUMERIC(Z_STR_P(offset), lval)) { map->handler->get_item(map, (zend_long) lval, rv); } else { - php_dom_named_node_map_get_named_item_into_zval(map, Z_STR_P(offset), NULL, rv); + php_dom_obj_map_get_named_item_into_zval(map, Z_STR_P(offset), NULL, rv); } } else if (Z_TYPE_P(offset) == IS_LONG) { map->handler->get_item(map, Z_LVAL_P(offset), rv); diff --git a/ext/dom/php_dom.h b/ext/dom/php_dom.h index 571f99cfe2be3..0ff8692c4cc74 100644 --- a/ext/dom/php_dom.h +++ b/ext/dom/php_dom.h @@ -56,7 +56,7 @@ extern zend_module_entry dom_module_entry; #include "xpath_callbacks.h" #include "zend_exceptions.h" #include "dom_ce.h" -#include "obj_map.h" + /* DOM API_VERSION, please bump it up, if you change anything in the API therefore it's easier for the script-programmers to check, what's working how Can be checked with phpversion("dom"); @@ -76,23 +76,6 @@ static inline dom_xpath_object *php_xpath_obj_from_obj(zend_object *obj) { #define Z_XPATHOBJ_P(zv) php_xpath_obj_from_obj(Z_OBJ_P((zv))) -typedef struct dom_nnodemap_object { - dom_object *baseobj; - zval baseobj_zv; - int cached_length; - xmlHashTable *ht; - xmlChar *local; - zend_string *local_lower; - xmlChar *ns; - php_libxml_cache_tag cache_tag; - dom_object *cached_obj; - zend_long cached_obj_index; - xmlDictPtr dict; - const php_dom_obj_map_handler *handler; - bool release_local : 1; - bool release_ns : 1; -} dom_nnodemap_object; - typedef struct { zend_object_iterator intern; zval curobj; @@ -145,7 +128,6 @@ int dom_hierarchy(xmlNodePtr parent, xmlNodePtr child); bool dom_has_feature(zend_string *feature, zend_string *version); bool dom_node_is_read_only(const xmlNode *node); bool dom_node_children_valid(const xmlNode *node); -void dom_namednode_iter(dom_object *basenode, dom_object *intern, xmlHashTablePtr ht, zend_string *local, zend_string *ns, const php_dom_obj_map_handler *handler); xmlNodePtr create_notation(const xmlChar *name, const xmlChar *ExternalID, const xmlChar *SystemID); xmlNode *php_dom_libxml_hash_iter(xmlHashTable *ht, int index); zend_object_iterator *php_dom_get_iterator(zend_class_entry *ce, zval *object, int by_ref); @@ -210,8 +192,6 @@ void dom_element_closest(xmlNodePtr thisp, dom_object *intern, zval *return_valu xmlNodePtr dom_parse_fragment(dom_object *obj, xmlNodePtr context_node, const zend_string *input); /* nodemap and nodelist APIs */ -void php_dom_named_node_map_get_named_item_into_zval(dom_nnodemap_object *objmap, const zend_string *named, const char *ns, zval *return_value); -xmlNodePtr php_dom_named_node_map_get_item(dom_nnodemap_object *objmap, zend_long index); zend_long php_dom_get_namednodemap_length(dom_object *obj); xmlNodePtr dom_nodelist_iter_start_first_child(xmlNodePtr nodep); diff --git a/ext/dom/xpath.c b/ext/dom/xpath.c index b410f7b264997..ebf61f10e80bf 100644 --- a/ext/dom/xpath.c +++ b/ext/dom/xpath.c @@ -22,8 +22,8 @@ #include "php.h" #if defined(HAVE_LIBXML) && defined(HAVE_DOM) #include "php_dom.h" +#include "obj_map.h" #include "namespace_compat.h" -#include "private_data.h" #include "internal_helpers.h" #define PHP_DOM_XPATH_QUERY 0 From 26aea0ed3780382490d51c7ab5859af4ae55bdd9 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sat, 21 Jun 2025 22:34:23 +0200 Subject: [PATCH 119/682] Tweak sizes of some dom_nnodemap_object fields We don't have to pack the bools, and the cached index would better be a zend_long so we don't get implicit narrowing. --- ext/dom/obj_map.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ext/dom/obj_map.h b/ext/dom/obj_map.h index 70b6fdd107b49..b1649999fbf16 100644 --- a/ext/dom/obj_map.h +++ b/ext/dom/obj_map.h @@ -31,7 +31,7 @@ typedef struct php_dom_obj_map_handler { typedef struct dom_nnodemap_object { dom_object *baseobj; zval baseobj_zv; - int cached_length; + zend_long cached_length; xmlHashTable *ht; xmlChar *local; zend_string *local_lower; @@ -41,8 +41,8 @@ typedef struct dom_nnodemap_object { zend_long cached_obj_index; xmlDictPtr dict; const php_dom_obj_map_handler *handler; - bool release_local : 1; - bool release_ns : 1; + bool release_local; + bool release_ns; } dom_nnodemap_object; void php_dom_create_obj_map(dom_object *basenode, dom_object *intern, xmlHashTablePtr ht, zend_string *local, zend_string *ns, const php_dom_obj_map_handler *handler); From 873634278200d6b617bc135e224f36a8379ba410 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sat, 21 Jun 2025 22:38:01 +0200 Subject: [PATCH 120/682] Pack dom_nnodemap_object fields together with a union that can't be active at the same time --- ext/dom/obj_map.h | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/ext/dom/obj_map.h b/ext/dom/obj_map.h index b1649999fbf16..4935799564286 100644 --- a/ext/dom/obj_map.h +++ b/ext/dom/obj_map.h @@ -32,10 +32,14 @@ typedef struct dom_nnodemap_object { dom_object *baseobj; zval baseobj_zv; zend_long cached_length; - xmlHashTable *ht; - xmlChar *local; - zend_string *local_lower; - xmlChar *ns; + union { + xmlHashTable *ht; + struct { + xmlChar *local; + zend_string *local_lower; + xmlChar *ns; + }; + }; php_libxml_cache_tag cache_tag; dom_object *cached_obj; zend_long cached_obj_index; From 479e9be45d7ca51e7cb09663c9e36d45fcd10960 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sat, 21 Jun 2025 23:13:04 +0200 Subject: [PATCH 121/682] Store hash table entry directly in dom_nnodemap_object Splits the purpose of the baseobj_zv: now no longer either is an array or an object. Stores the hash table pointer directly, if used. --- ext/dom/obj_map.c | 8 +++----- ext/dom/obj_map.h | 3 ++- ext/dom/parentnode/css_selectors.c | 3 ++- ext/dom/php_dom.c | 7 +++++-- ext/dom/xpath.c | 18 ++++++++---------- 5 files changed, 20 insertions(+), 19 deletions(-) diff --git a/ext/dom/obj_map.c b/ext/dom/obj_map.c index c92a5834f3c9e..12407a560112e 100644 --- a/ext/dom/obj_map.c +++ b/ext/dom/obj_map.c @@ -55,8 +55,7 @@ static zend_long dom_map_get_xmlht_length(dom_nnodemap_object *map) static zend_long dom_map_get_nodeset_length(dom_nnodemap_object *map) { - HashTable *nodeht = Z_ARRVAL(map->baseobj_zv); - return zend_hash_num_elements(nodeht); + return zend_hash_num_elements(map->array); } static zend_long dom_map_get_prop_length(dom_nnodemap_object *map) @@ -132,8 +131,7 @@ static void dom_map_get_notation_item(dom_nnodemap_object *map, zend_long index, static void dom_map_get_nodeset_item(dom_nnodemap_object *map, zend_long index, zval *return_value) { - HashTable *nodeht = Z_ARRVAL(map->baseobj_zv); - zval *entry = zend_hash_index_find(nodeht, index); + zval *entry = zend_hash_index_find(map->array, index); if (entry) { RETURN_COPY(entry); } else { @@ -294,7 +292,7 @@ void php_dom_create_obj_map(dom_object *basenode, dom_object *intern, xmlHashTab ZEND_ASSERT(basenode != NULL); - ZVAL_OBJ_COPY(&mapptr->baseobj_zv, &basenode->std); + GC_ADDREF(&basenode->std); xmlDocPtr doc = basenode->document ? basenode->document->ptr : NULL; diff --git a/ext/dom/obj_map.h b/ext/dom/obj_map.h index 4935799564286..e1de9addd98f2 100644 --- a/ext/dom/obj_map.h +++ b/ext/dom/obj_map.h @@ -30,10 +30,10 @@ typedef struct php_dom_obj_map_handler { typedef struct dom_nnodemap_object { dom_object *baseobj; - zval baseobj_zv; zend_long cached_length; union { xmlHashTable *ht; + HashTable *array; struct { xmlChar *local; zend_string *local_lower; @@ -47,6 +47,7 @@ typedef struct dom_nnodemap_object { const php_dom_obj_map_handler *handler; bool release_local; bool release_ns; + bool release_array; } dom_nnodemap_object; void php_dom_create_obj_map(dom_object *basenode, dom_object *intern, xmlHashTablePtr ht, zend_string *local, zend_string *ns, const php_dom_obj_map_handler *handler); diff --git a/ext/dom/parentnode/css_selectors.c b/ext/dom/parentnode/css_selectors.c index b9d2edee723bc..4f77359835ce5 100644 --- a/ext/dom/parentnode/css_selectors.c +++ b/ext/dom/parentnode/css_selectors.c @@ -249,7 +249,8 @@ void dom_parent_node_query_selector_all(xmlNodePtr thisp, dom_object *intern, zv object_init_ex(return_value, dom_modern_nodelist_class_entry); dom_object *ret_obj = Z_DOMOBJ_P(return_value); dom_nnodemap_object *mapptr = (dom_nnodemap_object *) ret_obj->ptr; - ZVAL_ARR(&mapptr->baseobj_zv, list); + mapptr->array = list; + mapptr->release_array = true; mapptr->handler = &php_dom_obj_map_nodeset; } } diff --git a/ext/dom/php_dom.c b/ext/dom/php_dom.c index e71e4e259c304..6e85ea887e4ec 100644 --- a/ext/dom/php_dom.c +++ b/ext/dom/php_dom.c @@ -1545,8 +1545,11 @@ void dom_nnodemap_objects_free_storage(zend_object *object) /* {{{ */ if (objmap->local_lower) { zend_string_release(objmap->local_lower); } - if (!Z_ISUNDEF(objmap->baseobj_zv)) { - zval_ptr_dtor(&objmap->baseobj_zv); + if (objmap->release_array) { + zend_array_release(objmap->array); + } + if (objmap->baseobj) { + OBJ_RELEASE(&objmap->baseobj->std); } xmlDictFree(objmap->dict); efree(objmap); diff --git a/ext/dom/xpath.c b/ext/dom/xpath.c index ebf61f10e80bf..21baa59ffed0b 100644 --- a/ext/dom/xpath.c +++ b/ext/dom/xpath.c @@ -234,15 +234,6 @@ PHP_METHOD(DOMXPath, registerNamespace) } /* }}} */ -static void dom_xpath_iter(zval *baseobj, dom_object *intern) /* {{{ */ -{ - dom_nnodemap_object *mapptr = (dom_nnodemap_object *) intern->ptr; - - ZVAL_COPY_VALUE(&mapptr->baseobj_zv, baseobj); - mapptr->handler = &php_dom_obj_map_nodeset; -} -/* }}} */ - static void php_xpath_eval(INTERNAL_FUNCTION_PARAMETERS, int type, bool modern) /* {{{ */ { zval *context = NULL; @@ -335,6 +326,7 @@ static void php_xpath_eval(INTERNAL_FUNCTION_PARAMETERS, int type, bool modern) { xmlNodeSetPtr nodesetp; zval retval; + bool release_array = false; if (xpathobjp->type == XPATH_NODESET && NULL != (nodesetp = xpathobjp->nodesetval) && nodesetp->nodeNr) { array_init_size(&retval, nodesetp->nodeNr); @@ -369,12 +361,18 @@ static void php_xpath_eval(INTERNAL_FUNCTION_PARAMETERS, int type, bool modern) } add_next_index_zval(&retval, &child); } + release_array = true; } else { ZVAL_EMPTY_ARRAY(&retval); } + object_init_ex(return_value, dom_get_nodelist_ce(modern)); nodeobj = Z_DOMOBJ_P(return_value); - dom_xpath_iter(&retval, nodeobj); + dom_nnodemap_object *mapptr = nodeobj->ptr; + + mapptr->array = Z_ARR(retval); + mapptr->release_array = release_array; + mapptr->handler = &php_dom_obj_map_nodeset; break; } From 9b6df109c7fbca47ef1f1744d94e31f0d4acfd02 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sun, 22 Jun 2025 12:31:06 +0200 Subject: [PATCH 122/682] Don't use the obj_map cache for attributes (#18895) --- ext/dom/obj_map.c | 28 ++++++++----------- .../common/attr_named_node_map_cache.phpt | 17 +++++++++++ 2 files changed, 29 insertions(+), 16 deletions(-) create mode 100644 ext/dom/tests/modern/common/attr_named_node_map_cache.phpt diff --git a/ext/dom/obj_map.c b/ext/dom/obj_map.c index 12407a560112e..fe71a6f47a38a 100644 --- a/ext/dom/obj_map.c +++ b/ext/dom/obj_map.c @@ -196,19 +196,25 @@ static void dom_map_cache_obj(dom_nnodemap_object *map, xmlNodePtr itemnode, zen map->cached_obj = cached_obj; } -static xmlNodePtr dom_map_get_attr_start(xmlNodePtr node) +static void dom_map_get_attributes_item(dom_nnodemap_object *map, zend_long index, zval *return_value) { - ZEND_ASSERT(node->type == XML_ELEMENT_NODE); - return (xmlNodePtr) node->properties; + xmlNodePtr nodep = dom_object_get_node(map->baseobj); + xmlNodePtr itemnode = NULL; + if (nodep && index >= 0) { + ZEND_ASSERT(nodep->type == XML_ELEMENT_NODE); + itemnode = (xmlNodePtr) nodep->properties; + for (; index > 0 && itemnode; itemnode = itemnode->next, index--); + } + dom_ret_node_to_zobj(map, itemnode, return_value); } -static void dom_map_get_chain_item(dom_nnodemap_object *map, zend_long index, zval *return_value, xmlNodePtr (*get_start)(xmlNodePtr)) +static void dom_map_get_nodes_item(dom_nnodemap_object *map, zend_long index, zval *return_value) { xmlNodePtr nodep = dom_object_get_node(map->baseobj); xmlNodePtr itemnode = NULL; if (nodep && index >= 0) { dom_node_idx_pair start_point = dom_obj_map_get_start_point(map, nodep, index); - itemnode = start_point.node ? start_point.node : get_start(nodep); + itemnode = start_point.node ? start_point.node : dom_nodelist_iter_start_first_child(nodep); for (; start_point.index > 0 && itemnode; itemnode = itemnode->next, start_point.index--); } dom_ret_node_to_zobj(map, itemnode, return_value); @@ -217,16 +223,6 @@ static void dom_map_get_chain_item(dom_nnodemap_object *map, zend_long index, zv } } -static void dom_map_get_attributes_item(dom_nnodemap_object *map, zend_long index, zval *return_value) -{ - dom_map_get_chain_item(map, index, return_value, dom_map_get_attr_start); -} - -static void dom_map_get_nodes_item(dom_nnodemap_object *map, zend_long index, zval *return_value) -{ - dom_map_get_chain_item(map, index, return_value, dom_nodelist_iter_start_first_child); -} - static void dom_map_get_by_tag_name_item(dom_nnodemap_object *map, zend_long index, zval *return_value) { xmlNodePtr nodep = dom_object_get_node(map->baseobj); @@ -411,7 +407,7 @@ const php_dom_obj_map_handler php_dom_obj_map_attributes = { .get_item = dom_map_get_attributes_item, .get_named_item = dom_map_get_named_item_prop, .has_named_item = dom_map_has_named_item_prop, - .use_cache = true, + .use_cache = false, .nameless = false, }; diff --git a/ext/dom/tests/modern/common/attr_named_node_map_cache.phpt b/ext/dom/tests/modern/common/attr_named_node_map_cache.phpt new file mode 100644 index 0000000000000..18dedaddb9a9e --- /dev/null +++ b/ext/dom/tests/modern/common/attr_named_node_map_cache.phpt @@ -0,0 +1,17 @@ +--TEST-- +Attribute named node map cache +--EXTENSIONS-- +dom +--FILE-- +'); +$attrs = $dom->documentElement->attributes; +var_dump($attrs[1]->nodeName); +$dom->documentElement->removeAttribute('b'); +var_dump($attrs[1]->nodeName); + +?> +--EXPECT-- +string(1) "b" +string(1) "c" From 2694eb9df04beaab1bc052a4da53d9adc6c29f0a Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sun, 22 Jun 2025 08:00:08 +0100 Subject: [PATCH 123/682] Fixed GH-18902: ldap_exop/ldap_exop_sync assert triggered on empty request OID close GH-18903 --- NEWS | 4 ++++ ext/ldap/ldap.c | 7 ++++++- ext/ldap/tests/gh18902.phpt | 30 ++++++++++++++++++++++++++++++ 3 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 ext/ldap/tests/gh18902.phpt diff --git a/NEWS b/NEWS index 9881c36d4b012..25706b1efc0a0 100644 --- a/NEWS +++ b/NEWS @@ -10,6 +10,10 @@ PHP NEWS . Fix memory leaks when returning refcounted value from curl callback. (nielsdos) +- LDAP: + . Fixed GH-18902 ldap_exop/ldap_exop_sync assert triggered on empty + request OID. (David Carlier) + - Streams: . Fixed GH-13264 (fgets() and stream_get_line() do not return false on filter fatal error). (Jakub Zelenka) diff --git a/ext/ldap/ldap.c b/ext/ldap/ldap.c index fecb8846400a6..769e6caa277b4 100644 --- a/ext/ldap/ldap.c +++ b/ext/ldap/ldap.c @@ -4036,7 +4036,12 @@ static void php_ldap_exop(INTERNAL_FUNCTION_PARAMETERS, bool force_sync) { LDAPControl **lserverctrls = NULL; int rc, msgid; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "OS|S!a!zz", &link, ldap_link_ce, &reqoid, &reqdata, &serverctrls, &retdata, &retoid) != SUCCESS) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "OP|S!a!zz", &link, ldap_link_ce, &reqoid, &reqdata, &serverctrls, &retdata, &retoid) != SUCCESS) { + RETURN_THROWS(); + } + + if (ZSTR_LEN(reqoid) == 0) { + zend_argument_value_error(2, "must not be empty"); RETURN_THROWS(); } diff --git a/ext/ldap/tests/gh18902.phpt b/ext/ldap/tests/gh18902.phpt new file mode 100644 index 0000000000000..329cbb59c1b11 --- /dev/null +++ b/ext/ldap/tests/gh18902.phpt @@ -0,0 +1,30 @@ +--TEST-- +GH-17704 (ldap_search fails when $attributes contains a non-packed array with numerical keys) +--EXTENSIONS-- +ldap +--FILE-- +getMessage(), PHP_EOL; +} + +try { + ldap_exop_sync($conn,""); +} catch (\ValueError $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + ldap_exop_sync($conn,"test\0"); +} catch (\ValueError $e) { + echo $e->getMessage(), PHP_EOL; +} +?> +--EXPECTF-- +ldap_exop(): Argument #2 ($request_oid) must not contain any null bytes +ldap_exop_sync(): Argument #2 ($request_oid) must not be empty +ldap_exop_sync(): Argument #2 ($request_oid) must not contain any null bytes From a5f21ca7005f3825c47d52772ee2f66c6a81cf86 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sun, 22 Jun 2025 10:13:06 +0200 Subject: [PATCH 124/682] Fix GH-18901: integer overflow mb_split We prevent signed overflow by making the count unsigned. The actual interpretation of the count doesn't matter as it's just used to denote a limit. The test output for some limit values looks strange though, so that may need extra investigation. However, that's orthogonal to this fix. Closes GH-18906. --- NEWS | 3 ++ ext/mbstring/php_mbregex.c | 2 +- ext/mbstring/tests/gh18901.phpt | 54 +++++++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 ext/mbstring/tests/gh18901.phpt diff --git a/NEWS b/NEWS index 25706b1efc0a0..ea77125b205eb 100644 --- a/NEWS +++ b/NEWS @@ -14,6 +14,9 @@ PHP NEWS . Fixed GH-18902 ldap_exop/ldap_exop_sync assert triggered on empty request OID. (David Carlier) +- MbString: + . Fixed bug GH-18901 (integer overflow mb_split). (nielsdos) + - Streams: . Fixed GH-13264 (fgets() and stream_get_line() do not return false on filter fatal error). (Jakub Zelenka) diff --git a/ext/mbstring/php_mbregex.c b/ext/mbstring/php_mbregex.c index 99dc91e34dcae..86bc5f61d8543 100644 --- a/ext/mbstring/php_mbregex.c +++ b/ext/mbstring/php_mbregex.c @@ -1184,7 +1184,7 @@ PHP_FUNCTION(mb_split) size_t string_len; int err; - zend_long count = -1; + zend_ulong count = -1; /* unsigned, it's a limit and we want to prevent signed overflow */ if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss|l", &arg_pattern, &arg_pattern_len, &string, &string_len, &count) == FAILURE) { RETURN_THROWS(); diff --git a/ext/mbstring/tests/gh18901.phpt b/ext/mbstring/tests/gh18901.phpt new file mode 100644 index 0000000000000..8d862a537c3b1 --- /dev/null +++ b/ext/mbstring/tests/gh18901.phpt @@ -0,0 +1,54 @@ +--TEST-- +GH-18901 (integer overflow mb_split) +--EXTENSIONS-- +mbstring +--SKIPIF-- + +--FILE-- + +--EXPECT-- +array(4) { + [0]=> + string(0) "" + [1]=> + string(0) "" + [2]=> + string(0) "" + [3]=> + string(0) "" +} +array(4) { + [0]=> + string(0) "" + [1]=> + string(0) "" + [2]=> + string(0) "" + [3]=> + string(0) "" +} +array(4) { + [0]=> + string(0) "" + [1]=> + string(0) "" + [2]=> + string(0) "" + [3]=> + string(0) "" +} +array(1) { + [0]=> + string(3) "123" +} +array(1) { + [0]=> + string(3) "123" +} From 3df665a8889d76216c80a6e21f1201ddc8eab1d9 Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Sun, 22 Jun 2025 15:19:08 +0200 Subject: [PATCH 125/682] [Windows build] Remove redundant flags definitions (#18890) The /d2FuncCache1 compile option is already added by toolset_setup_common_cflags() in confutils.js to all targets. ZEND_DVAL_TO_LVAL_CAST_OK was removed in 3725717de18fd60a679a02210b3ed14517972524. --- win32/build/config.w32 | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/win32/build/config.w32 b/win32/build/config.w32 index f509e4eae9337..403f0aa6efbfe 100644 --- a/win32/build/config.w32 +++ b/win32/build/config.w32 @@ -285,12 +285,6 @@ if (TARGET_ARCH == 'x64') { } ADD_FLAG("CFLAGS_BD_ZEND", "/D ZEND_ENABLE_STATIC_TSRMLS_CACHE=1"); -if (VS_TOOLSET && VCVERS >= 1914) { - ADD_FLAG("CFLAGS_BD_ZEND", "/d2FuncCache1"); -} - -/* XXX inspect this for other toolsets */ -//AC_DEFINE('ZEND_DVAL_TO_LVAL_CAST_OK', 1); ADD_SOURCES("main", "main.c snprintf.c spprintf.c getopt.c fopen_wrappers.c \ php_ini_builder.c php_glob.c \ @@ -299,9 +293,6 @@ ADD_SOURCES("main", "main.c snprintf.c spprintf.c getopt.c fopen_wrappers.c \ php_open_temporary_file.c output.c internal_functions.c \ php_syslog.c php_odbc_utils.c safe_bcmp.c"); ADD_FLAG("CFLAGS_BD_MAIN", "/D ZEND_ENABLE_STATIC_TSRMLS_CACHE=1"); -if (VS_TOOLSET && VCVERS >= 1914) { - ADD_FLAG("CFLAGS_BD_MAIN", "/d2FuncCache1"); -} AC_DEFINE('HAVE_STRNLEN', 1); @@ -310,9 +301,6 @@ AC_DEFINE('ZEND_CHECK_STACK_LIMIT', 1) ADD_SOURCES("main/streams", "streams.c cast.c memory.c filter.c plain_wrapper.c \ userspace.c transports.c xp_socket.c mmap.c glob_wrapper.c"); ADD_FLAG("CFLAGS_BD_MAIN_STREAMS", "/D ZEND_ENABLE_STATIC_TSRMLS_CACHE=1"); -if (VS_TOOLSET && VCVERS >= 1914) { - ADD_FLAG("CFLAGS_BD_MAIN_STREAMS", "/d2FuncCache1"); -} ADD_SOURCES("win32", "dllmain.c readdir.c \ registry.c select.c sendmail.c time.c winutil.c wsyslog.c globals.c \ @@ -320,9 +308,6 @@ ADD_SOURCES("win32", "dllmain.c readdir.c \ fnmatch.c sockets.c console.c signal.c"); ADD_FLAG("CFLAGS_BD_WIN32", "/D ZEND_ENABLE_STATIC_TSRMLS_CACHE=1"); -if (VS_TOOLSET && VCVERS >= 1914) { - ADD_FLAG("CFLAGS_BD_WIN32", "/d2FuncCache1"); -} PHP_INSTALL_HEADERS("", "Zend/ TSRM/ main/ main/streams/ win32/"); PHP_INSTALL_HEADERS("Zend/Optimizer", "zend_call_graph.h zend_cfg.h zend_dfg.h zend_dump.h zend_func_info.h zend_inference.h zend_optimizer.h zend_ssa.h zend_worklist.h"); From 4f1b005522ddbd776ea3714851219e6c517aee8f Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Sun, 22 Jun 2025 16:03:55 +0200 Subject: [PATCH 126/682] Autotools: Enable tsrmls cache in hash extension on big endian (#15303) When system is detected as big endian this enables the TSRM Local Storage static cache with the ZEND_ENABLE_STATIC_TSRMLS_CACHE compilation flag. Previously it was enabled only on little endian systems. --- ext/hash/config.m4 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/hash/config.m4 b/ext/hash/config.m4 index 1eef801a2fea8..5248786efb084 100644 --- a/ext/hash/config.m4 +++ b/ext/hash/config.m4 @@ -31,7 +31,7 @@ AS_VAR_IF([ac_cv_c_bigendian_php], [yes], [ SHA3_OPT_SRC="$SHA3_DIR/KeccakP-1600-opt64.c" ]) EXT_HASH_SHA3_SOURCES="$SHA3_OPT_SRC $SHA3_DIR/KeccakHash.c $SHA3_DIR/KeccakSponge.c" - PHP_HASH_CFLAGS="$PHP_HASH_CFLAGS -I@ext_srcdir@/$SHA3_DIR -DKeccakP200_excluded -DKeccakP400_excluded -DKeccakP800_excluded -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1" + PHP_HASH_CFLAGS="$PHP_HASH_CFLAGS -I@ext_srcdir@/$SHA3_DIR -DKeccakP200_excluded -DKeccakP400_excluded -DKeccakP800_excluded" ]) PHP_NEW_EXTENSION([hash], m4_normalize([ @@ -58,7 +58,7 @@ PHP_NEW_EXTENSION([hash], m4_normalize([ murmur/PMurHash128.c ]), [no],, - [$PHP_HASH_CFLAGS]) + [$PHP_HASH_CFLAGS -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1]) PHP_ADD_BUILD_DIR([$ext_builddir/murmur]) AS_VAR_IF([SHA3_DIR],,, [PHP_ADD_BUILD_DIR([$ext_builddir/$SHA3_DIR])]) PHP_INSTALL_HEADERS([ext/hash], m4_normalize([ From 01c3001eb76456c37753032e89c1cbc031904a85 Mon Sep 17 00:00:00 2001 From: David CARLIER Date: Sun, 22 Jun 2025 19:47:36 +0100 Subject: [PATCH 127/682] ext/tidy: zend_parse_parameters_none -> ZEND_PARSE_PARAMETERS_NONE macro (#18913) --- ext/tidy/tidy.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/ext/tidy/tidy.c b/ext/tidy/tidy.c index 18fcc1bfdce68..fe2a0648af683 100644 --- a/ext/tidy/tidy.c +++ b/ext/tidy/tidy.c @@ -67,9 +67,7 @@ #define TIDY_FETCH_ONLY_OBJECT \ PHPTidyObj *obj; \ - if (zend_parse_parameters_none() != SUCCESS) { \ - RETURN_THROWS(); \ - } \ + ZEND_PARSE_PARAMETERS_NONE(); \ obj = Z_TIDY_P(ZEND_THIS); \ #define TIDY_SET_DEFAULT_CONFIG(_doc) \ @@ -1140,9 +1138,7 @@ PHP_FUNCTION(tidy_diagnose) /* {{{ Get release date (version) for Tidy library */ PHP_FUNCTION(tidy_get_release) { - if (zend_parse_parameters_none() != SUCCESS) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); #ifdef HAVE_TIDYRELEASEDATE RETURN_STRING((const char *)tidyReleaseDate()); From 4dfba7a2503c74db5d5337225e1406ceae1df87e Mon Sep 17 00:00:00 2001 From: DanielEScherzer Date: Sun, 22 Jun 2025 12:29:26 -0700 Subject: [PATCH 128/682] [RFC] Final Property Promotion https://wiki.php.net/rfc/final_promotion --- NEWS | 2 ++ UPGRADING | 2 ++ .../property_hooks/final_prop_promoted_1.phpt | 18 ++++++++++++ .../property_hooks/final_prop_promoted_2.phpt | 18 ++++++++++++ .../property_hooks/final_prop_promoted_3.phpt | 18 ++++++++++++ .../property_hooks/final_prop_promoted_4.phpt | 18 ++++++++++++ .../property_hooks/final_prop_promoted_5.phpt | 28 +++++++++++++++++++ .../final_prop_promoted_ast.phpt | 18 ++++++++++++ Zend/zend_ast.c | 3 ++ Zend/zend_compile.c | 10 ++----- .../tests/ReflectionProperty_isFinal.phpt | 4 +++ 11 files changed, 131 insertions(+), 8 deletions(-) create mode 100644 Zend/tests/property_hooks/final_prop_promoted_1.phpt create mode 100644 Zend/tests/property_hooks/final_prop_promoted_2.phpt create mode 100644 Zend/tests/property_hooks/final_prop_promoted_3.phpt create mode 100644 Zend/tests/property_hooks/final_prop_promoted_4.phpt create mode 100644 Zend/tests/property_hooks/final_prop_promoted_5.phpt create mode 100644 Zend/tests/property_hooks/final_prop_promoted_ast.phpt diff --git a/NEWS b/NEWS index fb117a221a37a..dfa90ed08f62c 100644 --- a/NEWS +++ b/NEWS @@ -56,6 +56,8 @@ PHP NEWS . Properly handle __debugInfo() returning an array reference. (nielsdos) . Properly handle reference return value from __toString(). (nielsdos) . Added the pipe (|>) operator. (crell) + . Added support for `final` with constructor property promotion. + (DanielEScherzer) - Curl: . Added curl_multi_get_handles(). (timwolla) diff --git a/UPGRADING b/UPGRADING index 72bb5d76da936..e551f0c894260 100644 --- a/UPGRADING +++ b/UPGRADING @@ -146,6 +146,8 @@ PHP 8.5 UPGRADE NOTES RFC: https://wiki.php.net/rfc/attributes-on-constants . Added the pipe (|>) operator. RFC: https://wiki.php.net/rfc/pipe-operator-v3 + . Constructor property promotion can now be used for final properties. + RFC: https://wiki.php.net/rfc/final_promotion - Curl: . Added support for share handles that are persisted across multiple PHP diff --git a/Zend/tests/property_hooks/final_prop_promoted_1.phpt b/Zend/tests/property_hooks/final_prop_promoted_1.phpt new file mode 100644 index 0000000000000..740588d8aa2ae --- /dev/null +++ b/Zend/tests/property_hooks/final_prop_promoted_1.phpt @@ -0,0 +1,18 @@ +--TEST-- +Promoted property may be marked final (hook) +--FILE-- + +--EXPECTF-- +Fatal error: Cannot override final property A::$prop in %s on line %d diff --git a/Zend/tests/property_hooks/final_prop_promoted_2.phpt b/Zend/tests/property_hooks/final_prop_promoted_2.phpt new file mode 100644 index 0000000000000..535a7ceabd317 --- /dev/null +++ b/Zend/tests/property_hooks/final_prop_promoted_2.phpt @@ -0,0 +1,18 @@ +--TEST-- +Promoted property may be marked final (normal) +--FILE-- + +--EXPECTF-- +Fatal error: Cannot override final property A::$prop in %s on line %d diff --git a/Zend/tests/property_hooks/final_prop_promoted_3.phpt b/Zend/tests/property_hooks/final_prop_promoted_3.phpt new file mode 100644 index 0000000000000..600d18df84c9a --- /dev/null +++ b/Zend/tests/property_hooks/final_prop_promoted_3.phpt @@ -0,0 +1,18 @@ +--TEST-- +Promoted property may be marked final (no visibility needed) +--FILE-- + +--EXPECTF-- +Fatal error: Cannot override final property A::$prop in %s on line %d diff --git a/Zend/tests/property_hooks/final_prop_promoted_4.phpt b/Zend/tests/property_hooks/final_prop_promoted_4.phpt new file mode 100644 index 0000000000000..2b8270ff5a062 --- /dev/null +++ b/Zend/tests/property_hooks/final_prop_promoted_4.phpt @@ -0,0 +1,18 @@ +--TEST-- +Final promoted property conflicts with non-promoted non-hooked property +--FILE-- + +--EXPECTF-- +Fatal error: Cannot override final property A::$prop in %s on line %d diff --git a/Zend/tests/property_hooks/final_prop_promoted_5.phpt b/Zend/tests/property_hooks/final_prop_promoted_5.phpt new file mode 100644 index 0000000000000..3e592f99b3416 --- /dev/null +++ b/Zend/tests/property_hooks/final_prop_promoted_5.phpt @@ -0,0 +1,28 @@ +--TEST-- +Non-promoted constructor parameter does not conflict with final promoted property +--FILE-- + +--EXPECT-- +B::__construct(): test +A::__construct(): test diff --git a/Zend/tests/property_hooks/final_prop_promoted_ast.phpt b/Zend/tests/property_hooks/final_prop_promoted_ast.phpt new file mode 100644 index 0000000000000..32aa1f27dc323 --- /dev/null +++ b/Zend/tests/property_hooks/final_prop_promoted_ast.phpt @@ -0,0 +1,18 @@ +--TEST-- +Confirm that the AST indicates final promoted properties +--FILE-- +getMessage(), "\n"; +} +?> +--EXPECT-- +assert(false && new class { + public function __construct(public final $prop) { + } + +}) diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index beecf51216a94..cdc86faa95aa3 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -2795,6 +2795,9 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio zend_ast_export_attributes(str, ast->child[3], indent, 0); } zend_ast_export_visibility(str, ast->attr, ZEND_MODIFIER_TARGET_CPP); + if (ast->attr & ZEND_ACC_FINAL) { + smart_str_appends(str, "final "); + } if (ast->child[0]) { zend_ast_export_type(str, ast->child[0], indent); smart_str_appendc(str, ' '); diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 2bc0cf7b703d9..28bea1a21d759 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -903,13 +903,7 @@ uint32_t zend_modifier_token_to_flag(zend_modifier_target target, uint32_t token } break; case T_FINAL: - if (target == ZEND_MODIFIER_TARGET_METHOD - || target == ZEND_MODIFIER_TARGET_CONSTANT - || target == ZEND_MODIFIER_TARGET_PROPERTY - || target == ZEND_MODIFIER_TARGET_PROPERTY_HOOK) { - return ZEND_ACC_FINAL; - } - break; + return ZEND_ACC_FINAL; case T_STATIC: if (target == ZEND_MODIFIER_TARGET_PROPERTY || target == ZEND_MODIFIER_TARGET_METHOD) { return ZEND_ACC_STATIC; @@ -7681,7 +7675,7 @@ static void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32 zend_string *name = zval_make_interned_string(zend_ast_get_zval(var_ast)); bool is_ref = (param_ast->attr & ZEND_PARAM_REF) != 0; bool is_variadic = (param_ast->attr & ZEND_PARAM_VARIADIC) != 0; - uint32_t property_flags = param_ast->attr & (ZEND_ACC_PPP_MASK | ZEND_ACC_PPP_SET_MASK | ZEND_ACC_READONLY); + uint32_t property_flags = param_ast->attr & (ZEND_ACC_PPP_MASK | ZEND_ACC_PPP_SET_MASK | ZEND_ACC_READONLY | ZEND_ACC_FINAL); bool is_promoted = property_flags || hooks_ast; znode var_node, default_node; diff --git a/ext/reflection/tests/ReflectionProperty_isFinal.phpt b/ext/reflection/tests/ReflectionProperty_isFinal.phpt index 62b792fd7253f..f01835efbed25 100644 --- a/ext/reflection/tests/ReflectionProperty_isFinal.phpt +++ b/ext/reflection/tests/ReflectionProperty_isFinal.phpt @@ -12,6 +12,8 @@ class C { public protected(set) final mixed $p6; public private(set) mixed $p7; public private(set) final mixed $p8; + + public function __construct(final $p9, public $p10) {} } $rc = new ReflectionClass(C::class); @@ -30,3 +32,5 @@ p5: bool(false) p6: bool(true) p7: bool(true) p8: bool(true) +p9: bool(true) +p10: bool(false) From ddd33fd7e4e88f96fef91180b6bdf6d7af3af18e Mon Sep 17 00:00:00 2001 From: DanielEScherzer Date: Sun, 22 Jun 2025 14:35:28 -0700 Subject: [PATCH 129/682] Generated arginfo headers: combine preprocessor conditional blocks (2) (#18667) When global constants' or class constants' availability is based on some preprocessor condition, the generated arginfo header files wrap the declarations in the preprocessor `#if` conditional blocks, one per declaration, even if they are in the same conditional block based on comments in the stub file. Instead of having multiple conditional blocks one after the other with the same condition, combine them into a single conditional block. --- build/gen_stub.php | 30 ++-- ext/com_dotnet/com_extension_arginfo.h | 2 - ext/curl/curl_arginfo.h | 158 ------------------ ext/dba/dba_arginfo.h | 2 - ext/gd/gd_arginfo.h | 18 -- .../listformatter/listformatter_arginfo.h | 6 - ext/intl/spoofchecker/spoofchecker_arginfo.h | 12 -- ext/intl/uchar/uchar_arginfo.h | 4 - ext/ldap/ldap_arginfo.h | 96 ----------- ext/odbc/odbc_arginfo.h | 32 ---- ext/openssl/openssl_arginfo.h | 16 -- ext/openssl/openssl_pwhash_arginfo.h | 10 -- ext/pcntl/pcntl_arginfo.h | 28 ---- ext/pdo_mysql/pdo_mysql_arginfo.h | 4 - ext/pdo_sqlite/pdo_sqlite_arginfo.h | 4 - ext/sockets/sockets_arginfo.h | 76 --------- ext/sodium/libsodium_arginfo.h | 82 --------- ext/sodium/sodium_pwhash_arginfo.h | 10 -- ext/standard/basic_functions_arginfo.h | 106 ------------ ext/standard/file_arginfo.h | 4 - ext/standard/password_arginfo.h | 10 -- ext/tidy/tidy_arginfo.h | 54 ------ ext/xsl/php_xsl_arginfo.h | 2 - ext/zip/php_zip_arginfo.h | 44 ----- main/main_arginfo.h | 20 --- 25 files changed, 13 insertions(+), 817 deletions(-) diff --git a/build/gen_stub.php b/build/gen_stub.php index ff86106e8a2a4..fcef8213d0b55 100755 --- a/build/gen_stub.php +++ b/build/gen_stub.php @@ -2749,23 +2749,15 @@ public function getDeclaration(array $allConstInfos): string throw new Exception("Constant " . $this->name->__toString() . " must have a @cvalue annotation"); } - $code = ""; - - if ($this->cond) { - $code .= "#if {$this->cond}\n"; - } + // Condition will be added by generateCodeWithConditions() if ($this->name->isClassConst()) { - $code .= $this->getClassConstDeclaration($value, $allConstInfos); + $code = $this->getClassConstDeclaration($value, $allConstInfos); } else { - $code .= $this->getGlobalConstDeclaration($value); + $code = $this->getGlobalConstDeclaration($value); } $code .= $this->getValueAssertion($value); - if ($this->cond) { - $code .= "#endif\n"; - } - return $code; } @@ -3556,9 +3548,11 @@ function (Name $item) { $code .= "\tzend_register_class_alias(\"" . str_replace("\\", "\\\\", $this->alias) . "\", class_entry);\n"; } - foreach ($this->constInfos as $const) { - $code .= $const->getDeclaration($allConstInfos); - } + $code .= generateCodeWithConditions( + $this->constInfos, + '', + static fn (ConstInfo $const): string => $const->getDeclaration($allConstInfos) + ); foreach ($this->enumCaseInfos as $enumCase) { $code .= $enumCase->getDeclaration($allConstInfos); @@ -5192,9 +5186,11 @@ static function (FuncInfo $funcInfo) use ($fileInfo, &$generatedFunctionDeclarat $code .= "\nstatic void register_{$stubFilenameWithoutExtension}_symbols(int module_number)\n"; $code .= "{\n"; - foreach ($fileInfo->constInfos as $constInfo) { - $code .= $constInfo->getDeclaration($allConstInfos); - } + $code .= generateCodeWithConditions( + $fileInfo->constInfos, + '', + static fn (ConstInfo $constInfo): string => $constInfo->getDeclaration($allConstInfos) + ); if ($attributeInitializationCode !== "" && $fileInfo->constInfos) { $code .= "\n"; diff --git a/ext/com_dotnet/com_extension_arginfo.h b/ext/com_dotnet/com_extension_arginfo.h index b5773f17fda3d..a2bcbf31c968b 100644 --- a/ext/com_dotnet/com_extension_arginfo.h +++ b/ext/com_dotnet/com_extension_arginfo.h @@ -281,8 +281,6 @@ static void register_com_extension_symbols(int module_number) REGISTER_LONG_CONSTANT("MK_E_UNAVAILABLE", PHP_MK_E_UNAVAILABLE, CONST_PERSISTENT); #if SIZEOF_ZEND_LONG == 8 REGISTER_LONG_CONSTANT("VT_UI8", VT_UI8, CONST_PERSISTENT); -#endif -#if SIZEOF_ZEND_LONG == 8 REGISTER_LONG_CONSTANT("VT_I8", VT_I8, CONST_PERSISTENT); #endif } diff --git a/ext/curl/curl_arginfo.h b/ext/curl/curl_arginfo.h index 8928da3f47453..ef267cd803ba2 100644 --- a/ext/curl/curl_arginfo.h +++ b/ext/curl/curl_arginfo.h @@ -328,11 +328,7 @@ static void register_curl_symbols(int module_number) REGISTER_LONG_CONSTANT("CURLOPT_DEBUGFUNCTION", CURLOPT_DEBUGFUNCTION, CONST_PERSISTENT); #if LIBCURL_VERSION_NUM >= 0x080d00 /* Available since 8.13.0 */ REGISTER_LONG_CONSTANT("CURLFOLLOW_ALL", CURLFOLLOW_ALL, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x080d00 /* Available since 8.13.0 */ REGISTER_LONG_CONSTANT("CURLFOLLOW_OBEYCODE", CURLFOLLOW_OBEYCODE, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x080d00 /* Available since 8.13.0 */ REGISTER_LONG_CONSTANT("CURLFOLLOW_FIRSTONLY", CURLFOLLOW_FIRSTONLY, CONST_PERSISTENT); #endif REGISTER_LONG_CONSTANT("CURLINFO_TEXT", CURLINFO_TEXT, CONST_PERSISTENT); @@ -436,14 +432,10 @@ static void register_curl_symbols(int module_number) #endif #if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */ REGISTER_LONG_CONSTANT("CURLINFO_CAPATH", CURLINFO_CAPATH, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */ REGISTER_LONG_CONSTANT("CURLINFO_CAINFO", CURLINFO_CAINFO, CONST_PERSISTENT); #endif #if LIBCURL_VERSION_NUM >= 0x080c00 /* Available since 8.12.0 */ REGISTER_LONG_CONSTANT("CURLINFO_HTTPAUTH_USED", CURLINFO_HTTPAUTH_USED, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x080c00 /* Available since 8.12.0 */ REGISTER_LONG_CONSTANT("CURLINFO_PROXYAUTH_USED", CURLINFO_PROXYAUTH_USED, CONST_PERSISTENT); #endif REGISTER_LONG_CONSTANT("CURLMSG_DONE", CURLMSG_DONE, CONST_PERSISTENT); @@ -838,11 +830,7 @@ static void register_curl_symbols(int module_number) REGISTER_LONG_CONSTANT("CURLOPT_TLS13_CIPHERS", CURLOPT_TLS13_CIPHERS, CONST_PERSISTENT); #if LIBCURL_VERSION_NUM >= 0x073E00 /* Available since 7.62.0 */ REGISTER_LONG_CONSTANT("CURLOPT_DOH_URL", CURLOPT_DOH_URL, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x073E00 /* Available since 7.62.0 */ REGISTER_LONG_CONSTANT("CURLOPT_UPKEEP_INTERVAL_MS", CURLOPT_UPKEEP_INTERVAL_MS, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x073E00 /* Available since 7.62.0 */ REGISTER_LONG_CONSTANT("CURLOPT_UPLOAD_BUFFERSIZE", CURLOPT_UPLOAD_BUFFERSIZE, CONST_PERSISTENT); #endif #if LIBCURL_VERSION_NUM >= 0x074000 /* Available since 7.64.0 */ @@ -850,23 +838,11 @@ static void register_curl_symbols(int module_number) #endif #if LIBCURL_VERSION_NUM >= 0x074001 /* Available since 7.64.1 */ REGISTER_LONG_CONSTANT("CURLALTSVC_H1", CURLALTSVC_H1, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x074001 /* Available since 7.64.1 */ REGISTER_LONG_CONSTANT("CURLALTSVC_H2", CURLALTSVC_H2, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x074001 /* Available since 7.64.1 */ REGISTER_LONG_CONSTANT("CURLALTSVC_H3", CURLALTSVC_H3, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x074001 /* Available since 7.64.1 */ REGISTER_LONG_CONSTANT("CURLALTSVC_READONLYFILE", CURLALTSVC_READONLYFILE, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x074001 /* Available since 7.64.1 */ REGISTER_LONG_CONSTANT("CURLOPT_ALTSVC", CURLOPT_ALTSVC, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x074001 /* Available since 7.64.1 */ REGISTER_LONG_CONSTANT("CURLOPT_ALTSVC_CTRL", CURLOPT_ALTSVC_CTRL, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x074001 /* Available since 7.64.1 */ REGISTER_LONG_CONSTANT("CURL_VERSION_ALTSVC", CURL_VERSION_ALTSVC, CONST_PERSISTENT); #endif #if LIBCURL_VERSION_NUM >= 0x074100 /* Available since 7.65.0 */ @@ -874,14 +850,8 @@ static void register_curl_symbols(int module_number) #endif #if LIBCURL_VERSION_NUM >= 0x074200 /* Available since 7.66.0 */ REGISTER_LONG_CONSTANT("CURLOPT_SASL_AUTHZID", CURLOPT_SASL_AUTHZID, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x074200 /* Available since 7.66.0 */ REGISTER_LONG_CONSTANT("CURL_VERSION_HTTP3", CURL_VERSION_HTTP3, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x074200 /* Available since 7.66.0 */ REGISTER_LONG_CONSTANT("CURLINFO_RETRY_AFTER", CURLINFO_RETRY_AFTER, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x074200 /* Available since 7.66.0 */ REGISTER_LONG_CONSTANT("CURL_HTTP_VERSION_3", CURL_HTTP_VERSION_3, CONST_PERSISTENT); #endif #if LIBCURL_VERSION_NUM >= 0x074300 /* Available since 7.67.0 */ @@ -898,212 +868,90 @@ static void register_curl_symbols(int module_number) #endif #if LIBCURL_VERSION_NUM >= 0x074700 /* Available since 7.71.0 */ REGISTER_LONG_CONSTANT("CURLOPT_ISSUERCERT_BLOB", CURLOPT_ISSUERCERT_BLOB, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x074700 /* Available since 7.71.0 */ REGISTER_LONG_CONSTANT("CURLOPT_PROXY_ISSUERCERT", CURLOPT_PROXY_ISSUERCERT, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x074700 /* Available since 7.71.0 */ REGISTER_LONG_CONSTANT("CURLOPT_PROXY_ISSUERCERT_BLOB", CURLOPT_PROXY_ISSUERCERT_BLOB, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x074700 /* Available since 7.71.0 */ REGISTER_LONG_CONSTANT("CURLOPT_PROXY_SSLCERT_BLOB", CURLOPT_PROXY_SSLCERT_BLOB, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x074700 /* Available since 7.71.0 */ REGISTER_LONG_CONSTANT("CURLOPT_PROXY_SSLKEY_BLOB", CURLOPT_PROXY_SSLKEY_BLOB, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x074700 /* Available since 7.71.0 */ REGISTER_LONG_CONSTANT("CURLOPT_SSLCERT_BLOB", CURLOPT_SSLCERT_BLOB, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x074700 /* Available since 7.71.0 */ REGISTER_LONG_CONSTANT("CURLOPT_SSLKEY_BLOB", CURLOPT_SSLKEY_BLOB, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x074700 /* Available since 7.71.0 */ REGISTER_LONG_CONSTANT("CURLPROTO_MQTT", CURLPROTO_MQTT, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x074700 /* Available since 7.71.0 */ REGISTER_LONG_CONSTANT("CURLSSLOPT_NATIVE_CA", CURLSSLOPT_NATIVE_CA, CONST_PERSISTENT); #endif #if LIBCURL_VERSION_NUM >= 0x074800 /* Available since 7.72.0 */ REGISTER_LONG_CONSTANT("CURL_VERSION_UNICODE", CURL_VERSION_UNICODE, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x074800 /* Available since 7.72.0 */ REGISTER_LONG_CONSTANT("CURL_VERSION_ZSTD", CURL_VERSION_ZSTD, CONST_PERSISTENT); #endif #if LIBCURL_VERSION_NUM >= 0x074900 /* Available since 7.73.0 */ REGISTER_LONG_CONSTANT("CURLE_PROXY", CURLE_PROXY, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x074900 /* Available since 7.73.0 */ REGISTER_LONG_CONSTANT("CURLINFO_PROXY_ERROR", CURLINFO_PROXY_ERROR, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x074900 /* Available since 7.73.0 */ REGISTER_LONG_CONSTANT("CURLOPT_SSL_EC_CURVES", CURLOPT_SSL_EC_CURVES, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x074900 /* Available since 7.73.0 */ REGISTER_LONG_CONSTANT("CURLPX_BAD_ADDRESS_TYPE", CURLPX_BAD_ADDRESS_TYPE, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x074900 /* Available since 7.73.0 */ REGISTER_LONG_CONSTANT("CURLPX_BAD_VERSION", CURLPX_BAD_VERSION, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x074900 /* Available since 7.73.0 */ REGISTER_LONG_CONSTANT("CURLPX_CLOSED", CURLPX_CLOSED, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x074900 /* Available since 7.73.0 */ REGISTER_LONG_CONSTANT("CURLPX_GSSAPI", CURLPX_GSSAPI, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x074900 /* Available since 7.73.0 */ REGISTER_LONG_CONSTANT("CURLPX_GSSAPI_PERMSG", CURLPX_GSSAPI_PERMSG, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x074900 /* Available since 7.73.0 */ REGISTER_LONG_CONSTANT("CURLPX_GSSAPI_PROTECTION", CURLPX_GSSAPI_PROTECTION, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x074900 /* Available since 7.73.0 */ REGISTER_LONG_CONSTANT("CURLPX_IDENTD", CURLPX_IDENTD, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x074900 /* Available since 7.73.0 */ REGISTER_LONG_CONSTANT("CURLPX_IDENTD_DIFFER", CURLPX_IDENTD_DIFFER, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x074900 /* Available since 7.73.0 */ REGISTER_LONG_CONSTANT("CURLPX_LONG_HOSTNAME", CURLPX_LONG_HOSTNAME, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x074900 /* Available since 7.73.0 */ REGISTER_LONG_CONSTANT("CURLPX_LONG_PASSWD", CURLPX_LONG_PASSWD, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x074900 /* Available since 7.73.0 */ REGISTER_LONG_CONSTANT("CURLPX_LONG_USER", CURLPX_LONG_USER, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x074900 /* Available since 7.73.0 */ REGISTER_LONG_CONSTANT("CURLPX_NO_AUTH", CURLPX_NO_AUTH, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x074900 /* Available since 7.73.0 */ REGISTER_LONG_CONSTANT("CURLPX_OK", CURLPX_OK, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x074900 /* Available since 7.73.0 */ REGISTER_LONG_CONSTANT("CURLPX_RECV_ADDRESS", CURLPX_RECV_ADDRESS, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x074900 /* Available since 7.73.0 */ REGISTER_LONG_CONSTANT("CURLPX_RECV_AUTH", CURLPX_RECV_AUTH, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x074900 /* Available since 7.73.0 */ REGISTER_LONG_CONSTANT("CURLPX_RECV_CONNECT", CURLPX_RECV_CONNECT, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x074900 /* Available since 7.73.0 */ REGISTER_LONG_CONSTANT("CURLPX_RECV_REQACK", CURLPX_RECV_REQACK, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x074900 /* Available since 7.73.0 */ REGISTER_LONG_CONSTANT("CURLPX_REPLY_ADDRESS_TYPE_NOT_SUPPORTED", CURLPX_REPLY_ADDRESS_TYPE_NOT_SUPPORTED, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x074900 /* Available since 7.73.0 */ REGISTER_LONG_CONSTANT("CURLPX_REPLY_COMMAND_NOT_SUPPORTED", CURLPX_REPLY_COMMAND_NOT_SUPPORTED, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x074900 /* Available since 7.73.0 */ REGISTER_LONG_CONSTANT("CURLPX_REPLY_CONNECTION_REFUSED", CURLPX_REPLY_CONNECTION_REFUSED, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x074900 /* Available since 7.73.0 */ REGISTER_LONG_CONSTANT("CURLPX_REPLY_GENERAL_SERVER_FAILURE", CURLPX_REPLY_GENERAL_SERVER_FAILURE, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x074900 /* Available since 7.73.0 */ REGISTER_LONG_CONSTANT("CURLPX_REPLY_HOST_UNREACHABLE", CURLPX_REPLY_HOST_UNREACHABLE, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x074900 /* Available since 7.73.0 */ REGISTER_LONG_CONSTANT("CURLPX_REPLY_NETWORK_UNREACHABLE", CURLPX_REPLY_NETWORK_UNREACHABLE, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x074900 /* Available since 7.73.0 */ REGISTER_LONG_CONSTANT("CURLPX_REPLY_NOT_ALLOWED", CURLPX_REPLY_NOT_ALLOWED, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x074900 /* Available since 7.73.0 */ REGISTER_LONG_CONSTANT("CURLPX_REPLY_TTL_EXPIRED", CURLPX_REPLY_TTL_EXPIRED, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x074900 /* Available since 7.73.0 */ REGISTER_LONG_CONSTANT("CURLPX_REPLY_UNASSIGNED", CURLPX_REPLY_UNASSIGNED, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x074900 /* Available since 7.73.0 */ REGISTER_LONG_CONSTANT("CURLPX_REQUEST_FAILED", CURLPX_REQUEST_FAILED, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x074900 /* Available since 7.73.0 */ REGISTER_LONG_CONSTANT("CURLPX_RESOLVE_HOST", CURLPX_RESOLVE_HOST, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x074900 /* Available since 7.73.0 */ REGISTER_LONG_CONSTANT("CURLPX_SEND_AUTH", CURLPX_SEND_AUTH, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x074900 /* Available since 7.73.0 */ REGISTER_LONG_CONSTANT("CURLPX_SEND_CONNECT", CURLPX_SEND_CONNECT, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x074900 /* Available since 7.73.0 */ REGISTER_LONG_CONSTANT("CURLPX_SEND_REQUEST", CURLPX_SEND_REQUEST, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x074900 /* Available since 7.73.0 */ REGISTER_LONG_CONSTANT("CURLPX_UNKNOWN_FAIL", CURLPX_UNKNOWN_FAIL, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x074900 /* Available since 7.73.0 */ REGISTER_LONG_CONSTANT("CURLPX_UNKNOWN_MODE", CURLPX_UNKNOWN_MODE, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x074900 /* Available since 7.73.0 */ REGISTER_LONG_CONSTANT("CURLPX_USER_REJECTED", CURLPX_USER_REJECTED, CONST_PERSISTENT); #endif #if LIBCURL_VERSION_NUM >= 0x074a00 /* Available since 7.74.0 */ REGISTER_LONG_CONSTANT("CURLHSTS_ENABLE", CURLHSTS_ENABLE, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x074a00 /* Available since 7.74.0 */ REGISTER_LONG_CONSTANT("CURLHSTS_READONLYFILE", CURLHSTS_READONLYFILE, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x074a00 /* Available since 7.74.0 */ REGISTER_LONG_CONSTANT("CURLOPT_HSTS", CURLOPT_HSTS, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x074a00 /* Available since 7.74.0 */ REGISTER_LONG_CONSTANT("CURLOPT_HSTS_CTRL", CURLOPT_HSTS_CTRL, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x074a00 /* Available since 7.74.0 */ REGISTER_LONG_CONSTANT("CURL_VERSION_HSTS", CURL_VERSION_HSTS, CONST_PERSISTENT); #endif #if LIBCURL_VERSION_NUM >= 0x074b00 /* Available since 7.75.0 */ REGISTER_LONG_CONSTANT("CURLAUTH_AWS_SIGV4", CURLAUTH_AWS_SIGV4, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x074b00 /* Available since 7.75.0 */ REGISTER_LONG_CONSTANT("CURLOPT_AWS_SIGV4", CURLOPT_AWS_SIGV4, CONST_PERSISTENT); #endif #if LIBCURL_VERSION_NUM >= 0x074c00 /* Available since 7.76.0 */ REGISTER_LONG_CONSTANT("CURLINFO_REFERER", CURLINFO_REFERER, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x074c00 /* Available since 7.76.0 */ REGISTER_LONG_CONSTANT("CURLOPT_DOH_SSL_VERIFYHOST", CURLOPT_DOH_SSL_VERIFYHOST, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x074c00 /* Available since 7.76.0 */ REGISTER_LONG_CONSTANT("CURLOPT_DOH_SSL_VERIFYPEER", CURLOPT_DOH_SSL_VERIFYPEER, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x074c00 /* Available since 7.76.0 */ REGISTER_LONG_CONSTANT("CURLOPT_DOH_SSL_VERIFYSTATUS", CURLOPT_DOH_SSL_VERIFYSTATUS, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x074c00 /* Available since 7.76.0 */ REGISTER_LONG_CONSTANT("CURL_VERSION_GSASL", CURL_VERSION_GSASL, CONST_PERSISTENT); #endif #if LIBCURL_VERSION_NUM >= 0x074d00 /* Available since 7.77.0 */ REGISTER_LONG_CONSTANT("CURLOPT_CAINFO_BLOB", CURLOPT_CAINFO_BLOB, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x074d00 /* Available since 7.77.0 */ REGISTER_LONG_CONSTANT("CURLOPT_PROXY_CAINFO_BLOB", CURLOPT_PROXY_CAINFO_BLOB, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x074d00 /* Available since 7.77.0 */ REGISTER_LONG_CONSTANT("CURLSSLOPT_AUTO_CLIENT_CERT", CURLSSLOPT_AUTO_CLIENT_CERT, CONST_PERSISTENT); #endif #if LIBCURL_VERSION_NUM >= 0x075000 /* Available since 7.80.0 */ REGISTER_LONG_CONSTANT("CURLOPT_MAXLIFETIME_CONN", CURLOPT_MAXLIFETIME_CONN, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x075000 /* Available since 7.80.0 */ REGISTER_LONG_CONSTANT("CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256", CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x075000 /* Available since 7.80.0 */ REGISTER_LONG_CONSTANT("CURLOPT_PREREQFUNCTION", CURLOPT_PREREQFUNCTION, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x075000 /* Available since 7.80.0 */ REGISTER_LONG_CONSTANT("CURL_PREREQFUNC_OK", CURL_PREREQFUNC_OK, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x075000 /* Available since 7.80.0 */ REGISTER_LONG_CONSTANT("CURL_PREREQFUNC_ABORT", CURL_PREREQFUNC_ABORT, CONST_PERSISTENT); #endif #if LIBCURL_VERSION_NUM >= 0x075100 /* Available since 7.81.0 */ REGISTER_LONG_CONSTANT("CURLOPT_MIME_OPTIONS", CURLOPT_MIME_OPTIONS, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x075100 /* Available since 7.81.0 */ REGISTER_LONG_CONSTANT("CURLMIMEOPT_FORMESCAPE", CURLMIMEOPT_FORMESCAPE, CONST_PERSISTENT); #endif #if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */ @@ -1111,20 +959,14 @@ static void register_curl_symbols(int module_number) #endif #if LIBCURL_VERSION_NUM >= 0x075500 /* Available since 7.85.0 */ REGISTER_LONG_CONSTANT("CURLOPT_PROTOCOLS_STR", CURLOPT_PROTOCOLS_STR, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x075500 /* Available since 7.85.0 */ REGISTER_LONG_CONSTANT("CURLOPT_REDIR_PROTOCOLS_STR", CURLOPT_REDIR_PROTOCOLS_STR, CONST_PERSISTENT); #endif #if LIBCURL_VERSION_NUM >= 0x075600 /* Available since 7.86.0 */ REGISTER_LONG_CONSTANT("CURLOPT_WS_OPTIONS", CURLOPT_WS_OPTIONS, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x075600 /* Available since 7.86.0 */ REGISTER_LONG_CONSTANT("CURLWS_RAW_MODE", CURLWS_RAW_MODE, CONST_PERSISTENT); #endif #if LIBCURL_VERSION_NUM >= 0x075700 /* Available since 7.87.0 */ REGISTER_LONG_CONSTANT("CURLOPT_CA_CACHE_TIMEOUT", CURLOPT_CA_CACHE_TIMEOUT, CONST_PERSISTENT); -#endif -#if LIBCURL_VERSION_NUM >= 0x075700 /* Available since 7.87.0 */ REGISTER_LONG_CONSTANT("CURLOPT_QUICK_EXIT", CURLOPT_QUICK_EXIT, CONST_PERSISTENT); #endif #if LIBCURL_VERSION_NUM >= 0x075800 /* Available since 7.88.0 */ diff --git a/ext/dba/dba_arginfo.h b/ext/dba/dba_arginfo.h index 97a45381c92bf..c2befedfda7c7 100644 --- a/ext/dba/dba_arginfo.h +++ b/ext/dba/dba_arginfo.h @@ -99,8 +99,6 @@ static void register_dba_symbols(int module_number) { #if defined(DBA_LMDB) REGISTER_LONG_CONSTANT("DBA_LMDB_USE_SUB_DIR", 0, CONST_PERSISTENT); -#endif -#if defined(DBA_LMDB) REGISTER_LONG_CONSTANT("DBA_LMDB_NO_SUB_DIR", MDB_NOSUBDIR, CONST_PERSISTENT); #endif } diff --git a/ext/gd/gd_arginfo.h b/ext/gd/gd_arginfo.h index 5cf4f2f29a9bd..c80aaac463348 100644 --- a/ext/gd/gd_arginfo.h +++ b/ext/gd/gd_arginfo.h @@ -932,35 +932,17 @@ static void register_gd_symbols(int module_number) #endif #if (defined(GD_MAJOR_VERSION) && defined(GD_MINOR_VERSION) && defined(GD_RELEASE_VERSION) && defined(GD_EXTRA_VERSION)) REGISTER_LONG_CONSTANT("GD_MAJOR_VERSION", GD_MAJOR_VERSION, CONST_PERSISTENT); -#endif -#if (defined(GD_MAJOR_VERSION) && defined(GD_MINOR_VERSION) && defined(GD_RELEASE_VERSION) && defined(GD_EXTRA_VERSION)) REGISTER_LONG_CONSTANT("GD_MINOR_VERSION", GD_MINOR_VERSION, CONST_PERSISTENT); -#endif -#if (defined(GD_MAJOR_VERSION) && defined(GD_MINOR_VERSION) && defined(GD_RELEASE_VERSION) && defined(GD_EXTRA_VERSION)) REGISTER_LONG_CONSTANT("GD_RELEASE_VERSION", GD_RELEASE_VERSION, CONST_PERSISTENT); -#endif -#if (defined(GD_MAJOR_VERSION) && defined(GD_MINOR_VERSION) && defined(GD_RELEASE_VERSION) && defined(GD_EXTRA_VERSION)) REGISTER_STRING_CONSTANT("GD_EXTRA_VERSION", GD_EXTRA_VERSION, CONST_PERSISTENT); #endif #if defined(HAVE_GD_PNG) REGISTER_LONG_CONSTANT("PNG_NO_FILTER", 0x0, CONST_PERSISTENT); -#endif -#if defined(HAVE_GD_PNG) REGISTER_LONG_CONSTANT("PNG_FILTER_NONE", 0x8, CONST_PERSISTENT); -#endif -#if defined(HAVE_GD_PNG) REGISTER_LONG_CONSTANT("PNG_FILTER_SUB", 0x10, CONST_PERSISTENT); -#endif -#if defined(HAVE_GD_PNG) REGISTER_LONG_CONSTANT("PNG_FILTER_UP", 0x20, CONST_PERSISTENT); -#endif -#if defined(HAVE_GD_PNG) REGISTER_LONG_CONSTANT("PNG_FILTER_AVG", 0x40, CONST_PERSISTENT); -#endif -#if defined(HAVE_GD_PNG) REGISTER_LONG_CONSTANT("PNG_FILTER_PAETH", 0x80, CONST_PERSISTENT); -#endif -#if defined(HAVE_GD_PNG) REGISTER_LONG_CONSTANT("PNG_ALL_FILTERS", 0x8 | 0x10 | 0x20 | 0x40 | 0x80, CONST_PERSISTENT); #endif } diff --git a/ext/intl/listformatter/listformatter_arginfo.h b/ext/intl/listformatter/listformatter_arginfo.h index d9a4c3fb84ddc..3a2afa2d1c2d9 100644 --- a/ext/intl/listformatter/listformatter_arginfo.h +++ b/ext/intl/listformatter/listformatter_arginfo.h @@ -59,16 +59,12 @@ static zend_class_entry *register_class_IntlListFormatter(void) zend_string *const_TYPE_OR_name = zend_string_init_interned("TYPE_OR", sizeof("TYPE_OR") - 1, 1); zend_declare_typed_class_constant(class_entry, const_TYPE_OR_name, &const_TYPE_OR_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release(const_TYPE_OR_name); -#endif -#if U_ICU_VERSION_MAJOR_NUM >= 67 zval const_TYPE_UNITS_value; ZVAL_LONG(&const_TYPE_UNITS_value, ULISTFMT_TYPE_UNITS); zend_string *const_TYPE_UNITS_name = zend_string_init_interned("TYPE_UNITS", sizeof("TYPE_UNITS") - 1, 1); zend_declare_typed_class_constant(class_entry, const_TYPE_UNITS_name, &const_TYPE_UNITS_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release(const_TYPE_UNITS_name); -#endif -#if U_ICU_VERSION_MAJOR_NUM >= 67 zval const_WIDTH_WIDE_value; ZVAL_LONG(&const_WIDTH_WIDE_value, ULISTFMT_WIDTH_WIDE); @@ -91,8 +87,6 @@ static zend_class_entry *register_class_IntlListFormatter(void) zend_string *const_WIDTH_SHORT_name = zend_string_init_interned("WIDTH_SHORT", sizeof("WIDTH_SHORT") - 1, 1); zend_declare_typed_class_constant(class_entry, const_WIDTH_SHORT_name, &const_WIDTH_SHORT_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release(const_WIDTH_SHORT_name); -#endif -#if U_ICU_VERSION_MAJOR_NUM >= 67 zval const_WIDTH_NARROW_value; ZVAL_LONG(&const_WIDTH_NARROW_value, ULISTFMT_WIDTH_NARROW); diff --git a/ext/intl/spoofchecker/spoofchecker_arginfo.h b/ext/intl/spoofchecker/spoofchecker_arginfo.h index cee17335dcd40..6a3c0e55aa27d 100644 --- a/ext/intl/spoofchecker/spoofchecker_arginfo.h +++ b/ext/intl/spoofchecker/spoofchecker_arginfo.h @@ -112,48 +112,36 @@ static zend_class_entry *register_class_Spoofchecker(void) zend_string *const_ASCII_name = zend_string_init_interned("ASCII", sizeof("ASCII") - 1, 1); zend_declare_typed_class_constant(class_entry, const_ASCII_name, &const_ASCII_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release(const_ASCII_name); -#endif -#if U_ICU_VERSION_MAJOR_NUM >= 58 zval const_HIGHLY_RESTRICTIVE_value; ZVAL_LONG(&const_HIGHLY_RESTRICTIVE_value, USPOOF_HIGHLY_RESTRICTIVE); zend_string *const_HIGHLY_RESTRICTIVE_name = zend_string_init_interned("HIGHLY_RESTRICTIVE", sizeof("HIGHLY_RESTRICTIVE") - 1, 1); zend_declare_typed_class_constant(class_entry, const_HIGHLY_RESTRICTIVE_name, &const_HIGHLY_RESTRICTIVE_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release(const_HIGHLY_RESTRICTIVE_name); -#endif -#if U_ICU_VERSION_MAJOR_NUM >= 58 zval const_MODERATELY_RESTRICTIVE_value; ZVAL_LONG(&const_MODERATELY_RESTRICTIVE_value, USPOOF_MODERATELY_RESTRICTIVE); zend_string *const_MODERATELY_RESTRICTIVE_name = zend_string_init_interned("MODERATELY_RESTRICTIVE", sizeof("MODERATELY_RESTRICTIVE") - 1, 1); zend_declare_typed_class_constant(class_entry, const_MODERATELY_RESTRICTIVE_name, &const_MODERATELY_RESTRICTIVE_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release(const_MODERATELY_RESTRICTIVE_name); -#endif -#if U_ICU_VERSION_MAJOR_NUM >= 58 zval const_MINIMALLY_RESTRICTIVE_value; ZVAL_LONG(&const_MINIMALLY_RESTRICTIVE_value, USPOOF_MINIMALLY_RESTRICTIVE); zend_string *const_MINIMALLY_RESTRICTIVE_name = zend_string_init_interned("MINIMALLY_RESTRICTIVE", sizeof("MINIMALLY_RESTRICTIVE") - 1, 1); zend_declare_typed_class_constant(class_entry, const_MINIMALLY_RESTRICTIVE_name, &const_MINIMALLY_RESTRICTIVE_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release(const_MINIMALLY_RESTRICTIVE_name); -#endif -#if U_ICU_VERSION_MAJOR_NUM >= 58 zval const_UNRESTRICTIVE_value; ZVAL_LONG(&const_UNRESTRICTIVE_value, USPOOF_UNRESTRICTIVE); zend_string *const_UNRESTRICTIVE_name = zend_string_init_interned("UNRESTRICTIVE", sizeof("UNRESTRICTIVE") - 1, 1); zend_declare_typed_class_constant(class_entry, const_UNRESTRICTIVE_name, &const_UNRESTRICTIVE_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release(const_UNRESTRICTIVE_name); -#endif -#if U_ICU_VERSION_MAJOR_NUM >= 58 zval const_SINGLE_SCRIPT_RESTRICTIVE_value; ZVAL_LONG(&const_SINGLE_SCRIPT_RESTRICTIVE_value, USPOOF_SINGLE_SCRIPT_RESTRICTIVE); zend_string *const_SINGLE_SCRIPT_RESTRICTIVE_name = zend_string_init_interned("SINGLE_SCRIPT_RESTRICTIVE", sizeof("SINGLE_SCRIPT_RESTRICTIVE") - 1, 1); zend_declare_typed_class_constant(class_entry, const_SINGLE_SCRIPT_RESTRICTIVE_name, &const_SINGLE_SCRIPT_RESTRICTIVE_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release(const_SINGLE_SCRIPT_RESTRICTIVE_name); -#endif -#if U_ICU_VERSION_MAJOR_NUM >= 58 zval const_MIXED_NUMBERS_value; ZVAL_LONG(&const_MIXED_NUMBERS_value, USPOOF_MIXED_NUMBERS); diff --git a/ext/intl/uchar/uchar_arginfo.h b/ext/intl/uchar/uchar_arginfo.h index 085b3b0a5eff3..f290fb2b958ec 100644 --- a/ext/intl/uchar/uchar_arginfo.h +++ b/ext/intl/uchar/uchar_arginfo.h @@ -465,16 +465,12 @@ static zend_class_entry *register_class_IntlChar(void) zend_string *const_PROPERTY_IDS_UNARY_OPERATOR_name = zend_string_init_interned("PROPERTY_IDS_UNARY_OPERATOR", sizeof("PROPERTY_IDS_UNARY_OPERATOR") - 1, 1); zend_declare_typed_class_constant(class_entry, const_PROPERTY_IDS_UNARY_OPERATOR_name, &const_PROPERTY_IDS_UNARY_OPERATOR_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release(const_PROPERTY_IDS_UNARY_OPERATOR_name); -#endif -#if U_ICU_VERSION_MAJOR_NUM >= 74 zval const_PROPERTY_ID_COMPAT_MATH_START_value; ZVAL_LONG(&const_PROPERTY_ID_COMPAT_MATH_START_value, UCHAR_ID_COMPAT_MATH_START); zend_string *const_PROPERTY_ID_COMPAT_MATH_START_name = zend_string_init_interned("PROPERTY_ID_COMPAT_MATH_START", sizeof("PROPERTY_ID_COMPAT_MATH_START") - 1, 1); zend_declare_typed_class_constant(class_entry, const_PROPERTY_ID_COMPAT_MATH_START_name, &const_PROPERTY_ID_COMPAT_MATH_START_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release(const_PROPERTY_ID_COMPAT_MATH_START_name); -#endif -#if U_ICU_VERSION_MAJOR_NUM >= 74 zval const_PROPERTY_ID_COMPAT_MATH_CONTINUE_value; ZVAL_LONG(&const_PROPERTY_ID_COMPAT_MATH_CONTINUE_value, UCHAR_ID_COMPAT_MATH_CONTINUE); diff --git a/ext/ldap/ldap_arginfo.h b/ext/ldap/ldap_arginfo.h index 984a4bab9a69f..3341b736bb2f1 100644 --- a/ext/ldap/ldap_arginfo.h +++ b/ext/ldap/ldap_arginfo.h @@ -541,11 +541,7 @@ static void register_ldap_symbols(int module_number) REGISTER_STRING_CONSTANT("LDAP_MODIFY_BATCH_VALUES", LDAP_MODIFY_BATCH_VALUES, CONST_PERSISTENT); #if ((LDAP_API_VERSION > 2000) || defined(HAVE_ORALDAP)) REGISTER_LONG_CONSTANT("LDAP_OPT_DEREF", LDAP_OPT_DEREF, CONST_PERSISTENT); -#endif -#if ((LDAP_API_VERSION > 2000) || defined(HAVE_ORALDAP)) REGISTER_LONG_CONSTANT("LDAP_OPT_SIZELIMIT", LDAP_OPT_SIZELIMIT, CONST_PERSISTENT); -#endif -#if ((LDAP_API_VERSION > 2000) || defined(HAVE_ORALDAP)) REGISTER_LONG_CONSTANT("LDAP_OPT_TIMELIMIT", LDAP_OPT_TIMELIMIT, CONST_PERSISTENT); #endif #if ((LDAP_API_VERSION > 2000) || defined(HAVE_ORALDAP)) && defined(LDAP_OPT_NETWORK_TIMEOUT) @@ -559,11 +555,7 @@ static void register_ldap_symbols(int module_number) #endif #if ((LDAP_API_VERSION > 2000) || defined(HAVE_ORALDAP)) REGISTER_LONG_CONSTANT("LDAP_OPT_PROTOCOL_VERSION", LDAP_OPT_PROTOCOL_VERSION, CONST_PERSISTENT); -#endif -#if ((LDAP_API_VERSION > 2000) || defined(HAVE_ORALDAP)) REGISTER_LONG_CONSTANT("LDAP_OPT_ERROR_NUMBER", LDAP_OPT_ERROR_NUMBER, CONST_PERSISTENT); -#endif -#if ((LDAP_API_VERSION > 2000) || defined(HAVE_ORALDAP)) REGISTER_LONG_CONSTANT("LDAP_OPT_REFERRALS", LDAP_OPT_REFERRALS, CONST_PERSISTENT); #endif #if ((LDAP_API_VERSION > 2000) || defined(HAVE_ORALDAP)) && defined(LDAP_OPT_RESTART) @@ -580,8 +572,6 @@ static void register_ldap_symbols(int module_number) #endif #if ((LDAP_API_VERSION > 2000) || defined(HAVE_ORALDAP)) REGISTER_LONG_CONSTANT("LDAP_OPT_SERVER_CONTROLS", LDAP_OPT_SERVER_CONTROLS, CONST_PERSISTENT); -#endif -#if ((LDAP_API_VERSION > 2000) || defined(HAVE_ORALDAP)) REGISTER_LONG_CONSTANT("LDAP_OPT_CLIENT_CONTROLS", LDAP_OPT_CLIENT_CONTROLS, CONST_PERSISTENT); #endif #if defined(LDAP_OPT_DEBUG_LEVEL) @@ -592,14 +582,8 @@ static void register_ldap_symbols(int module_number) #endif #if defined(HAVE_LDAP_SASL) REGISTER_LONG_CONSTANT("LDAP_OPT_X_SASL_MECH", LDAP_OPT_X_SASL_MECH, CONST_PERSISTENT); -#endif -#if defined(HAVE_LDAP_SASL) REGISTER_LONG_CONSTANT("LDAP_OPT_X_SASL_REALM", LDAP_OPT_X_SASL_REALM, CONST_PERSISTENT); -#endif -#if defined(HAVE_LDAP_SASL) REGISTER_LONG_CONSTANT("LDAP_OPT_X_SASL_AUTHCID", LDAP_OPT_X_SASL_AUTHCID, CONST_PERSISTENT); -#endif -#if defined(HAVE_LDAP_SASL) REGISTER_LONG_CONSTANT("LDAP_OPT_X_SASL_AUTHZID", LDAP_OPT_X_SASL_AUTHZID, CONST_PERSISTENT); #endif #if defined(LDAP_OPT_X_SASL_NOCANON) @@ -610,59 +594,27 @@ static void register_ldap_symbols(int module_number) #endif #if defined(HAVE_ORALDAP) REGISTER_LONG_CONSTANT("GSLC_SSL_NO_AUTH", GSLC_SSL_NO_AUTH, CONST_PERSISTENT); -#endif -#if defined(HAVE_ORALDAP) REGISTER_LONG_CONSTANT("GSLC_SSL_ONEWAY_AUTH", GSLC_SSL_ONEWAY_AUTH, CONST_PERSISTENT); -#endif -#if defined(HAVE_ORALDAP) REGISTER_LONG_CONSTANT("GSLC_SSL_TWOWAY_AUTH", GSLC_SSL_TWOWAY_AUTH, CONST_PERSISTENT); #endif #if (LDAP_API_VERSION > 2000) REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_REQUIRE_CERT", LDAP_OPT_X_TLS_REQUIRE_CERT, CONST_PERSISTENT); -#endif -#if (LDAP_API_VERSION > 2000) REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_NEVER", LDAP_OPT_X_TLS_NEVER, CONST_PERSISTENT); -#endif -#if (LDAP_API_VERSION > 2000) REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_HARD", LDAP_OPT_X_TLS_HARD, CONST_PERSISTENT); -#endif -#if (LDAP_API_VERSION > 2000) REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_DEMAND", LDAP_OPT_X_TLS_DEMAND, CONST_PERSISTENT); -#endif -#if (LDAP_API_VERSION > 2000) REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_ALLOW", LDAP_OPT_X_TLS_ALLOW, CONST_PERSISTENT); -#endif -#if (LDAP_API_VERSION > 2000) REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_TRY", LDAP_OPT_X_TLS_TRY, CONST_PERSISTENT); -#endif -#if (LDAP_API_VERSION > 2000) REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_CACERTDIR", LDAP_OPT_X_TLS_CACERTDIR, CONST_PERSISTENT); -#endif -#if (LDAP_API_VERSION > 2000) REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_CACERTFILE", LDAP_OPT_X_TLS_CACERTFILE, CONST_PERSISTENT); -#endif -#if (LDAP_API_VERSION > 2000) REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_CERTFILE", LDAP_OPT_X_TLS_CERTFILE, CONST_PERSISTENT); -#endif -#if (LDAP_API_VERSION > 2000) REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_CIPHER_SUITE", LDAP_OPT_X_TLS_CIPHER_SUITE, CONST_PERSISTENT); -#endif -#if (LDAP_API_VERSION > 2000) REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_KEYFILE", LDAP_OPT_X_TLS_KEYFILE, CONST_PERSISTENT); -#endif -#if (LDAP_API_VERSION > 2000) REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_RANDOM_FILE", LDAP_OPT_X_TLS_RANDOM_FILE, CONST_PERSISTENT); #endif #if defined(LDAP_OPT_X_TLS_CRLCHECK) REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_CRLCHECK", LDAP_OPT_X_TLS_CRLCHECK, CONST_PERSISTENT); -#endif -#if defined(LDAP_OPT_X_TLS_CRLCHECK) REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_CRL_NONE", LDAP_OPT_X_TLS_CRL_NONE, CONST_PERSISTENT); -#endif -#if defined(LDAP_OPT_X_TLS_CRLCHECK) REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_CRL_PEER", LDAP_OPT_X_TLS_CRL_PEER, CONST_PERSISTENT); -#endif -#if defined(LDAP_OPT_X_TLS_CRLCHECK) REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_CRL_ALL", LDAP_OPT_X_TLS_CRL_ALL, CONST_PERSISTENT); #endif #if defined(LDAP_OPT_X_TLS_DHFILE) @@ -673,20 +625,10 @@ static void register_ldap_symbols(int module_number) #endif #if defined(LDAP_OPT_X_TLS_PROTOCOL_MIN) REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_PROTOCOL_MIN", LDAP_OPT_X_TLS_PROTOCOL_MIN, CONST_PERSISTENT); -#endif -#if defined(LDAP_OPT_X_TLS_PROTOCOL_MIN) REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_PROTOCOL_SSL2", LDAP_OPT_X_TLS_PROTOCOL_SSL2, CONST_PERSISTENT); -#endif -#if defined(LDAP_OPT_X_TLS_PROTOCOL_MIN) REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_PROTOCOL_SSL3", LDAP_OPT_X_TLS_PROTOCOL_SSL3, CONST_PERSISTENT); -#endif -#if defined(LDAP_OPT_X_TLS_PROTOCOL_MIN) REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_PROTOCOL_TLS1_0", LDAP_OPT_X_TLS_PROTOCOL_TLS1_0, CONST_PERSISTENT); -#endif -#if defined(LDAP_OPT_X_TLS_PROTOCOL_MIN) REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_PROTOCOL_TLS1_1", LDAP_OPT_X_TLS_PROTOCOL_TLS1_1, CONST_PERSISTENT); -#endif -#if defined(LDAP_OPT_X_TLS_PROTOCOL_MIN) REGISTER_LONG_CONSTANT("LDAP_OPT_X_TLS_PROTOCOL_TLS1_2", LDAP_OPT_X_TLS_PROTOCOL_TLS1_2, CONST_PERSISTENT); #endif #if defined(LDAP_OPT_X_TLS_PROTOCOL_TLS1_3) @@ -700,28 +642,16 @@ static void register_ldap_symbols(int module_number) #endif #if defined(LDAP_OPT_X_KEEPALIVE_IDLE) REGISTER_LONG_CONSTANT("LDAP_OPT_X_KEEPALIVE_IDLE", LDAP_OPT_X_KEEPALIVE_IDLE, CONST_PERSISTENT); -#endif -#if defined(LDAP_OPT_X_KEEPALIVE_IDLE) REGISTER_LONG_CONSTANT("LDAP_OPT_X_KEEPALIVE_PROBES", LDAP_OPT_X_KEEPALIVE_PROBES, CONST_PERSISTENT); -#endif -#if defined(LDAP_OPT_X_KEEPALIVE_IDLE) REGISTER_LONG_CONSTANT("LDAP_OPT_X_KEEPALIVE_INTERVAL", LDAP_OPT_X_KEEPALIVE_INTERVAL, CONST_PERSISTENT); #endif REGISTER_LONG_CONSTANT("LDAP_ESCAPE_FILTER", PHP_LDAP_ESCAPE_FILTER, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("LDAP_ESCAPE_DN", PHP_LDAP_ESCAPE_DN, CONST_PERSISTENT); #if defined(HAVE_LDAP_EXTENDED_OPERATION_S) REGISTER_STRING_CONSTANT("LDAP_EXOP_START_TLS", LDAP_EXOP_START_TLS, CONST_PERSISTENT); -#endif -#if defined(HAVE_LDAP_EXTENDED_OPERATION_S) REGISTER_STRING_CONSTANT("LDAP_EXOP_MODIFY_PASSWD", LDAP_EXOP_MODIFY_PASSWD, CONST_PERSISTENT); -#endif -#if defined(HAVE_LDAP_EXTENDED_OPERATION_S) REGISTER_STRING_CONSTANT("LDAP_EXOP_REFRESH", LDAP_EXOP_REFRESH, CONST_PERSISTENT); -#endif -#if defined(HAVE_LDAP_EXTENDED_OPERATION_S) REGISTER_STRING_CONSTANT("LDAP_EXOP_WHO_AM_I", LDAP_EXOP_WHO_AM_I, CONST_PERSISTENT); -#endif -#if defined(HAVE_LDAP_EXTENDED_OPERATION_S) REGISTER_STRING_CONSTANT("LDAP_EXOP_TURN", LDAP_EXOP_TURN, CONST_PERSISTENT); #endif #if defined(LDAP_CONTROL_MANAGEDSAIT) @@ -738,17 +668,11 @@ static void register_ldap_symbols(int module_number) #endif #if defined(LDAP_CONTROL_ASSERT) REGISTER_STRING_CONSTANT("LDAP_CONTROL_ASSERT", LDAP_CONTROL_ASSERT, CONST_PERSISTENT); -#endif -#if defined(LDAP_CONTROL_ASSERT) REGISTER_STRING_CONSTANT("LDAP_CONTROL_PRE_READ", LDAP_CONTROL_PRE_READ, CONST_PERSISTENT); -#endif -#if defined(LDAP_CONTROL_ASSERT) REGISTER_STRING_CONSTANT("LDAP_CONTROL_POST_READ", LDAP_CONTROL_POST_READ, CONST_PERSISTENT); #endif #if defined(LDAP_CONTROL_SORTREQUEST) REGISTER_STRING_CONSTANT("LDAP_CONTROL_SORTREQUEST", LDAP_CONTROL_SORTREQUEST, CONST_PERSISTENT); -#endif -#if defined(LDAP_CONTROL_SORTREQUEST) REGISTER_STRING_CONSTANT("LDAP_CONTROL_SORTRESPONSE", LDAP_CONTROL_SORTRESPONSE, CONST_PERSISTENT); #endif #if defined(LDAP_CONTROL_PAGEDRESULTS) @@ -756,17 +680,11 @@ static void register_ldap_symbols(int module_number) #endif #if defined(LDAP_CONTROL_AUTHZID_REQUEST) REGISTER_STRING_CONSTANT("LDAP_CONTROL_AUTHZID_REQUEST", LDAP_CONTROL_AUTHZID_REQUEST, CONST_PERSISTENT); -#endif -#if defined(LDAP_CONTROL_AUTHZID_REQUEST) REGISTER_STRING_CONSTANT("LDAP_CONTROL_AUTHZID_RESPONSE", LDAP_CONTROL_AUTHZID_RESPONSE, CONST_PERSISTENT); #endif #if defined(LDAP_CONTROL_SYNC) REGISTER_STRING_CONSTANT("LDAP_CONTROL_SYNC", LDAP_CONTROL_SYNC, CONST_PERSISTENT); -#endif -#if defined(LDAP_CONTROL_SYNC) REGISTER_STRING_CONSTANT("LDAP_CONTROL_SYNC_STATE", LDAP_CONTROL_SYNC_STATE, CONST_PERSISTENT); -#endif -#if defined(LDAP_CONTROL_SYNC) REGISTER_STRING_CONSTANT("LDAP_CONTROL_SYNC_DONE", LDAP_CONTROL_SYNC_DONE, CONST_PERSISTENT); #endif #if defined(LDAP_CONTROL_DONTUSECOPY) @@ -774,32 +692,18 @@ static void register_ldap_symbols(int module_number) #endif #if defined(LDAP_CONTROL_PASSWORDPOLICYREQUEST) REGISTER_STRING_CONSTANT("LDAP_CONTROL_PASSWORDPOLICYREQUEST", LDAP_CONTROL_PASSWORDPOLICYREQUEST, CONST_PERSISTENT); -#endif -#if defined(LDAP_CONTROL_PASSWORDPOLICYREQUEST) REGISTER_STRING_CONSTANT("LDAP_CONTROL_PASSWORDPOLICYRESPONSE", LDAP_CONTROL_PASSWORDPOLICYRESPONSE, CONST_PERSISTENT); #endif #if defined(LDAP_CONTROL_X_INCREMENTAL_VALUES) REGISTER_STRING_CONSTANT("LDAP_CONTROL_X_INCREMENTAL_VALUES", LDAP_CONTROL_X_INCREMENTAL_VALUES, CONST_PERSISTENT); -#endif -#if defined(LDAP_CONTROL_X_INCREMENTAL_VALUES) REGISTER_STRING_CONSTANT("LDAP_CONTROL_X_DOMAIN_SCOPE", LDAP_CONTROL_X_DOMAIN_SCOPE, CONST_PERSISTENT); -#endif -#if defined(LDAP_CONTROL_X_INCREMENTAL_VALUES) REGISTER_STRING_CONSTANT("LDAP_CONTROL_X_PERMISSIVE_MODIFY", LDAP_CONTROL_X_PERMISSIVE_MODIFY, CONST_PERSISTENT); -#endif -#if defined(LDAP_CONTROL_X_INCREMENTAL_VALUES) REGISTER_STRING_CONSTANT("LDAP_CONTROL_X_SEARCH_OPTIONS", LDAP_CONTROL_X_SEARCH_OPTIONS, CONST_PERSISTENT); -#endif -#if defined(LDAP_CONTROL_X_INCREMENTAL_VALUES) REGISTER_STRING_CONSTANT("LDAP_CONTROL_X_TREE_DELETE", LDAP_CONTROL_X_TREE_DELETE, CONST_PERSISTENT); -#endif -#if defined(LDAP_CONTROL_X_INCREMENTAL_VALUES) REGISTER_STRING_CONSTANT("LDAP_CONTROL_X_EXTENDED_DN", LDAP_CONTROL_X_EXTENDED_DN, CONST_PERSISTENT); #endif #if defined(LDAP_CONTROL_VLVREQUEST) REGISTER_STRING_CONSTANT("LDAP_CONTROL_VLVREQUEST", LDAP_CONTROL_VLVREQUEST, CONST_PERSISTENT); -#endif -#if defined(LDAP_CONTROL_VLVREQUEST) REGISTER_STRING_CONSTANT("LDAP_CONTROL_VLVRESPONSE", LDAP_CONTROL_VLVRESPONSE, CONST_PERSISTENT); #endif diff --git a/ext/odbc/odbc_arginfo.h b/ext/odbc/odbc_arginfo.h index 91df4da846d49..d458fee43383b 100644 --- a/ext/odbc/odbc_arginfo.h +++ b/ext/odbc/odbc_arginfo.h @@ -410,53 +410,21 @@ static void register_odbc_symbols(int module_number) REGISTER_LONG_CONSTANT("SQL_TIMESTAMP", SQL_TIMESTAMP, CONST_PERSISTENT); #if (defined(ODBCVER) && (ODBCVER >= 0x0300)) REGISTER_LONG_CONSTANT("SQL_TYPE_DATE", SQL_TYPE_DATE, CONST_PERSISTENT); -#endif -#if (defined(ODBCVER) && (ODBCVER >= 0x0300)) REGISTER_LONG_CONSTANT("SQL_TYPE_TIME", SQL_TYPE_TIME, CONST_PERSISTENT); -#endif -#if (defined(ODBCVER) && (ODBCVER >= 0x0300)) REGISTER_LONG_CONSTANT("SQL_TYPE_TIMESTAMP", SQL_TYPE_TIMESTAMP, CONST_PERSISTENT); -#endif -#if (defined(ODBCVER) && (ODBCVER >= 0x0300)) REGISTER_LONG_CONSTANT("SQL_WCHAR", SQL_WCHAR, CONST_PERSISTENT); -#endif -#if (defined(ODBCVER) && (ODBCVER >= 0x0300)) REGISTER_LONG_CONSTANT("SQL_WVARCHAR", SQL_WVARCHAR, CONST_PERSISTENT); -#endif -#if (defined(ODBCVER) && (ODBCVER >= 0x0300)) REGISTER_LONG_CONSTANT("SQL_WLONGVARCHAR", SQL_WLONGVARCHAR, CONST_PERSISTENT); -#endif -#if (defined(ODBCVER) && (ODBCVER >= 0x0300)) REGISTER_LONG_CONSTANT("SQL_BEST_ROWID", SQL_BEST_ROWID, CONST_PERSISTENT); -#endif -#if (defined(ODBCVER) && (ODBCVER >= 0x0300)) REGISTER_LONG_CONSTANT("SQL_ROWVER", SQL_ROWVER, CONST_PERSISTENT); -#endif -#if (defined(ODBCVER) && (ODBCVER >= 0x0300)) REGISTER_LONG_CONSTANT("SQL_SCOPE_CURROW", SQL_SCOPE_CURROW, CONST_PERSISTENT); -#endif -#if (defined(ODBCVER) && (ODBCVER >= 0x0300)) REGISTER_LONG_CONSTANT("SQL_SCOPE_TRANSACTION", SQL_SCOPE_TRANSACTION, CONST_PERSISTENT); -#endif -#if (defined(ODBCVER) && (ODBCVER >= 0x0300)) REGISTER_LONG_CONSTANT("SQL_SCOPE_SESSION", SQL_SCOPE_SESSION, CONST_PERSISTENT); -#endif -#if (defined(ODBCVER) && (ODBCVER >= 0x0300)) REGISTER_LONG_CONSTANT("SQL_NO_NULLS", SQL_NO_NULLS, CONST_PERSISTENT); -#endif -#if (defined(ODBCVER) && (ODBCVER >= 0x0300)) REGISTER_LONG_CONSTANT("SQL_NULLABLE", SQL_NULLABLE, CONST_PERSISTENT); -#endif -#if (defined(ODBCVER) && (ODBCVER >= 0x0300)) REGISTER_LONG_CONSTANT("SQL_INDEX_UNIQUE", SQL_INDEX_UNIQUE, CONST_PERSISTENT); -#endif -#if (defined(ODBCVER) && (ODBCVER >= 0x0300)) REGISTER_LONG_CONSTANT("SQL_INDEX_ALL", SQL_INDEX_ALL, CONST_PERSISTENT); -#endif -#if (defined(ODBCVER) && (ODBCVER >= 0x0300)) REGISTER_LONG_CONSTANT("SQL_ENSURE", SQL_ENSURE, CONST_PERSISTENT); -#endif -#if (defined(ODBCVER) && (ODBCVER >= 0x0300)) REGISTER_LONG_CONSTANT("SQL_QUICK", SQL_QUICK, CONST_PERSISTENT); #endif diff --git a/ext/openssl/openssl_arginfo.h b/ext/openssl/openssl_arginfo.h index 94f59ce268510..fb53dcae903e7 100644 --- a/ext/openssl/openssl_arginfo.h +++ b/ext/openssl/openssl_arginfo.h @@ -600,26 +600,16 @@ static void register_openssl_symbols(int module_number) REGISTER_STRING_CONSTANT("OPENSSL_DEFAULT_STREAM_CIPHERS", OPENSSL_DEFAULT_STREAM_CIPHERS, CONST_PERSISTENT); #if !defined(OPENSSL_NO_RC2) REGISTER_LONG_CONSTANT("OPENSSL_CIPHER_RC2_40", PHP_OPENSSL_CIPHER_RC2_40, CONST_PERSISTENT); -#endif -#if !defined(OPENSSL_NO_RC2) REGISTER_LONG_CONSTANT("OPENSSL_CIPHER_RC2_128", PHP_OPENSSL_CIPHER_RC2_128, CONST_PERSISTENT); -#endif -#if !defined(OPENSSL_NO_RC2) REGISTER_LONG_CONSTANT("OPENSSL_CIPHER_RC2_64", PHP_OPENSSL_CIPHER_RC2_64, CONST_PERSISTENT); #endif #if !defined(OPENSSL_NO_DES) REGISTER_LONG_CONSTANT("OPENSSL_CIPHER_DES", PHP_OPENSSL_CIPHER_DES, CONST_PERSISTENT); -#endif -#if !defined(OPENSSL_NO_DES) REGISTER_LONG_CONSTANT("OPENSSL_CIPHER_3DES", PHP_OPENSSL_CIPHER_3DES, CONST_PERSISTENT); #endif #if !defined(OPENSSL_NO_AES) REGISTER_LONG_CONSTANT("OPENSSL_CIPHER_AES_128_CBC", PHP_OPENSSL_CIPHER_AES_128_CBC, CONST_PERSISTENT); -#endif -#if !defined(OPENSSL_NO_AES) REGISTER_LONG_CONSTANT("OPENSSL_CIPHER_AES_192_CBC", PHP_OPENSSL_CIPHER_AES_192_CBC, CONST_PERSISTENT); -#endif -#if !defined(OPENSSL_NO_AES) REGISTER_LONG_CONSTANT("OPENSSL_CIPHER_AES_256_CBC", PHP_OPENSSL_CIPHER_AES_256_CBC, CONST_PERSISTENT); #endif REGISTER_LONG_CONSTANT("OPENSSL_KEYTYPE_RSA", OPENSSL_KEYTYPE_RSA, CONST_PERSISTENT); @@ -632,14 +622,8 @@ static void register_openssl_symbols(int module_number) #endif #if PHP_OPENSSL_API_VERSION >= 0x30000 REGISTER_LONG_CONSTANT("OPENSSL_KEYTYPE_X25519", OPENSSL_KEYTYPE_X25519, CONST_PERSISTENT); -#endif -#if PHP_OPENSSL_API_VERSION >= 0x30000 REGISTER_LONG_CONSTANT("OPENSSL_KEYTYPE_ED25519", OPENSSL_KEYTYPE_ED25519, CONST_PERSISTENT); -#endif -#if PHP_OPENSSL_API_VERSION >= 0x30000 REGISTER_LONG_CONSTANT("OPENSSL_KEYTYPE_X448", OPENSSL_KEYTYPE_X448, CONST_PERSISTENT); -#endif -#if PHP_OPENSSL_API_VERSION >= 0x30000 REGISTER_LONG_CONSTANT("OPENSSL_KEYTYPE_ED448", OPENSSL_KEYTYPE_ED448, CONST_PERSISTENT); #endif REGISTER_LONG_CONSTANT("OPENSSL_RAW_DATA", OPENSSL_RAW_DATA, CONST_PERSISTENT); diff --git a/ext/openssl/openssl_pwhash_arginfo.h b/ext/openssl/openssl_pwhash_arginfo.h index 7696675a5a84f..b4213158a169a 100644 --- a/ext/openssl/openssl_pwhash_arginfo.h +++ b/ext/openssl/openssl_pwhash_arginfo.h @@ -5,20 +5,10 @@ static void register_openssl_pwhash_symbols(int module_number) { #if defined(HAVE_OPENSSL_ARGON2) REGISTER_STRING_CONSTANT("PASSWORD_ARGON2I", "argon2i", CONST_PERSISTENT); -#endif -#if defined(HAVE_OPENSSL_ARGON2) REGISTER_STRING_CONSTANT("PASSWORD_ARGON2ID", "argon2id", CONST_PERSISTENT); -#endif -#if defined(HAVE_OPENSSL_ARGON2) REGISTER_LONG_CONSTANT("PASSWORD_ARGON2_DEFAULT_MEMORY_COST", PHP_OPENSSL_PWHASH_MEMLIMIT, CONST_PERSISTENT); -#endif -#if defined(HAVE_OPENSSL_ARGON2) REGISTER_LONG_CONSTANT("PASSWORD_ARGON2_DEFAULT_TIME_COST", PHP_OPENSSL_PWHASH_ITERLIMIT, CONST_PERSISTENT); -#endif -#if defined(HAVE_OPENSSL_ARGON2) REGISTER_LONG_CONSTANT("PASSWORD_ARGON2_DEFAULT_THREADS", PHP_OPENSSL_PWHASH_THREADS, CONST_PERSISTENT); -#endif -#if defined(HAVE_OPENSSL_ARGON2) REGISTER_STRING_CONSTANT("PASSWORD_ARGON2_PROVIDER", "openssl", CONST_PERSISTENT); #endif } diff --git a/ext/pcntl/pcntl_arginfo.h b/ext/pcntl/pcntl_arginfo.h index 8da7cb70a6aa7..bc6581d41e17e 100644 --- a/ext/pcntl/pcntl_arginfo.h +++ b/ext/pcntl/pcntl_arginfo.h @@ -317,11 +317,7 @@ static void register_pcntl_symbols(int module_number) #endif #if defined(HAVE_WAITID) && defined(HAVE_POSIX_IDTYPES) REGISTER_LONG_CONSTANT("P_ALL", LONG_CONST(P_ALL), CONST_PERSISTENT); -#endif -#if defined(HAVE_WAITID) && defined(HAVE_POSIX_IDTYPES) REGISTER_LONG_CONSTANT("P_PID", LONG_CONST(P_PID), CONST_PERSISTENT); -#endif -#if defined(HAVE_WAITID) && defined(HAVE_POSIX_IDTYPES) REGISTER_LONG_CONSTANT("P_PGID", LONG_CONST(P_PGID), CONST_PERSISTENT); #endif #if defined(HAVE_WAITID) && defined(HAVE_LINUX_IDTYPES) @@ -329,11 +325,7 @@ static void register_pcntl_symbols(int module_number) #endif #if defined(HAVE_WAITID) && defined(HAVE_NETBSD_IDTYPES) REGISTER_LONG_CONSTANT("P_UID", LONG_CONST(P_UID), CONST_PERSISTENT); -#endif -#if defined(HAVE_WAITID) && defined(HAVE_NETBSD_IDTYPES) REGISTER_LONG_CONSTANT("P_GID", LONG_CONST(P_GID), CONST_PERSISTENT); -#endif -#if defined(HAVE_WAITID) && defined(HAVE_NETBSD_IDTYPES) REGISTER_LONG_CONSTANT("P_SID", LONG_CONST(P_SID), CONST_PERSISTENT); #endif #if defined(HAVE_WAITID) && defined(HAVE_FREEBSD_IDTYPES) @@ -394,8 +386,6 @@ static void register_pcntl_symbols(int module_number) #endif #if defined(SIGSYS) REGISTER_LONG_CONSTANT("SIGSYS", LONG_CONST(SIGSYS), CONST_PERSISTENT); -#endif -#if defined(SIGSYS) REGISTER_LONG_CONSTANT("SIGBABY", LONG_CONST(SIGSYS), CONST_PERSISTENT); #endif #if defined(SIGCKPT) @@ -412,26 +402,16 @@ static void register_pcntl_symbols(int module_number) #endif #if (defined(HAVE_GETPRIORITY) || defined(HAVE_SETPRIORITY)) REGISTER_LONG_CONSTANT("PRIO_PGRP", PRIO_PGRP, CONST_PERSISTENT); -#endif -#if (defined(HAVE_GETPRIORITY) || defined(HAVE_SETPRIORITY)) REGISTER_LONG_CONSTANT("PRIO_USER", PRIO_USER, CONST_PERSISTENT); -#endif -#if (defined(HAVE_GETPRIORITY) || defined(HAVE_SETPRIORITY)) REGISTER_LONG_CONSTANT("PRIO_PROCESS", PRIO_PROCESS, CONST_PERSISTENT); #endif #if (defined(HAVE_GETPRIORITY) || defined(HAVE_SETPRIORITY)) && defined(PRIO_DARWIN_BG) REGISTER_LONG_CONSTANT("PRIO_DARWIN_BG", PRIO_DARWIN_BG, CONST_PERSISTENT); -#endif -#if (defined(HAVE_GETPRIORITY) || defined(HAVE_SETPRIORITY)) && defined(PRIO_DARWIN_BG) REGISTER_LONG_CONSTANT("PRIO_DARWIN_THREAD", PRIO_DARWIN_THREAD, CONST_PERSISTENT); #endif #if defined(HAVE_SIGPROCMASK) REGISTER_LONG_CONSTANT("SIG_BLOCK", SIG_BLOCK, CONST_PERSISTENT); -#endif -#if defined(HAVE_SIGPROCMASK) REGISTER_LONG_CONSTANT("SIG_UNBLOCK", SIG_UNBLOCK, CONST_PERSISTENT); -#endif -#if defined(HAVE_SIGPROCMASK) REGISTER_LONG_CONSTANT("SIG_SETMASK", SIG_SETMASK, CONST_PERSISTENT); #endif #if (defined(HAVE_SIGWAITINFO) && defined(HAVE_SIGTIMEDWAIT)) @@ -445,14 +425,8 @@ static void register_pcntl_symbols(int module_number) #endif #if (defined(HAVE_SIGWAITINFO) && defined(HAVE_SIGTIMEDWAIT)) REGISTER_LONG_CONSTANT("SI_QUEUE", SI_QUEUE, CONST_PERSISTENT); -#endif -#if (defined(HAVE_SIGWAITINFO) && defined(HAVE_SIGTIMEDWAIT)) REGISTER_LONG_CONSTANT("SI_TIMER", SI_TIMER, CONST_PERSISTENT); -#endif -#if (defined(HAVE_SIGWAITINFO) && defined(HAVE_SIGTIMEDWAIT)) REGISTER_LONG_CONSTANT("SI_MESGQ", SI_MESGQ, CONST_PERSISTENT); -#endif -#if (defined(HAVE_SIGWAITINFO) && defined(HAVE_SIGTIMEDWAIT)) REGISTER_LONG_CONSTANT("SI_ASYNCIO", SI_ASYNCIO, CONST_PERSISTENT); #endif #if (defined(HAVE_SIGWAITINFO) && defined(HAVE_SIGTIMEDWAIT)) && defined(SI_SIGIO) @@ -610,8 +584,6 @@ static void register_pcntl_symbols(int module_number) #endif #if defined(HAVE_FORKX) REGISTER_LONG_CONSTANT("FORK_NOSIGCHLD", FORK_NOSIGCHLD, CONST_PERSISTENT); -#endif -#if defined(HAVE_FORKX) REGISTER_LONG_CONSTANT("FORK_WAITPID", FORK_WAITPID, CONST_PERSISTENT); #endif #if defined(EINTR) diff --git a/ext/pdo_mysql/pdo_mysql_arginfo.h b/ext/pdo_mysql/pdo_mysql_arginfo.h index a6a1b5e1b5c78..0e9c04bd3cfd3 100644 --- a/ext/pdo_mysql/pdo_mysql_arginfo.h +++ b/ext/pdo_mysql/pdo_mysql_arginfo.h @@ -42,16 +42,12 @@ static zend_class_entry *register_class_Pdo_Mysql(zend_class_entry *class_entry_ zend_string *const_ATTR_MAX_BUFFER_SIZE_name = zend_string_init_interned("ATTR_MAX_BUFFER_SIZE", sizeof("ATTR_MAX_BUFFER_SIZE") - 1, 1); zend_declare_typed_class_constant(class_entry, const_ATTR_MAX_BUFFER_SIZE_name, &const_ATTR_MAX_BUFFER_SIZE_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release(const_ATTR_MAX_BUFFER_SIZE_name); -#endif -#if !defined(PDO_USE_MYSQLND) zval const_ATTR_READ_DEFAULT_FILE_value; ZVAL_LONG(&const_ATTR_READ_DEFAULT_FILE_value, PDO_MYSQL_ATTR_READ_DEFAULT_FILE); zend_string *const_ATTR_READ_DEFAULT_FILE_name = zend_string_init_interned("ATTR_READ_DEFAULT_FILE", sizeof("ATTR_READ_DEFAULT_FILE") - 1, 1); zend_declare_typed_class_constant(class_entry, const_ATTR_READ_DEFAULT_FILE_name, &const_ATTR_READ_DEFAULT_FILE_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release(const_ATTR_READ_DEFAULT_FILE_name); -#endif -#if !defined(PDO_USE_MYSQLND) zval const_ATTR_READ_DEFAULT_GROUP_value; ZVAL_LONG(&const_ATTR_READ_DEFAULT_GROUP_value, PDO_MYSQL_ATTR_READ_DEFAULT_GROUP); diff --git a/ext/pdo_sqlite/pdo_sqlite_arginfo.h b/ext/pdo_sqlite/pdo_sqlite_arginfo.h index 02cf12673ce53..e2cd71723706e 100644 --- a/ext/pdo_sqlite/pdo_sqlite_arginfo.h +++ b/ext/pdo_sqlite/pdo_sqlite_arginfo.h @@ -128,16 +128,12 @@ static zend_class_entry *register_class_Pdo_Sqlite(zend_class_entry *class_entry zend_string *const_EXPLAIN_MODE_PREPARED_name = zend_string_init_interned("EXPLAIN_MODE_PREPARED", sizeof("EXPLAIN_MODE_PREPARED") - 1, 1); zend_declare_typed_class_constant(class_entry, const_EXPLAIN_MODE_PREPARED_name, &const_EXPLAIN_MODE_PREPARED_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release(const_EXPLAIN_MODE_PREPARED_name); -#endif -#if SQLITE_VERSION_NUMBER >= 3043000 zval const_EXPLAIN_MODE_EXPLAIN_value; ZVAL_LONG(&const_EXPLAIN_MODE_EXPLAIN_value, 1); zend_string *const_EXPLAIN_MODE_EXPLAIN_name = zend_string_init_interned("EXPLAIN_MODE_EXPLAIN", sizeof("EXPLAIN_MODE_EXPLAIN") - 1, 1); zend_declare_typed_class_constant(class_entry, const_EXPLAIN_MODE_EXPLAIN_name, &const_EXPLAIN_MODE_EXPLAIN_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release(const_EXPLAIN_MODE_EXPLAIN_name); -#endif -#if SQLITE_VERSION_NUMBER >= 3043000 zval const_EXPLAIN_MODE_EXPLAIN_QUERY_PLAN_value; ZVAL_LONG(&const_EXPLAIN_MODE_EXPLAIN_QUERY_PLAN_value, 2); diff --git a/ext/sockets/sockets_arginfo.h b/ext/sockets/sockets_arginfo.h index 4be006bc7f0af..b7af6eb6b89ac 100644 --- a/ext/sockets/sockets_arginfo.h +++ b/ext/sockets/sockets_arginfo.h @@ -413,17 +413,9 @@ static void register_sockets_symbols(int module_number) #endif #if defined(SO_USER_COOKIE) REGISTER_LONG_CONSTANT("SO_LABEL", SO_LABEL, CONST_PERSISTENT); -#endif -#if defined(SO_USER_COOKIE) REGISTER_LONG_CONSTANT("SO_PEERLABEL", SO_PEERLABEL, CONST_PERSISTENT); -#endif -#if defined(SO_USER_COOKIE) REGISTER_LONG_CONSTANT("SO_LISTENQLIMIT", SO_LISTENQLIMIT, CONST_PERSISTENT); -#endif -#if defined(SO_USER_COOKIE) REGISTER_LONG_CONSTANT("SO_LISTENQLEN", SO_LISTENQLEN, CONST_PERSISTENT); -#endif -#if defined(SO_USER_COOKIE) REGISTER_LONG_CONSTANT("SO_USER_COOKIE", SO_USER_COOKIE, CONST_PERSISTENT); #endif #if defined(SO_SETFIB) @@ -443,11 +435,7 @@ static void register_sockets_symbols(int module_number) #endif #if defined(SOL_FILTER) REGISTER_LONG_CONSTANT("SOL_FILTER", SOL_FILTER, CONST_PERSISTENT); -#endif -#if defined(SOL_FILTER) REGISTER_LONG_CONSTANT("FIL_ATTACH", FIL_ATTACH, CONST_PERSISTENT); -#endif -#if defined(SOL_FILTER) REGISTER_LONG_CONSTANT("FIL_DETACH", FIL_DETACH, CONST_PERSISTENT); #endif #if defined(SO_DONTTRUNC) @@ -556,11 +544,7 @@ static void register_sockets_symbols(int module_number) #endif #if defined(TCP_KEEPIDLE) REGISTER_LONG_CONSTANT("TCP_KEEPIDLE", TCP_KEEPIDLE, CONST_PERSISTENT); -#endif -#if defined(TCP_KEEPIDLE) REGISTER_LONG_CONSTANT("TCP_KEEPINTVL", TCP_KEEPINTVL, CONST_PERSISTENT); -#endif -#if defined(TCP_KEEPIDLE) REGISTER_LONG_CONSTANT("TCP_KEEPCNT", TCP_KEEPCNT, CONST_PERSISTENT); #endif #if defined(TCP_FUNCTION_BLK) @@ -571,11 +555,7 @@ static void register_sockets_symbols(int module_number) #endif #if defined(TCP_REUSPORT_LB_NUMA) REGISTER_LONG_CONSTANT("TCP_REUSPORT_LB_NUMA", TCP_REUSPORT_LB_NUMA, CONST_PERSISTENT); -#endif -#if defined(TCP_REUSPORT_LB_NUMA) REGISTER_LONG_CONSTANT("TCP_REUSPORT_LB_NUMA_NODOM", TCP_REUSPORT_LB_NUMA_NODOM, CONST_PERSISTENT); -#endif -#if defined(TCP_REUSPORT_LB_NUMA) REGISTER_LONG_CONSTANT("TCP_REUSPORT_LB_NUMA_CURDOM", TCP_REUSPORT_LB_NUMA_CURDOM, CONST_PERSISTENT); #endif #if defined(TCP_BBR_ALGORITHM) @@ -587,14 +567,8 @@ static void register_sockets_symbols(int module_number) REGISTER_LONG_CONSTANT("MCAST_LEAVE_GROUP", PHP_MCAST_LEAVE_GROUP, CONST_PERSISTENT); #if defined(HAS_MCAST_EXT) REGISTER_LONG_CONSTANT("MCAST_BLOCK_SOURCE", PHP_MCAST_BLOCK_SOURCE, CONST_PERSISTENT); -#endif -#if defined(HAS_MCAST_EXT) REGISTER_LONG_CONSTANT("MCAST_UNBLOCK_SOURCE", PHP_MCAST_UNBLOCK_SOURCE, CONST_PERSISTENT); -#endif -#if defined(HAS_MCAST_EXT) REGISTER_LONG_CONSTANT("MCAST_JOIN_SOURCE_GROUP", PHP_MCAST_JOIN_SOURCE_GROUP, CONST_PERSISTENT); -#endif -#if defined(HAS_MCAST_EXT) REGISTER_LONG_CONSTANT("MCAST_LEAVE_SOURCE_GROUP", PHP_MCAST_LEAVE_SOURCE_GROUP, CONST_PERSISTENT); #endif REGISTER_LONG_CONSTANT("IP_MULTICAST_IF", IP_MULTICAST_IF, CONST_PERSISTENT); @@ -605,11 +579,7 @@ static void register_sockets_symbols(int module_number) #endif #if defined(HAVE_IPV6) REGISTER_LONG_CONSTANT("IPV6_MULTICAST_IF", IPV6_MULTICAST_IF, CONST_PERSISTENT); -#endif -#if defined(HAVE_IPV6) REGISTER_LONG_CONSTANT("IPV6_MULTICAST_HOPS", IPV6_MULTICAST_HOPS, CONST_PERSISTENT); -#endif -#if defined(HAVE_IPV6) REGISTER_LONG_CONSTANT("IPV6_MULTICAST_LOOP", IPV6_MULTICAST_LOOP, CONST_PERSISTENT); #endif #if defined(IPV6_V6ONLY) @@ -617,14 +587,8 @@ static void register_sockets_symbols(int module_number) #endif #if defined(IP_PORTRANGE) REGISTER_LONG_CONSTANT("IP_PORTRANGE", IP_PORTRANGE, CONST_PERSISTENT); -#endif -#if defined(IP_PORTRANGE) REGISTER_LONG_CONSTANT("IP_PORTRANGE_DEFAULT", IP_PORTRANGE_DEFAULT, CONST_PERSISTENT); -#endif -#if defined(IP_PORTRANGE) REGISTER_LONG_CONSTANT("IP_PORTRANGE_HIGH", IP_PORTRANGE_HIGH, CONST_PERSISTENT); -#endif -#if defined(IP_PORTRANGE) REGISTER_LONG_CONSTANT("IP_PORTRANGE_LOW", IP_PORTRANGE_LOW, CONST_PERSISTENT); #endif #if defined(EPERM) @@ -932,32 +896,14 @@ static void register_sockets_symbols(int module_number) #endif #if defined(PHP_WIN32) REGISTER_LONG_CONSTANT("SOCKET_ESTALE", WSAESTALE, CONST_PERSISTENT); -#endif -#if defined(PHP_WIN32) REGISTER_LONG_CONSTANT("SOCKET_EDISCON", WSAEDISCON, CONST_PERSISTENT); -#endif -#if defined(PHP_WIN32) REGISTER_LONG_CONSTANT("SOCKET_SYSNOTREADY", WSASYSNOTREADY, CONST_PERSISTENT); -#endif -#if defined(PHP_WIN32) REGISTER_LONG_CONSTANT("SOCKET_VERNOTSUPPORTED", WSAVERNOTSUPPORTED, CONST_PERSISTENT); -#endif -#if defined(PHP_WIN32) REGISTER_LONG_CONSTANT("SOCKET_NOTINITIALISED", WSANOTINITIALISED, CONST_PERSISTENT); -#endif -#if defined(PHP_WIN32) REGISTER_LONG_CONSTANT("SOCKET_HOST_NOT_FOUND", WSAHOST_NOT_FOUND, CONST_PERSISTENT); -#endif -#if defined(PHP_WIN32) REGISTER_LONG_CONSTANT("SOCKET_TRY_AGAIN", WSATRY_AGAIN, CONST_PERSISTENT); -#endif -#if defined(PHP_WIN32) REGISTER_LONG_CONSTANT("SOCKET_NO_RECOVERY", WSANO_RECOVERY, CONST_PERSISTENT); -#endif -#if defined(PHP_WIN32) REGISTER_LONG_CONSTANT("SOCKET_NO_DATA", WSANO_DATA, CONST_PERSISTENT); -#endif -#if defined(PHP_WIN32) REGISTER_LONG_CONSTANT("SOCKET_NO_ADDRESS", WSANO_ADDRESS, CONST_PERSISTENT); #endif REGISTER_LONG_CONSTANT("IPPROTO_IP", IPPROTO_IP, CONST_PERSISTENT); @@ -990,8 +936,6 @@ static void register_sockets_symbols(int module_number) REGISTER_LONG_CONSTANT("AI_ADDRCONFIG", AI_ADDRCONFIG, CONST_PERSISTENT); #if defined(AI_IDN) REGISTER_LONG_CONSTANT("AI_IDN", AI_IDN, CONST_PERSISTENT); -#endif -#if defined(AI_IDN) REGISTER_LONG_CONSTANT("AI_CANONIDN", AI_CANONIDN, CONST_PERSISTENT); #endif #if defined(AI_NUMERICSERV) @@ -1002,20 +946,14 @@ static void register_sockets_symbols(int module_number) #endif #if (defined(IPV6_RECVPKTINFO) && defined(HAVE_IPV6)) REGISTER_LONG_CONSTANT("IPV6_RECVPKTINFO", IPV6_RECVPKTINFO, CONST_PERSISTENT); -#endif -#if (defined(IPV6_RECVPKTINFO) && defined(HAVE_IPV6)) REGISTER_LONG_CONSTANT("IPV6_PKTINFO", IPV6_PKTINFO, CONST_PERSISTENT); #endif #if (defined(IPV6_RECVHOPLIMIT) && defined(HAVE_IPV6)) REGISTER_LONG_CONSTANT("IPV6_RECVHOPLIMIT", IPV6_RECVHOPLIMIT, CONST_PERSISTENT); -#endif -#if (defined(IPV6_RECVHOPLIMIT) && defined(HAVE_IPV6)) REGISTER_LONG_CONSTANT("IPV6_HOPLIMIT", IPV6_HOPLIMIT, CONST_PERSISTENT); #endif #if (defined(IPV6_RECVTCLASS) && defined(HAVE_IPV6)) REGISTER_LONG_CONSTANT("IPV6_RECVTCLASS", IPV6_RECVTCLASS, CONST_PERSISTENT); -#endif -#if (defined(IPV6_RECVTCLASS) && defined(HAVE_IPV6)) REGISTER_LONG_CONSTANT("IPV6_TCLASS", IPV6_TCLASS, CONST_PERSISTENT); #endif #if defined(SCM_RIGHTS) @@ -1032,14 +970,10 @@ static void register_sockets_symbols(int module_number) #endif #if defined(LOCAL_CREDS_PERSISTENT) REGISTER_LONG_CONSTANT("SCM_CREDS2", SCM_CREDS2, CONST_PERSISTENT); -#endif -#if defined(LOCAL_CREDS_PERSISTENT) REGISTER_LONG_CONSTANT("LOCAL_CREDS_PERSISTENT", LOCAL_CREDS_PERSISTENT, CONST_PERSISTENT); #endif #if (!defined(LOCAL_CREDS_PERSISTENT) && defined(LOCAL_CREDS)) REGISTER_LONG_CONSTANT("SCM_CREDS", SCM_CREDS, CONST_PERSISTENT); -#endif -#if (!defined(LOCAL_CREDS_PERSISTENT) && defined(LOCAL_CREDS)) REGISTER_LONG_CONSTANT("LOCAL_CREDS", LOCAL_CREDS, CONST_PERSISTENT); #endif #if defined(SO_ATTACH_REUSEPORT_CBPF) @@ -1098,14 +1032,8 @@ static void register_sockets_symbols(int module_number) #endif #if defined(ETH_P_ALL) REGISTER_LONG_CONSTANT("ETH_P_IP", ETH_P_IP, CONST_PERSISTENT); -#endif -#if defined(ETH_P_ALL) REGISTER_LONG_CONSTANT("ETH_P_IPV6", ETH_P_IPV6, CONST_PERSISTENT); -#endif -#if defined(ETH_P_ALL) REGISTER_LONG_CONSTANT("ETH_P_LOOP", ETH_P_LOOP, CONST_PERSISTENT); -#endif -#if defined(ETH_P_ALL) REGISTER_LONG_CONSTANT("ETH_P_ALL", ETH_P_ALL, CONST_PERSISTENT); #endif #if defined(UDP_SEGMENT) @@ -1113,11 +1041,7 @@ static void register_sockets_symbols(int module_number) #endif #if defined(SHUT_RDWR) REGISTER_LONG_CONSTANT("SHUT_RD", SHUT_RD, CONST_PERSISTENT); -#endif -#if defined(SHUT_RDWR) REGISTER_LONG_CONSTANT("SHUT_WR", SHUT_WR, CONST_PERSISTENT); -#endif -#if defined(SHUT_RDWR) REGISTER_LONG_CONSTANT("SHUT_RDWR", SHUT_RDWR, CONST_PERSISTENT); #endif } diff --git a/ext/sodium/libsodium_arginfo.h b/ext/sodium/libsodium_arginfo.h index f91a7c5dfe0d9..5fbd831c22e4a 100644 --- a/ext/sodium/libsodium_arginfo.h +++ b/ext/sodium/libsodium_arginfo.h @@ -762,38 +762,20 @@ static void register_libsodium_symbols(int module_number) REGISTER_LONG_CONSTANT("SODIUM_LIBRARY_MINOR_VERSION", sodium_library_version_minor(), CONST_PERSISTENT); #if defined(HAVE_AESGCM) REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_AEAD_AES256GCM_KEYBYTES", crypto_aead_aes256gcm_KEYBYTES, CONST_PERSISTENT); -#endif -#if defined(HAVE_AESGCM) REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_AEAD_AES256GCM_NSECBYTES", crypto_aead_aes256gcm_NSECBYTES, CONST_PERSISTENT); -#endif -#if defined(HAVE_AESGCM) REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_AEAD_AES256GCM_NPUBBYTES", crypto_aead_aes256gcm_NPUBBYTES, CONST_PERSISTENT); -#endif -#if defined(HAVE_AESGCM) REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_AEAD_AES256GCM_ABYTES", crypto_aead_aes256gcm_ABYTES, CONST_PERSISTENT); #endif #if defined(crypto_aead_aegis128l_KEYBYTES) REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_AEAD_AEGIS128L_KEYBYTES", crypto_aead_aegis128l_KEYBYTES, CONST_PERSISTENT); -#endif -#if defined(crypto_aead_aegis128l_KEYBYTES) REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_AEAD_AEGIS128L_NSECBYTES", crypto_aead_aegis128l_NSECBYTES, CONST_PERSISTENT); -#endif -#if defined(crypto_aead_aegis128l_KEYBYTES) REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_AEAD_AEGIS128L_NPUBBYTES", crypto_aead_aegis128l_NPUBBYTES, CONST_PERSISTENT); -#endif -#if defined(crypto_aead_aegis128l_KEYBYTES) REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_AEAD_AEGIS128L_ABYTES", crypto_aead_aegis128l_ABYTES, CONST_PERSISTENT); #endif #if defined(crypto_aead_aegis256_KEYBYTES) REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_AEAD_AEGIS256_KEYBYTES", crypto_aead_aegis256_KEYBYTES, CONST_PERSISTENT); -#endif -#if defined(crypto_aead_aegis256_KEYBYTES) REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_AEAD_AEGIS256_NSECBYTES", crypto_aead_aegis256_NSECBYTES, CONST_PERSISTENT); -#endif -#if defined(crypto_aead_aegis256_KEYBYTES) REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_AEAD_AEGIS256_NPUBBYTES", crypto_aead_aegis256_NPUBBYTES, CONST_PERSISTENT); -#endif -#if defined(crypto_aead_aegis256_KEYBYTES) REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_AEAD_AEGIS256_ABYTES", crypto_aead_aegis256_ABYTES, CONST_PERSISTENT); #endif REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_AEAD_CHACHA20POLY1305_KEYBYTES", crypto_aead_chacha20poly1305_KEYBYTES, CONST_PERSISTENT); @@ -806,14 +788,8 @@ static void register_libsodium_symbols(int module_number) REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_AEAD_CHACHA20POLY1305_IETF_ABYTES", crypto_aead_chacha20poly1305_IETF_ABYTES, CONST_PERSISTENT); #if defined(crypto_aead_xchacha20poly1305_IETF_NPUBBYTES) REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES", crypto_aead_xchacha20poly1305_IETF_KEYBYTES, CONST_PERSISTENT); -#endif -#if defined(crypto_aead_xchacha20poly1305_IETF_NPUBBYTES) REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NSECBYTES", crypto_aead_xchacha20poly1305_IETF_NSECBYTES, CONST_PERSISTENT); -#endif -#if defined(crypto_aead_xchacha20poly1305_IETF_NPUBBYTES) REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES", crypto_aead_xchacha20poly1305_IETF_NPUBBYTES, CONST_PERSISTENT); -#endif -#if defined(crypto_aead_xchacha20poly1305_IETF_NPUBBYTES) REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES", crypto_aead_xchacha20poly1305_IETF_ABYTES, CONST_PERSISTENT); #endif REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_AUTH_BYTES", crypto_auth_BYTES, CONST_PERSISTENT); @@ -836,26 +812,12 @@ static void register_libsodium_symbols(int module_number) REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_KX_KEYPAIRBYTES", SODIUM_CRYPTO_KX_KEYPAIRBYTES(), CONST_PERSISTENT); #if defined(crypto_secretstream_xchacha20poly1305_ABYTES) REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_ABYTES", crypto_secretstream_xchacha20poly1305_ABYTES, CONST_PERSISTENT); -#endif -#if defined(crypto_secretstream_xchacha20poly1305_ABYTES) REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_HEADERBYTES", crypto_secretstream_xchacha20poly1305_HEADERBYTES, CONST_PERSISTENT); -#endif -#if defined(crypto_secretstream_xchacha20poly1305_ABYTES) REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_KEYBYTES", crypto_secretstream_xchacha20poly1305_KEYBYTES, CONST_PERSISTENT); -#endif -#if defined(crypto_secretstream_xchacha20poly1305_ABYTES) REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_MESSAGEBYTES_MAX", crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX, CONST_PERSISTENT); -#endif -#if defined(crypto_secretstream_xchacha20poly1305_ABYTES) REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_MESSAGE", crypto_secretstream_xchacha20poly1305_TAG_MESSAGE, CONST_PERSISTENT); -#endif -#if defined(crypto_secretstream_xchacha20poly1305_ABYTES) REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_PUSH", crypto_secretstream_xchacha20poly1305_TAG_PUSH, CONST_PERSISTENT); -#endif -#if defined(crypto_secretstream_xchacha20poly1305_ABYTES) REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_REKEY", crypto_secretstream_xchacha20poly1305_TAG_REKEY, CONST_PERSISTENT); -#endif -#if defined(crypto_secretstream_xchacha20poly1305_ABYTES) REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_FINAL", crypto_secretstream_xchacha20poly1305_TAG_FINAL, CONST_PERSISTENT); #endif REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_GENERICHASH_BYTES", crypto_generichash_BYTES, CONST_PERSISTENT); @@ -872,47 +834,21 @@ static void register_libsodium_symbols(int module_number) #endif #if defined(crypto_pwhash_SALTBYTES) REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_PWHASH_ALG_DEFAULT", crypto_pwhash_ALG_DEFAULT, CONST_PERSISTENT); -#endif -#if defined(crypto_pwhash_SALTBYTES) REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_PWHASH_SALTBYTES", crypto_pwhash_SALTBYTES, CONST_PERSISTENT); -#endif -#if defined(crypto_pwhash_SALTBYTES) REGISTER_STRING_CONSTANT("SODIUM_CRYPTO_PWHASH_STRPREFIX", crypto_pwhash_STRPREFIX, CONST_PERSISTENT); -#endif -#if defined(crypto_pwhash_SALTBYTES) REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_PWHASH_OPSLIMIT_INTERACTIVE", crypto_pwhash_opslimit_interactive(), CONST_PERSISTENT); -#endif -#if defined(crypto_pwhash_SALTBYTES) REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_PWHASH_MEMLIMIT_INTERACTIVE", crypto_pwhash_memlimit_interactive(), CONST_PERSISTENT); -#endif -#if defined(crypto_pwhash_SALTBYTES) REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_PWHASH_OPSLIMIT_MODERATE", crypto_pwhash_opslimit_moderate(), CONST_PERSISTENT); -#endif -#if defined(crypto_pwhash_SALTBYTES) REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_PWHASH_MEMLIMIT_MODERATE", crypto_pwhash_memlimit_moderate(), CONST_PERSISTENT); -#endif -#if defined(crypto_pwhash_SALTBYTES) REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_PWHASH_OPSLIMIT_SENSITIVE", crypto_pwhash_opslimit_sensitive(), CONST_PERSISTENT); -#endif -#if defined(crypto_pwhash_SALTBYTES) REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_PWHASH_MEMLIMIT_SENSITIVE", crypto_pwhash_memlimit_sensitive(), CONST_PERSISTENT); #endif #if defined(crypto_pwhash_scryptsalsa208sha256_SALTBYTES) REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_PWHASH_SCRYPTSALSA208SHA256_SALTBYTES", crypto_pwhash_scryptsalsa208sha256_SALTBYTES, CONST_PERSISTENT); -#endif -#if defined(crypto_pwhash_scryptsalsa208sha256_SALTBYTES) REGISTER_STRING_CONSTANT("SODIUM_CRYPTO_PWHASH_SCRYPTSALSA208SHA256_STRPREFIX", crypto_pwhash_scryptsalsa208sha256_STRPREFIX, CONST_PERSISTENT); -#endif -#if defined(crypto_pwhash_scryptsalsa208sha256_SALTBYTES) REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_PWHASH_SCRYPTSALSA208SHA256_OPSLIMIT_INTERACTIVE", crypto_pwhash_scryptsalsa208sha256_opslimit_interactive(), CONST_PERSISTENT); -#endif -#if defined(crypto_pwhash_scryptsalsa208sha256_SALTBYTES) REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_PWHASH_SCRYPTSALSA208SHA256_MEMLIMIT_INTERACTIVE", crypto_pwhash_scryptsalsa208sha256_memlimit_interactive(), CONST_PERSISTENT); -#endif -#if defined(crypto_pwhash_scryptsalsa208sha256_SALTBYTES) REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_PWHASH_SCRYPTSALSA208SHA256_OPSLIMIT_SENSITIVE", crypto_pwhash_scryptsalsa208sha256_opslimit_sensitive(), CONST_PERSISTENT); -#endif -#if defined(crypto_pwhash_scryptsalsa208sha256_SALTBYTES) REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_PWHASH_SCRYPTSALSA208SHA256_MEMLIMIT_SENSITIVE", crypto_pwhash_scryptsalsa208sha256_memlimit_sensitive(), CONST_PERSISTENT); #endif REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_SCALARMULT_BYTES", crypto_scalarmult_BYTES, CONST_PERSISTENT); @@ -931,38 +867,20 @@ static void register_libsodium_symbols(int module_number) REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_STREAM_KEYBYTES", crypto_stream_KEYBYTES, CONST_PERSISTENT); #if defined(crypto_stream_xchacha20_KEYBYTES) REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_STREAM_XCHACHA20_NONCEBYTES", crypto_stream_xchacha20_NONCEBYTES, CONST_PERSISTENT); -#endif -#if defined(crypto_stream_xchacha20_KEYBYTES) REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_STREAM_XCHACHA20_KEYBYTES", crypto_stream_xchacha20_KEYBYTES, CONST_PERSISTENT); #endif #if defined(sodium_base64_VARIANT_ORIGINAL) REGISTER_LONG_CONSTANT("SODIUM_BASE64_VARIANT_ORIGINAL", sodium_base64_VARIANT_ORIGINAL, CONST_PERSISTENT); -#endif -#if defined(sodium_base64_VARIANT_ORIGINAL) REGISTER_LONG_CONSTANT("SODIUM_BASE64_VARIANT_ORIGINAL_NO_PADDING", sodium_base64_VARIANT_ORIGINAL_NO_PADDING, CONST_PERSISTENT); -#endif -#if defined(sodium_base64_VARIANT_ORIGINAL) REGISTER_LONG_CONSTANT("SODIUM_BASE64_VARIANT_URLSAFE", sodium_base64_VARIANT_URLSAFE, CONST_PERSISTENT); -#endif -#if defined(sodium_base64_VARIANT_ORIGINAL) REGISTER_LONG_CONSTANT("SODIUM_BASE64_VARIANT_URLSAFE_NO_PADDING", sodium_base64_VARIANT_URLSAFE_NO_PADDING, CONST_PERSISTENT); #endif #if defined(crypto_core_ristretto255_HASHBYTES) REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_SCALARMULT_RISTRETTO255_BYTES", crypto_scalarmult_ristretto255_BYTES, CONST_PERSISTENT); -#endif -#if defined(crypto_core_ristretto255_HASHBYTES) REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_SCALARMULT_RISTRETTO255_SCALARBYTES", crypto_scalarmult_ristretto255_SCALARBYTES, CONST_PERSISTENT); -#endif -#if defined(crypto_core_ristretto255_HASHBYTES) REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_CORE_RISTRETTO255_BYTES", crypto_core_ristretto255_BYTES, CONST_PERSISTENT); -#endif -#if defined(crypto_core_ristretto255_HASHBYTES) REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_CORE_RISTRETTO255_HASHBYTES", crypto_core_ristretto255_HASHBYTES, CONST_PERSISTENT); -#endif -#if defined(crypto_core_ristretto255_HASHBYTES) REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_CORE_RISTRETTO255_SCALARBYTES", crypto_core_ristretto255_SCALARBYTES, CONST_PERSISTENT); -#endif -#if defined(crypto_core_ristretto255_HASHBYTES) REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_CORE_RISTRETTO255_NONREDUCEDSCALARBYTES", crypto_core_ristretto255_NONREDUCEDSCALARBYTES, CONST_PERSISTENT); #endif diff --git a/ext/sodium/sodium_pwhash_arginfo.h b/ext/sodium/sodium_pwhash_arginfo.h index b33d5b5b96f77..465f3090191d1 100644 --- a/ext/sodium/sodium_pwhash_arginfo.h +++ b/ext/sodium/sodium_pwhash_arginfo.h @@ -5,20 +5,10 @@ static void register_sodium_pwhash_symbols(int module_number) { #if SODIUM_LIBRARY_VERSION_MAJOR > 9 || (SODIUM_LIBRARY_VERSION_MAJOR == 9 && SODIUM_LIBRARY_VERSION_MINOR >= 6) REGISTER_STRING_CONSTANT("PASSWORD_ARGON2I", "argon2i", CONST_PERSISTENT); -#endif -#if SODIUM_LIBRARY_VERSION_MAJOR > 9 || (SODIUM_LIBRARY_VERSION_MAJOR == 9 && SODIUM_LIBRARY_VERSION_MINOR >= 6) REGISTER_STRING_CONSTANT("PASSWORD_ARGON2ID", "argon2id", CONST_PERSISTENT); -#endif -#if SODIUM_LIBRARY_VERSION_MAJOR > 9 || (SODIUM_LIBRARY_VERSION_MAJOR == 9 && SODIUM_LIBRARY_VERSION_MINOR >= 6) REGISTER_LONG_CONSTANT("PASSWORD_ARGON2_DEFAULT_MEMORY_COST", PHP_SODIUM_PWHASH_MEMLIMIT, CONST_PERSISTENT); -#endif -#if SODIUM_LIBRARY_VERSION_MAJOR > 9 || (SODIUM_LIBRARY_VERSION_MAJOR == 9 && SODIUM_LIBRARY_VERSION_MINOR >= 6) REGISTER_LONG_CONSTANT("PASSWORD_ARGON2_DEFAULT_TIME_COST", PHP_SODIUM_PWHASH_OPSLIMIT, CONST_PERSISTENT); -#endif -#if SODIUM_LIBRARY_VERSION_MAJOR > 9 || (SODIUM_LIBRARY_VERSION_MAJOR == 9 && SODIUM_LIBRARY_VERSION_MINOR >= 6) REGISTER_LONG_CONSTANT("PASSWORD_ARGON2_DEFAULT_THREADS", PHP_SODIUM_PWHASH_THREADS, CONST_PERSISTENT); -#endif -#if SODIUM_LIBRARY_VERSION_MAJOR > 9 || (SODIUM_LIBRARY_VERSION_MAJOR == 9 && SODIUM_LIBRARY_VERSION_MINOR >= 6) REGISTER_STRING_CONSTANT("PASSWORD_ARGON2_PROVIDER", "sodium", CONST_PERSISTENT); #endif } diff --git a/ext/standard/basic_functions_arginfo.h b/ext/standard/basic_functions_arginfo.h index d221a221f4132..37df4de7bdfcd 100644 --- a/ext/standard/basic_functions_arginfo.h +++ b/ext/standard/basic_functions_arginfo.h @@ -3592,20 +3592,10 @@ static void register_basic_functions_symbols(int module_number) REGISTER_LONG_CONSTANT("CRYPT_SHA512", 1, CONST_PERSISTENT); #if (defined(PHP_WIN32) || (defined(HAVE_DNS_SEARCH_FUNC) && defined(HAVE_FULL_DNS_FUNCS))) REGISTER_LONG_CONSTANT("DNS_A", PHP_DNS_A, CONST_PERSISTENT); -#endif -#if (defined(PHP_WIN32) || (defined(HAVE_DNS_SEARCH_FUNC) && defined(HAVE_FULL_DNS_FUNCS))) REGISTER_LONG_CONSTANT("DNS_NS", PHP_DNS_NS, CONST_PERSISTENT); -#endif -#if (defined(PHP_WIN32) || (defined(HAVE_DNS_SEARCH_FUNC) && defined(HAVE_FULL_DNS_FUNCS))) REGISTER_LONG_CONSTANT("DNS_CNAME", PHP_DNS_CNAME, CONST_PERSISTENT); -#endif -#if (defined(PHP_WIN32) || (defined(HAVE_DNS_SEARCH_FUNC) && defined(HAVE_FULL_DNS_FUNCS))) REGISTER_LONG_CONSTANT("DNS_SOA", PHP_DNS_SOA, CONST_PERSISTENT); -#endif -#if (defined(PHP_WIN32) || (defined(HAVE_DNS_SEARCH_FUNC) && defined(HAVE_FULL_DNS_FUNCS))) REGISTER_LONG_CONSTANT("DNS_PTR", PHP_DNS_PTR, CONST_PERSISTENT); -#endif -#if (defined(PHP_WIN32) || (defined(HAVE_DNS_SEARCH_FUNC) && defined(HAVE_FULL_DNS_FUNCS))) REGISTER_LONG_CONSTANT("DNS_HINFO", PHP_DNS_HINFO, CONST_PERSISTENT); #endif #if (defined(PHP_WIN32) || (defined(HAVE_DNS_SEARCH_FUNC) && defined(HAVE_FULL_DNS_FUNCS))) && (!defined(PHP_WIN32)) @@ -3613,26 +3603,12 @@ static void register_basic_functions_symbols(int module_number) #endif #if (defined(PHP_WIN32) || (defined(HAVE_DNS_SEARCH_FUNC) && defined(HAVE_FULL_DNS_FUNCS))) REGISTER_LONG_CONSTANT("DNS_MX", PHP_DNS_MX, CONST_PERSISTENT); -#endif -#if (defined(PHP_WIN32) || (defined(HAVE_DNS_SEARCH_FUNC) && defined(HAVE_FULL_DNS_FUNCS))) REGISTER_LONG_CONSTANT("DNS_TXT", PHP_DNS_TXT, CONST_PERSISTENT); -#endif -#if (defined(PHP_WIN32) || (defined(HAVE_DNS_SEARCH_FUNC) && defined(HAVE_FULL_DNS_FUNCS))) REGISTER_LONG_CONSTANT("DNS_SRV", PHP_DNS_SRV, CONST_PERSISTENT); -#endif -#if (defined(PHP_WIN32) || (defined(HAVE_DNS_SEARCH_FUNC) && defined(HAVE_FULL_DNS_FUNCS))) REGISTER_LONG_CONSTANT("DNS_NAPTR", PHP_DNS_NAPTR, CONST_PERSISTENT); -#endif -#if (defined(PHP_WIN32) || (defined(HAVE_DNS_SEARCH_FUNC) && defined(HAVE_FULL_DNS_FUNCS))) REGISTER_LONG_CONSTANT("DNS_AAAA", PHP_DNS_AAAA, CONST_PERSISTENT); -#endif -#if (defined(PHP_WIN32) || (defined(HAVE_DNS_SEARCH_FUNC) && defined(HAVE_FULL_DNS_FUNCS))) REGISTER_LONG_CONSTANT("DNS_A6", PHP_DNS_A6, CONST_PERSISTENT); -#endif -#if (defined(PHP_WIN32) || (defined(HAVE_DNS_SEARCH_FUNC) && defined(HAVE_FULL_DNS_FUNCS))) REGISTER_LONG_CONSTANT("DNS_ANY", PHP_DNS_ANY, CONST_PERSISTENT); -#endif -#if (defined(PHP_WIN32) || (defined(HAVE_DNS_SEARCH_FUNC) && defined(HAVE_FULL_DNS_FUNCS))) REGISTER_LONG_CONSTANT("DNS_ALL", PHP_DNS_ALL, CONST_PERSISTENT); #endif REGISTER_LONG_CONSTANT("HTML_SPECIALCHARS", PHP_HTML_SPECIALCHARS, CONST_PERSISTENT); @@ -3716,26 +3692,12 @@ static void register_basic_functions_symbols(int module_number) #endif #if !defined(PHP_WIN32) REGISTER_LONG_CONSTANT("LOG_LOCAL0", LOG_LOCAL0, CONST_PERSISTENT); -#endif -#if !defined(PHP_WIN32) REGISTER_LONG_CONSTANT("LOG_LOCAL1", LOG_LOCAL1, CONST_PERSISTENT); -#endif -#if !defined(PHP_WIN32) REGISTER_LONG_CONSTANT("LOG_LOCAL2", LOG_LOCAL2, CONST_PERSISTENT); -#endif -#if !defined(PHP_WIN32) REGISTER_LONG_CONSTANT("LOG_LOCAL3", LOG_LOCAL3, CONST_PERSISTENT); -#endif -#if !defined(PHP_WIN32) REGISTER_LONG_CONSTANT("LOG_LOCAL4", LOG_LOCAL4, CONST_PERSISTENT); -#endif -#if !defined(PHP_WIN32) REGISTER_LONG_CONSTANT("LOG_LOCAL5", LOG_LOCAL5, CONST_PERSISTENT); -#endif -#if !defined(PHP_WIN32) REGISTER_LONG_CONSTANT("LOG_LOCAL6", LOG_LOCAL6, CONST_PERSISTENT); -#endif -#if !defined(PHP_WIN32) REGISTER_LONG_CONSTANT("LOG_LOCAL7", LOG_LOCAL7, CONST_PERSISTENT); #endif REGISTER_LONG_CONSTANT("LOG_PID", LOG_PID, CONST_PERSISTENT); @@ -3768,116 +3730,48 @@ static void register_basic_functions_symbols(int module_number) #endif #if defined(HAVE_NL_LANGINFO) && defined(ABDAY_1) REGISTER_LONG_CONSTANT("ABDAY_1", ABDAY_1, CONST_PERSISTENT); -#endif -#if defined(HAVE_NL_LANGINFO) && defined(ABDAY_1) REGISTER_LONG_CONSTANT("ABDAY_2", ABDAY_2, CONST_PERSISTENT); -#endif -#if defined(HAVE_NL_LANGINFO) && defined(ABDAY_1) REGISTER_LONG_CONSTANT("ABDAY_3", ABDAY_3, CONST_PERSISTENT); -#endif -#if defined(HAVE_NL_LANGINFO) && defined(ABDAY_1) REGISTER_LONG_CONSTANT("ABDAY_4", ABDAY_4, CONST_PERSISTENT); -#endif -#if defined(HAVE_NL_LANGINFO) && defined(ABDAY_1) REGISTER_LONG_CONSTANT("ABDAY_5", ABDAY_5, CONST_PERSISTENT); -#endif -#if defined(HAVE_NL_LANGINFO) && defined(ABDAY_1) REGISTER_LONG_CONSTANT("ABDAY_6", ABDAY_6, CONST_PERSISTENT); -#endif -#if defined(HAVE_NL_LANGINFO) && defined(ABDAY_1) REGISTER_LONG_CONSTANT("ABDAY_7", ABDAY_7, CONST_PERSISTENT); #endif #if defined(HAVE_NL_LANGINFO) && defined(DAY_1) REGISTER_LONG_CONSTANT("DAY_1", DAY_1, CONST_PERSISTENT); -#endif -#if defined(HAVE_NL_LANGINFO) && defined(DAY_1) REGISTER_LONG_CONSTANT("DAY_2", DAY_2, CONST_PERSISTENT); -#endif -#if defined(HAVE_NL_LANGINFO) && defined(DAY_1) REGISTER_LONG_CONSTANT("DAY_3", DAY_3, CONST_PERSISTENT); -#endif -#if defined(HAVE_NL_LANGINFO) && defined(DAY_1) REGISTER_LONG_CONSTANT("DAY_4", DAY_4, CONST_PERSISTENT); -#endif -#if defined(HAVE_NL_LANGINFO) && defined(DAY_1) REGISTER_LONG_CONSTANT("DAY_5", DAY_5, CONST_PERSISTENT); -#endif -#if defined(HAVE_NL_LANGINFO) && defined(DAY_1) REGISTER_LONG_CONSTANT("DAY_6", DAY_6, CONST_PERSISTENT); -#endif -#if defined(HAVE_NL_LANGINFO) && defined(DAY_1) REGISTER_LONG_CONSTANT("DAY_7", DAY_7, CONST_PERSISTENT); #endif #if defined(HAVE_NL_LANGINFO) && defined(ABMON_1) REGISTER_LONG_CONSTANT("ABMON_1", ABMON_1, CONST_PERSISTENT); -#endif -#if defined(HAVE_NL_LANGINFO) && defined(ABMON_1) REGISTER_LONG_CONSTANT("ABMON_2", ABMON_2, CONST_PERSISTENT); -#endif -#if defined(HAVE_NL_LANGINFO) && defined(ABMON_1) REGISTER_LONG_CONSTANT("ABMON_3", ABMON_3, CONST_PERSISTENT); -#endif -#if defined(HAVE_NL_LANGINFO) && defined(ABMON_1) REGISTER_LONG_CONSTANT("ABMON_4", ABMON_4, CONST_PERSISTENT); -#endif -#if defined(HAVE_NL_LANGINFO) && defined(ABMON_1) REGISTER_LONG_CONSTANT("ABMON_5", ABMON_5, CONST_PERSISTENT); -#endif -#if defined(HAVE_NL_LANGINFO) && defined(ABMON_1) REGISTER_LONG_CONSTANT("ABMON_6", ABMON_6, CONST_PERSISTENT); -#endif -#if defined(HAVE_NL_LANGINFO) && defined(ABMON_1) REGISTER_LONG_CONSTANT("ABMON_7", ABMON_7, CONST_PERSISTENT); -#endif -#if defined(HAVE_NL_LANGINFO) && defined(ABMON_1) REGISTER_LONG_CONSTANT("ABMON_8", ABMON_8, CONST_PERSISTENT); -#endif -#if defined(HAVE_NL_LANGINFO) && defined(ABMON_1) REGISTER_LONG_CONSTANT("ABMON_9", ABMON_9, CONST_PERSISTENT); -#endif -#if defined(HAVE_NL_LANGINFO) && defined(ABMON_1) REGISTER_LONG_CONSTANT("ABMON_10", ABMON_10, CONST_PERSISTENT); -#endif -#if defined(HAVE_NL_LANGINFO) && defined(ABMON_1) REGISTER_LONG_CONSTANT("ABMON_11", ABMON_11, CONST_PERSISTENT); -#endif -#if defined(HAVE_NL_LANGINFO) && defined(ABMON_1) REGISTER_LONG_CONSTANT("ABMON_12", ABMON_12, CONST_PERSISTENT); #endif #if defined(HAVE_NL_LANGINFO) && defined(MON_1) REGISTER_LONG_CONSTANT("MON_1", MON_1, CONST_PERSISTENT); -#endif -#if defined(HAVE_NL_LANGINFO) && defined(MON_1) REGISTER_LONG_CONSTANT("MON_2", MON_2, CONST_PERSISTENT); -#endif -#if defined(HAVE_NL_LANGINFO) && defined(MON_1) REGISTER_LONG_CONSTANT("MON_3", MON_3, CONST_PERSISTENT); -#endif -#if defined(HAVE_NL_LANGINFO) && defined(MON_1) REGISTER_LONG_CONSTANT("MON_4", MON_4, CONST_PERSISTENT); -#endif -#if defined(HAVE_NL_LANGINFO) && defined(MON_1) REGISTER_LONG_CONSTANT("MON_5", MON_5, CONST_PERSISTENT); -#endif -#if defined(HAVE_NL_LANGINFO) && defined(MON_1) REGISTER_LONG_CONSTANT("MON_6", MON_6, CONST_PERSISTENT); -#endif -#if defined(HAVE_NL_LANGINFO) && defined(MON_1) REGISTER_LONG_CONSTANT("MON_7", MON_7, CONST_PERSISTENT); -#endif -#if defined(HAVE_NL_LANGINFO) && defined(MON_1) REGISTER_LONG_CONSTANT("MON_8", MON_8, CONST_PERSISTENT); -#endif -#if defined(HAVE_NL_LANGINFO) && defined(MON_1) REGISTER_LONG_CONSTANT("MON_9", MON_9, CONST_PERSISTENT); -#endif -#if defined(HAVE_NL_LANGINFO) && defined(MON_1) REGISTER_LONG_CONSTANT("MON_10", MON_10, CONST_PERSISTENT); -#endif -#if defined(HAVE_NL_LANGINFO) && defined(MON_1) REGISTER_LONG_CONSTANT("MON_11", MON_11, CONST_PERSISTENT); -#endif -#if defined(HAVE_NL_LANGINFO) && defined(MON_1) REGISTER_LONG_CONSTANT("MON_12", MON_12, CONST_PERSISTENT); #endif #if defined(HAVE_NL_LANGINFO) && defined(AM_STR) diff --git a/ext/standard/file_arginfo.h b/ext/standard/file_arginfo.h index b96e587a2ea1d..7dc8fcf80aabe 100644 --- a/ext/standard/file_arginfo.h +++ b/ext/standard/file_arginfo.h @@ -112,11 +112,7 @@ static void register_file_symbols(int module_number) REGISTER_LONG_CONSTANT("FILE_BINARY", 0, CONST_PERSISTENT | CONST_DEPRECATED); #if defined(HAVE_FNMATCH) REGISTER_LONG_CONSTANT("FNM_NOESCAPE", FNM_NOESCAPE, CONST_PERSISTENT); -#endif -#if defined(HAVE_FNMATCH) REGISTER_LONG_CONSTANT("FNM_PATHNAME", FNM_PATHNAME, CONST_PERSISTENT); -#endif -#if defined(HAVE_FNMATCH) REGISTER_LONG_CONSTANT("FNM_PERIOD", FNM_PERIOD, CONST_PERSISTENT); #endif #if defined(HAVE_FNMATCH) && defined(FNM_CASEFOLD) diff --git a/ext/standard/password_arginfo.h b/ext/standard/password_arginfo.h index bf1b614273a4a..fd25fbed67739 100644 --- a/ext/standard/password_arginfo.h +++ b/ext/standard/password_arginfo.h @@ -8,20 +8,10 @@ static void register_password_symbols(int module_number) REGISTER_LONG_CONSTANT("PASSWORD_BCRYPT_DEFAULT_COST", PHP_PASSWORD_BCRYPT_COST, CONST_PERSISTENT); #if defined(HAVE_ARGON2LIB) REGISTER_STRING_CONSTANT("PASSWORD_ARGON2I", "argon2i", CONST_PERSISTENT); -#endif -#if defined(HAVE_ARGON2LIB) REGISTER_STRING_CONSTANT("PASSWORD_ARGON2ID", "argon2id", CONST_PERSISTENT); -#endif -#if defined(HAVE_ARGON2LIB) REGISTER_STRING_CONSTANT("PASSWORD_ARGON2_PROVIDER", "standard", CONST_PERSISTENT); -#endif -#if defined(HAVE_ARGON2LIB) REGISTER_LONG_CONSTANT("PASSWORD_ARGON2_DEFAULT_MEMORY_COST", PHP_PASSWORD_ARGON2_MEMORY_COST, CONST_PERSISTENT); -#endif -#if defined(HAVE_ARGON2LIB) REGISTER_LONG_CONSTANT("PASSWORD_ARGON2_DEFAULT_TIME_COST", PHP_PASSWORD_ARGON2_TIME_COST, CONST_PERSISTENT); -#endif -#if defined(HAVE_ARGON2LIB) REGISTER_LONG_CONSTANT("PASSWORD_ARGON2_DEFAULT_THREADS", PHP_PASSWORD_ARGON2_THREADS, CONST_PERSISTENT); #endif } diff --git a/ext/tidy/tidy_arginfo.h b/ext/tidy/tidy_arginfo.h index b7dae788ef316..4084a29f4d908 100644 --- a/ext/tidy/tidy_arginfo.h +++ b/ext/tidy/tidy_arginfo.h @@ -437,86 +437,32 @@ static void register_tidy_symbols(int module_number) REGISTER_LONG_CONSTANT("TIDY_TAG_XMP", TidyTag_XMP, CONST_PERSISTENT); #if defined(HAVE_TIDYBUFFIO_H) REGISTER_LONG_CONSTANT("TIDY_TAG_ARTICLE", TidyTag_ARTICLE, CONST_PERSISTENT); -#endif -#if defined(HAVE_TIDYBUFFIO_H) REGISTER_LONG_CONSTANT("TIDY_TAG_ASIDE", TidyTag_ASIDE, CONST_PERSISTENT); -#endif -#if defined(HAVE_TIDYBUFFIO_H) REGISTER_LONG_CONSTANT("TIDY_TAG_AUDIO", TidyTag_AUDIO, CONST_PERSISTENT); -#endif -#if defined(HAVE_TIDYBUFFIO_H) REGISTER_LONG_CONSTANT("TIDY_TAG_BDI", TidyTag_BDI, CONST_PERSISTENT); -#endif -#if defined(HAVE_TIDYBUFFIO_H) REGISTER_LONG_CONSTANT("TIDY_TAG_CANVAS", TidyTag_CANVAS, CONST_PERSISTENT); -#endif -#if defined(HAVE_TIDYBUFFIO_H) REGISTER_LONG_CONSTANT("TIDY_TAG_COMMAND", TidyTag_COMMAND, CONST_PERSISTENT); -#endif -#if defined(HAVE_TIDYBUFFIO_H) REGISTER_LONG_CONSTANT("TIDY_TAG_DATALIST", TidyTag_DATALIST, CONST_PERSISTENT); -#endif -#if defined(HAVE_TIDYBUFFIO_H) REGISTER_LONG_CONSTANT("TIDY_TAG_DETAILS", TidyTag_DETAILS, CONST_PERSISTENT); -#endif -#if defined(HAVE_TIDYBUFFIO_H) REGISTER_LONG_CONSTANT("TIDY_TAG_DIALOG", TidyTag_DIALOG, CONST_PERSISTENT); -#endif -#if defined(HAVE_TIDYBUFFIO_H) REGISTER_LONG_CONSTANT("TIDY_TAG_FIGCAPTION", TidyTag_FIGCAPTION, CONST_PERSISTENT); -#endif -#if defined(HAVE_TIDYBUFFIO_H) REGISTER_LONG_CONSTANT("TIDY_TAG_FIGURE", TidyTag_FIGURE, CONST_PERSISTENT); -#endif -#if defined(HAVE_TIDYBUFFIO_H) REGISTER_LONG_CONSTANT("TIDY_TAG_FOOTER", TidyTag_FOOTER, CONST_PERSISTENT); -#endif -#if defined(HAVE_TIDYBUFFIO_H) REGISTER_LONG_CONSTANT("TIDY_TAG_HEADER", TidyTag_HEADER, CONST_PERSISTENT); -#endif -#if defined(HAVE_TIDYBUFFIO_H) REGISTER_LONG_CONSTANT("TIDY_TAG_HGROUP", TidyTag_HGROUP, CONST_PERSISTENT); -#endif -#if defined(HAVE_TIDYBUFFIO_H) REGISTER_LONG_CONSTANT("TIDY_TAG_MAIN", TidyTag_MAIN, CONST_PERSISTENT); -#endif -#if defined(HAVE_TIDYBUFFIO_H) REGISTER_LONG_CONSTANT("TIDY_TAG_MARK", TidyTag_MARK, CONST_PERSISTENT); -#endif -#if defined(HAVE_TIDYBUFFIO_H) REGISTER_LONG_CONSTANT("TIDY_TAG_MENUITEM", TidyTag_MENUITEM, CONST_PERSISTENT); -#endif -#if defined(HAVE_TIDYBUFFIO_H) REGISTER_LONG_CONSTANT("TIDY_TAG_METER", TidyTag_METER, CONST_PERSISTENT); -#endif -#if defined(HAVE_TIDYBUFFIO_H) REGISTER_LONG_CONSTANT("TIDY_TAG_NAV", TidyTag_NAV, CONST_PERSISTENT); -#endif -#if defined(HAVE_TIDYBUFFIO_H) REGISTER_LONG_CONSTANT("TIDY_TAG_OUTPUT", TidyTag_OUTPUT, CONST_PERSISTENT); -#endif -#if defined(HAVE_TIDYBUFFIO_H) REGISTER_LONG_CONSTANT("TIDY_TAG_PROGRESS", TidyTag_PROGRESS, CONST_PERSISTENT); -#endif -#if defined(HAVE_TIDYBUFFIO_H) REGISTER_LONG_CONSTANT("TIDY_TAG_SECTION", TidyTag_SECTION, CONST_PERSISTENT); -#endif -#if defined(HAVE_TIDYBUFFIO_H) REGISTER_LONG_CONSTANT("TIDY_TAG_SOURCE", TidyTag_SOURCE, CONST_PERSISTENT); -#endif -#if defined(HAVE_TIDYBUFFIO_H) REGISTER_LONG_CONSTANT("TIDY_TAG_SUMMARY", TidyTag_SUMMARY, CONST_PERSISTENT); -#endif -#if defined(HAVE_TIDYBUFFIO_H) REGISTER_LONG_CONSTANT("TIDY_TAG_TEMPLATE", TidyTag_TEMPLATE, CONST_PERSISTENT); -#endif -#if defined(HAVE_TIDYBUFFIO_H) REGISTER_LONG_CONSTANT("TIDY_TAG_TIME", TidyTag_TIME, CONST_PERSISTENT); -#endif -#if defined(HAVE_TIDYBUFFIO_H) REGISTER_LONG_CONSTANT("TIDY_TAG_TRACK", TidyTag_TRACK, CONST_PERSISTENT); -#endif -#if defined(HAVE_TIDYBUFFIO_H) REGISTER_LONG_CONSTANT("TIDY_TAG_VIDEO", TidyTag_VIDEO, CONST_PERSISTENT); #endif } diff --git a/ext/xsl/php_xsl_arginfo.h b/ext/xsl/php_xsl_arginfo.h index d040928197f65..72e7ed0b0a99d 100644 --- a/ext/xsl/php_xsl_arginfo.h +++ b/ext/xsl/php_xsl_arginfo.h @@ -106,8 +106,6 @@ static void register_php_xsl_symbols(int module_number) REGISTER_STRING_CONSTANT("LIBXSLT_DOTTED_VERSION", LIBXSLT_DOTTED_VERSION, CONST_PERSISTENT); #if defined(HAVE_XSL_EXSLT) REGISTER_LONG_CONSTANT("LIBEXSLT_VERSION", LIBEXSLT_VERSION, CONST_PERSISTENT); -#endif -#if defined(HAVE_XSL_EXSLT) REGISTER_STRING_CONSTANT("LIBEXSLT_DOTTED_VERSION", LIBEXSLT_DOTTED_VERSION, CONST_PERSISTENT); #endif } diff --git a/ext/zip/php_zip_arginfo.h b/ext/zip/php_zip_arginfo.h index 6166218b2d8dd..c8dab41560e48 100644 --- a/ext/zip/php_zip_arginfo.h +++ b/ext/zip/php_zip_arginfo.h @@ -1103,160 +1103,120 @@ static zend_class_entry *register_class_ZipArchive(zend_class_entry *class_entry zend_string *const_OPSYS_DOS_name = zend_string_init_interned("OPSYS_DOS", sizeof("OPSYS_DOS") - 1, 1); zend_declare_typed_class_constant(class_entry, const_OPSYS_DOS_name, &const_OPSYS_DOS_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release(const_OPSYS_DOS_name); -#endif -#if defined(ZIP_OPSYS_DEFAULT) zval const_OPSYS_AMIGA_value; ZVAL_LONG(&const_OPSYS_AMIGA_value, ZIP_OPSYS_AMIGA); zend_string *const_OPSYS_AMIGA_name = zend_string_init_interned("OPSYS_AMIGA", sizeof("OPSYS_AMIGA") - 1, 1); zend_declare_typed_class_constant(class_entry, const_OPSYS_AMIGA_name, &const_OPSYS_AMIGA_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release(const_OPSYS_AMIGA_name); -#endif -#if defined(ZIP_OPSYS_DEFAULT) zval const_OPSYS_OPENVMS_value; ZVAL_LONG(&const_OPSYS_OPENVMS_value, ZIP_OPSYS_OPENVMS); zend_string *const_OPSYS_OPENVMS_name = zend_string_init_interned("OPSYS_OPENVMS", sizeof("OPSYS_OPENVMS") - 1, 1); zend_declare_typed_class_constant(class_entry, const_OPSYS_OPENVMS_name, &const_OPSYS_OPENVMS_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release(const_OPSYS_OPENVMS_name); -#endif -#if defined(ZIP_OPSYS_DEFAULT) zval const_OPSYS_UNIX_value; ZVAL_LONG(&const_OPSYS_UNIX_value, ZIP_OPSYS_UNIX); zend_string *const_OPSYS_UNIX_name = zend_string_init_interned("OPSYS_UNIX", sizeof("OPSYS_UNIX") - 1, 1); zend_declare_typed_class_constant(class_entry, const_OPSYS_UNIX_name, &const_OPSYS_UNIX_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release(const_OPSYS_UNIX_name); -#endif -#if defined(ZIP_OPSYS_DEFAULT) zval const_OPSYS_VM_CMS_value; ZVAL_LONG(&const_OPSYS_VM_CMS_value, ZIP_OPSYS_VM_CMS); zend_string *const_OPSYS_VM_CMS_name = zend_string_init_interned("OPSYS_VM_CMS", sizeof("OPSYS_VM_CMS") - 1, 1); zend_declare_typed_class_constant(class_entry, const_OPSYS_VM_CMS_name, &const_OPSYS_VM_CMS_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release(const_OPSYS_VM_CMS_name); -#endif -#if defined(ZIP_OPSYS_DEFAULT) zval const_OPSYS_ATARI_ST_value; ZVAL_LONG(&const_OPSYS_ATARI_ST_value, ZIP_OPSYS_ATARI_ST); zend_string *const_OPSYS_ATARI_ST_name = zend_string_init_interned("OPSYS_ATARI_ST", sizeof("OPSYS_ATARI_ST") - 1, 1); zend_declare_typed_class_constant(class_entry, const_OPSYS_ATARI_ST_name, &const_OPSYS_ATARI_ST_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release(const_OPSYS_ATARI_ST_name); -#endif -#if defined(ZIP_OPSYS_DEFAULT) zval const_OPSYS_OS_2_value; ZVAL_LONG(&const_OPSYS_OS_2_value, ZIP_OPSYS_OS_2); zend_string *const_OPSYS_OS_2_name = zend_string_init_interned("OPSYS_OS_2", sizeof("OPSYS_OS_2") - 1, 1); zend_declare_typed_class_constant(class_entry, const_OPSYS_OS_2_name, &const_OPSYS_OS_2_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release(const_OPSYS_OS_2_name); -#endif -#if defined(ZIP_OPSYS_DEFAULT) zval const_OPSYS_MACINTOSH_value; ZVAL_LONG(&const_OPSYS_MACINTOSH_value, ZIP_OPSYS_MACINTOSH); zend_string *const_OPSYS_MACINTOSH_name = zend_string_init_interned("OPSYS_MACINTOSH", sizeof("OPSYS_MACINTOSH") - 1, 1); zend_declare_typed_class_constant(class_entry, const_OPSYS_MACINTOSH_name, &const_OPSYS_MACINTOSH_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release(const_OPSYS_MACINTOSH_name); -#endif -#if defined(ZIP_OPSYS_DEFAULT) zval const_OPSYS_Z_SYSTEM_value; ZVAL_LONG(&const_OPSYS_Z_SYSTEM_value, ZIP_OPSYS_Z_SYSTEM); zend_string *const_OPSYS_Z_SYSTEM_name = zend_string_init_interned("OPSYS_Z_SYSTEM", sizeof("OPSYS_Z_SYSTEM") - 1, 1); zend_declare_typed_class_constant(class_entry, const_OPSYS_Z_SYSTEM_name, &const_OPSYS_Z_SYSTEM_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release(const_OPSYS_Z_SYSTEM_name); -#endif -#if defined(ZIP_OPSYS_DEFAULT) zval const_OPSYS_CPM_value; ZVAL_LONG(&const_OPSYS_CPM_value, ZIP_OPSYS_CPM); zend_string *const_OPSYS_CPM_name = zend_string_init_interned("OPSYS_CPM", sizeof("OPSYS_CPM") - 1, 1); zend_declare_typed_class_constant(class_entry, const_OPSYS_CPM_name, &const_OPSYS_CPM_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release(const_OPSYS_CPM_name); -#endif -#if defined(ZIP_OPSYS_DEFAULT) zval const_OPSYS_WINDOWS_NTFS_value; ZVAL_LONG(&const_OPSYS_WINDOWS_NTFS_value, ZIP_OPSYS_WINDOWS_NTFS); zend_string *const_OPSYS_WINDOWS_NTFS_name = zend_string_init_interned("OPSYS_WINDOWS_NTFS", sizeof("OPSYS_WINDOWS_NTFS") - 1, 1); zend_declare_typed_class_constant(class_entry, const_OPSYS_WINDOWS_NTFS_name, &const_OPSYS_WINDOWS_NTFS_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release(const_OPSYS_WINDOWS_NTFS_name); -#endif -#if defined(ZIP_OPSYS_DEFAULT) zval const_OPSYS_MVS_value; ZVAL_LONG(&const_OPSYS_MVS_value, ZIP_OPSYS_MVS); zend_string *const_OPSYS_MVS_name = zend_string_init_interned("OPSYS_MVS", sizeof("OPSYS_MVS") - 1, 1); zend_declare_typed_class_constant(class_entry, const_OPSYS_MVS_name, &const_OPSYS_MVS_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release(const_OPSYS_MVS_name); -#endif -#if defined(ZIP_OPSYS_DEFAULT) zval const_OPSYS_VSE_value; ZVAL_LONG(&const_OPSYS_VSE_value, ZIP_OPSYS_VSE); zend_string *const_OPSYS_VSE_name = zend_string_init_interned("OPSYS_VSE", sizeof("OPSYS_VSE") - 1, 1); zend_declare_typed_class_constant(class_entry, const_OPSYS_VSE_name, &const_OPSYS_VSE_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release(const_OPSYS_VSE_name); -#endif -#if defined(ZIP_OPSYS_DEFAULT) zval const_OPSYS_ACORN_RISC_value; ZVAL_LONG(&const_OPSYS_ACORN_RISC_value, ZIP_OPSYS_ACORN_RISC); zend_string *const_OPSYS_ACORN_RISC_name = zend_string_init_interned("OPSYS_ACORN_RISC", sizeof("OPSYS_ACORN_RISC") - 1, 1); zend_declare_typed_class_constant(class_entry, const_OPSYS_ACORN_RISC_name, &const_OPSYS_ACORN_RISC_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release(const_OPSYS_ACORN_RISC_name); -#endif -#if defined(ZIP_OPSYS_DEFAULT) zval const_OPSYS_VFAT_value; ZVAL_LONG(&const_OPSYS_VFAT_value, ZIP_OPSYS_VFAT); zend_string *const_OPSYS_VFAT_name = zend_string_init_interned("OPSYS_VFAT", sizeof("OPSYS_VFAT") - 1, 1); zend_declare_typed_class_constant(class_entry, const_OPSYS_VFAT_name, &const_OPSYS_VFAT_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release(const_OPSYS_VFAT_name); -#endif -#if defined(ZIP_OPSYS_DEFAULT) zval const_OPSYS_ALTERNATE_MVS_value; ZVAL_LONG(&const_OPSYS_ALTERNATE_MVS_value, ZIP_OPSYS_ALTERNATE_MVS); zend_string *const_OPSYS_ALTERNATE_MVS_name = zend_string_init_interned("OPSYS_ALTERNATE_MVS", sizeof("OPSYS_ALTERNATE_MVS") - 1, 1); zend_declare_typed_class_constant(class_entry, const_OPSYS_ALTERNATE_MVS_name, &const_OPSYS_ALTERNATE_MVS_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release(const_OPSYS_ALTERNATE_MVS_name); -#endif -#if defined(ZIP_OPSYS_DEFAULT) zval const_OPSYS_BEOS_value; ZVAL_LONG(&const_OPSYS_BEOS_value, ZIP_OPSYS_BEOS); zend_string *const_OPSYS_BEOS_name = zend_string_init_interned("OPSYS_BEOS", sizeof("OPSYS_BEOS") - 1, 1); zend_declare_typed_class_constant(class_entry, const_OPSYS_BEOS_name, &const_OPSYS_BEOS_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release(const_OPSYS_BEOS_name); -#endif -#if defined(ZIP_OPSYS_DEFAULT) zval const_OPSYS_TANDEM_value; ZVAL_LONG(&const_OPSYS_TANDEM_value, ZIP_OPSYS_TANDEM); zend_string *const_OPSYS_TANDEM_name = zend_string_init_interned("OPSYS_TANDEM", sizeof("OPSYS_TANDEM") - 1, 1); zend_declare_typed_class_constant(class_entry, const_OPSYS_TANDEM_name, &const_OPSYS_TANDEM_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release(const_OPSYS_TANDEM_name); -#endif -#if defined(ZIP_OPSYS_DEFAULT) zval const_OPSYS_OS_400_value; ZVAL_LONG(&const_OPSYS_OS_400_value, ZIP_OPSYS_OS_400); zend_string *const_OPSYS_OS_400_name = zend_string_init_interned("OPSYS_OS_400", sizeof("OPSYS_OS_400") - 1, 1); zend_declare_typed_class_constant(class_entry, const_OPSYS_OS_400_name, &const_OPSYS_OS_400_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release(const_OPSYS_OS_400_name); -#endif -#if defined(ZIP_OPSYS_DEFAULT) zval const_OPSYS_OS_X_value; ZVAL_LONG(&const_OPSYS_OS_X_value, ZIP_OPSYS_OS_X); zend_string *const_OPSYS_OS_X_name = zend_string_init_interned("OPSYS_OS_X", sizeof("OPSYS_OS_X") - 1, 1); zend_declare_typed_class_constant(class_entry, const_OPSYS_OS_X_name, &const_OPSYS_OS_X_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release(const_OPSYS_OS_X_name); -#endif -#if defined(ZIP_OPSYS_DEFAULT) zval const_OPSYS_DEFAULT_value; ZVAL_LONG(&const_OPSYS_DEFAULT_value, ZIP_OPSYS_DEFAULT); @@ -1283,16 +1243,12 @@ static zend_class_entry *register_class_ZipArchive(zend_class_entry *class_entry zend_string *const_EM_AES_128_name = zend_string_init_interned("EM_AES_128", sizeof("EM_AES_128") - 1, 1); zend_declare_typed_class_constant(class_entry, const_EM_AES_128_name, &const_EM_AES_128_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release(const_EM_AES_128_name); -#endif -#if defined(HAVE_ENCRYPTION) zval const_EM_AES_192_value; ZVAL_LONG(&const_EM_AES_192_value, ZIP_EM_AES_192); zend_string *const_EM_AES_192_name = zend_string_init_interned("EM_AES_192", sizeof("EM_AES_192") - 1, 1); zend_declare_typed_class_constant(class_entry, const_EM_AES_192_name, &const_EM_AES_192_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release(const_EM_AES_192_name); -#endif -#if defined(HAVE_ENCRYPTION) zval const_EM_AES_256_value; ZVAL_LONG(&const_EM_AES_256_value, ZIP_EM_AES_256); diff --git a/main/main_arginfo.h b/main/main_arginfo.h index f0d2599baf473..922af3aff0629 100644 --- a/main/main_arginfo.h +++ b/main/main_arginfo.h @@ -43,35 +43,15 @@ static void register_main_symbols(int module_number) REGISTER_DOUBLE_CONSTANT("PHP_FLOAT_MIN", DBL_MIN, CONST_PERSISTENT); #if defined(PHP_WIN32) REGISTER_LONG_CONSTANT("PHP_WINDOWS_VERSION_MAJOR", EG(windows_version_info).dwMajorVersion, CONST_PERSISTENT); -#endif -#if defined(PHP_WIN32) REGISTER_LONG_CONSTANT("PHP_WINDOWS_VERSION_MINOR", EG(windows_version_info).dwMinorVersion, CONST_PERSISTENT); -#endif -#if defined(PHP_WIN32) REGISTER_LONG_CONSTANT("PHP_WINDOWS_VERSION_BUILD", EG(windows_version_info).dwBuildNumber, CONST_PERSISTENT); -#endif -#if defined(PHP_WIN32) REGISTER_LONG_CONSTANT("PHP_WINDOWS_VERSION_PLATFORM", EG(windows_version_info).dwPlatformId, CONST_PERSISTENT); -#endif -#if defined(PHP_WIN32) REGISTER_LONG_CONSTANT("PHP_WINDOWS_VERSION_SP_MAJOR", EG(windows_version_info).wServicePackMajor, CONST_PERSISTENT); -#endif -#if defined(PHP_WIN32) REGISTER_LONG_CONSTANT("PHP_WINDOWS_VERSION_SP_MINOR", EG(windows_version_info).wServicePackMinor, CONST_PERSISTENT); -#endif -#if defined(PHP_WIN32) REGISTER_LONG_CONSTANT("PHP_WINDOWS_VERSION_SUITEMASK", EG(windows_version_info).wSuiteMask, CONST_PERSISTENT); -#endif -#if defined(PHP_WIN32) REGISTER_LONG_CONSTANT("PHP_WINDOWS_VERSION_PRODUCTTYPE", EG(windows_version_info).wProductType, CONST_PERSISTENT); -#endif -#if defined(PHP_WIN32) REGISTER_LONG_CONSTANT("PHP_WINDOWS_NT_DOMAIN_CONTROLLER", VER_NT_DOMAIN_CONTROLLER, CONST_PERSISTENT); -#endif -#if defined(PHP_WIN32) REGISTER_LONG_CONSTANT("PHP_WINDOWS_NT_SERVER", VER_NT_SERVER, CONST_PERSISTENT); -#endif -#if defined(PHP_WIN32) REGISTER_LONG_CONSTANT("PHP_WINDOWS_NT_WORKSTATION", VER_NT_WORKSTATION, CONST_PERSISTENT); #endif REGISTER_LONG_CONSTANT("PHP_OUTPUT_HANDLER_START", PHP_OUTPUT_HANDLER_START, CONST_PERSISTENT); From e9310171f767d95c34df90eebfbec7d61ad22bbc Mon Sep 17 00:00:00 2001 From: Saki Takamachi <34942839+SakiTakamachi@users.noreply.github.com> Date: Mon, 23 Jun 2025 15:29:20 +0900 Subject: [PATCH 130/682] [skip ci] Add myself to EXTENSIONS (#18918) --- EXTENSIONS | 1 + 1 file changed, 1 insertion(+) diff --git a/EXTENSIONS b/EXTENSIONS index 34e289ffcae2b..d15401f372384 100644 --- a/EXTENSIONS +++ b/EXTENSIONS @@ -238,6 +238,7 @@ SINCE: 5.0 ------------------------------------------------------------------------------- EXTENSION: bcmath PRIMARY MAINTAINER: Andi Gutmans (2000 - 2004) + Saki Takamachi (2024 - 2025) MAINTENANCE: Maintained STATUS: Working ------------------------------------------------------------------------------- From 22bd2ae63f33cfcbd4f2383f4fe8ffa480a1ab42 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sun, 15 Jun 2025 00:04:30 +0100 Subject: [PATCH 131/682] ext/sqlite3: explain statement support addition. similar to what have been done for pdo/sqlite as having statement explain support to simulate how a query would operate or for more advanced users, analysing the VM routines used for possible optimisations. close GH-18853 --- NEWS | 2 + ext/sqlite3/sqlite3.c | 57 ++++ ext/sqlite3/sqlite3.stub.php | 9 + ext/sqlite3/sqlite3_arginfo.h | 43 ++- ext/sqlite3/tests/sqlite3_explain.phpt | 390 +++++++++++++++++++++++++ 5 files changed, 500 insertions(+), 1 deletion(-) create mode 100644 ext/sqlite3/tests/sqlite3_explain.phpt diff --git a/NEWS b/NEWS index dfa90ed08f62c..ccda0d50af84e 100644 --- a/NEWS +++ b/NEWS @@ -247,6 +247,8 @@ PHP NEWS - Sqlite: . Added Sqlite3Stmt::busy to check if a statement is still being executed. (David Carlier) + . Added Sqlite3Stmt::explain to produce a explain query plan from + the statement. (David Carlier) - Standard: . Fixed crypt() tests on musl when using --with-external-libcrypt diff --git a/ext/sqlite3/sqlite3.c b/ext/sqlite3/sqlite3.c index 799c77b24a8c8..349d2823bb1e0 100644 --- a/ext/sqlite3/sqlite3.c +++ b/ext/sqlite3/sqlite3.c @@ -26,6 +26,9 @@ #include "SAPI.h" #include +#ifdef __APPLE__ +#include +#endif #include "zend_exceptions.h" #include "sqlite3_arginfo.h" @@ -1500,6 +1503,60 @@ PHP_METHOD(SQLite3Stmt, busy) RETURN_FALSE; } +#if SQLITE_VERSION_NUMBER >= 3043000 +PHP_METHOD(SQLite3Stmt, explain) +{ +#ifdef __APPLE__ + if (__builtin_available(macOS 14.2, *)) { +#endif + php_sqlite3_stmt *stmt_obj; + zval *object = ZEND_THIS; + stmt_obj = Z_SQLITE3_STMT_P(object); + + ZEND_PARSE_PARAMETERS_NONE(); + + SQLITE3_CHECK_INITIALIZED(stmt_obj->db_obj, stmt_obj->initialised, SQLite3); + SQLITE3_CHECK_INITIALIZED_STMT(stmt_obj->stmt, SQLite3Stmt); + + RETURN_LONG((zend_long)sqlite3_stmt_isexplain(stmt_obj->stmt)); +#ifdef __APPLE__ + } else { + zend_throw_error(NULL, "explain statement unsupported"); + } +#endif +} + +PHP_METHOD(SQLite3Stmt, setExplain) +{ +#ifdef __APPLE__ + if (__builtin_available(macOS 14.2, *)) { +#endif + php_sqlite3_stmt *stmt_obj; + zend_long mode; + zval *object = ZEND_THIS; + stmt_obj = Z_SQLITE3_STMT_P(object); + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG(mode) + ZEND_PARSE_PARAMETERS_END(); + + if (mode < 0 || mode > 2) { + zend_argument_value_error(1, "must be one of the SQLite3Stmt::EXPLAIN_MODE_* constants"); + RETURN_THROWS(); + } + + SQLITE3_CHECK_INITIALIZED(stmt_obj->db_obj, stmt_obj->initialised, SQLite3); + SQLITE3_CHECK_INITIALIZED_STMT(stmt_obj->stmt, SQLite3Stmt); + + RETURN_BOOL(sqlite3_stmt_explain(stmt_obj->stmt, (int)mode) == SQLITE_OK); +#ifdef __APPLE__ + } else { + zend_throw_error(NULL, "explain statement unsupported"); + } +#endif +} +#endif + /* bind parameters to a statement before execution */ static int php_sqlite3_bind_params(php_sqlite3_stmt *stmt_obj) /* {{{ */ { diff --git a/ext/sqlite3/sqlite3.stub.php b/ext/sqlite3/sqlite3.stub.php index 8a3d90470767a..55af378b325c5 100644 --- a/ext/sqlite3/sqlite3.stub.php +++ b/ext/sqlite3/sqlite3.stub.php @@ -274,6 +274,15 @@ public function readOnly(): bool {} public function reset(): bool {} public function busy(): bool {} + +#if SQLITE_VERSION_NUMBER >= 3043000 + public const int EXPLAIN_MODE_PREPARED = 0; + public const int EXPLAIN_MODE_EXPLAIN = 1; + public const int EXPLAIN_MODE_EXPLAIN_QUERY_PLAN = 2; + + public function explain(): int {} + public function setExplain(int $mode): bool {} +#endif } /** @not-serializable */ diff --git a/ext/sqlite3/sqlite3_arginfo.h b/ext/sqlite3/sqlite3_arginfo.h index f83188841b43f..e306af04538b3 100644 --- a/ext/sqlite3/sqlite3_arginfo.h +++ b/ext/sqlite3/sqlite3_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 28132e0e4df61f19dc4b23a7c9f79be6b3e40a8e */ + * Stub hash: c3216eada9881743cbd3aa1510f1200b7ce0d942 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_SQLite3___construct, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, filename, IS_STRING, 0) @@ -147,6 +147,15 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_SQLite3Stmt_busy, 0, 0, _IS_BOOL, 0) ZEND_END_ARG_INFO() +#if SQLITE_VERSION_NUMBER >= 3043000 +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_SQLite3Stmt_explain, 0, 0, IS_LONG, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_SQLite3Stmt_setExplain, 0, 1, _IS_BOOL, 0) + ZEND_ARG_TYPE_INFO(0, mode, IS_LONG, 0) +ZEND_END_ARG_INFO() +#endif + ZEND_BEGIN_ARG_INFO_EX(arginfo_class_SQLite3Result___construct, 0, 0, 0) ZEND_END_ARG_INFO() @@ -206,6 +215,10 @@ ZEND_METHOD(SQLite3Stmt, paramCount); ZEND_METHOD(SQLite3Stmt, readOnly); ZEND_METHOD(SQLite3Stmt, reset); ZEND_METHOD(SQLite3Stmt, busy); +#if SQLITE_VERSION_NUMBER >= 3043000 +ZEND_METHOD(SQLite3Stmt, explain); +ZEND_METHOD(SQLite3Stmt, setExplain); +#endif ZEND_METHOD(SQLite3Result, __construct); ZEND_METHOD(SQLite3Result, numColumns); ZEND_METHOD(SQLite3Result, columnName); @@ -258,6 +271,10 @@ static const zend_function_entry class_SQLite3Stmt_methods[] = { ZEND_ME(SQLite3Stmt, readOnly, arginfo_class_SQLite3Stmt_readOnly, ZEND_ACC_PUBLIC) ZEND_ME(SQLite3Stmt, reset, arginfo_class_SQLite3Stmt_reset, ZEND_ACC_PUBLIC) ZEND_ME(SQLite3Stmt, busy, arginfo_class_SQLite3Stmt_busy, ZEND_ACC_PUBLIC) +#if SQLITE_VERSION_NUMBER >= 3043000 + ZEND_ME(SQLite3Stmt, explain, arginfo_class_SQLite3Stmt_explain, ZEND_ACC_PUBLIC) + ZEND_ME(SQLite3Stmt, setExplain, arginfo_class_SQLite3Stmt_setExplain, ZEND_ACC_PUBLIC) +#endif ZEND_FE_END }; @@ -540,6 +557,30 @@ static zend_class_entry *register_class_SQLite3Stmt(void) INIT_CLASS_ENTRY(ce, "SQLite3Stmt", class_SQLite3Stmt_methods); class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_NOT_SERIALIZABLE); +#if SQLITE_VERSION_NUMBER >= 3043000 + + zval const_EXPLAIN_MODE_PREPARED_value; + ZVAL_LONG(&const_EXPLAIN_MODE_PREPARED_value, 0); + zend_string *const_EXPLAIN_MODE_PREPARED_name = zend_string_init_interned("EXPLAIN_MODE_PREPARED", sizeof("EXPLAIN_MODE_PREPARED") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_EXPLAIN_MODE_PREPARED_name, &const_EXPLAIN_MODE_PREPARED_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_EXPLAIN_MODE_PREPARED_name); +#endif +#if SQLITE_VERSION_NUMBER >= 3043000 + + zval const_EXPLAIN_MODE_EXPLAIN_value; + ZVAL_LONG(&const_EXPLAIN_MODE_EXPLAIN_value, 1); + zend_string *const_EXPLAIN_MODE_EXPLAIN_name = zend_string_init_interned("EXPLAIN_MODE_EXPLAIN", sizeof("EXPLAIN_MODE_EXPLAIN") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_EXPLAIN_MODE_EXPLAIN_name, &const_EXPLAIN_MODE_EXPLAIN_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_EXPLAIN_MODE_EXPLAIN_name); +#endif +#if SQLITE_VERSION_NUMBER >= 3043000 + + zval const_EXPLAIN_MODE_EXPLAIN_QUERY_PLAN_value; + ZVAL_LONG(&const_EXPLAIN_MODE_EXPLAIN_QUERY_PLAN_value, 2); + zend_string *const_EXPLAIN_MODE_EXPLAIN_QUERY_PLAN_name = zend_string_init_interned("EXPLAIN_MODE_EXPLAIN_QUERY_PLAN", sizeof("EXPLAIN_MODE_EXPLAIN_QUERY_PLAN") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_EXPLAIN_MODE_EXPLAIN_QUERY_PLAN_name, &const_EXPLAIN_MODE_EXPLAIN_QUERY_PLAN_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_EXPLAIN_MODE_EXPLAIN_QUERY_PLAN_name); +#endif return class_entry; } diff --git a/ext/sqlite3/tests/sqlite3_explain.phpt b/ext/sqlite3/tests/sqlite3_explain.phpt new file mode 100644 index 0000000000000..f580783ca1d14 --- /dev/null +++ b/ext/sqlite3/tests/sqlite3_explain.phpt @@ -0,0 +1,390 @@ +--TEST-- +Sqlite3Stmt::explain/setExplain usage +--EXTENSIONS-- +sqlite3 +--SKIPIF-- + +--FILE-- +exec('CREATE TABLE test_explain (a string);'); +$stmt = $db->prepare('INSERT INTO test_explain VALUES ("first insert"), ("second_insert")'); +$stmt->setExplain(Sqlite3Stmt::EXPLAIN_MODE_EXPLAIN); +var_dump($stmt->explain() == Sqlite3Stmt::EXPLAIN_MODE_EXPLAIN); +$r = $stmt->execute(); +$result = []; +while (($arr = $r->fetchArray(SQLITE3_ASSOC)) !== false) $result[] = $arr; +var_dump($result); +$stmts = $db->prepare('SELECT * FROM test_explain'); +$stmts->setExplain(Sqlite3Stmt::EXPLAIN_MODE_EXPLAIN_QUERY_PLAN); +$r = $stmts->execute(); +$result = []; +while (($arr = $r->fetchArray(SQLITE3_ASSOC)) !== false) $result[] = $arr; +var_dump($result); + +$stmt = $db->prepare('INSERT INTO test_explain VALUES ("first insert"), ("second_insert")'); +$stmt->setExplain(Sqlite3Stmt::EXPLAIN_MODE_PREPARED); +$stmt->execute(); +$stmts = $db->prepare('SELECT * FROM test_explain'); +$stmts->setExplain(Sqlite3Stmt::EXPLAIN_MODE_PREPARED); +$r = $stmts->execute(); +$result = []; +while (($arr = $r->fetchArray(SQLITE3_ASSOC)) !== false) $result[] = $arr; +var_dump($result); + +try { + $stmts->setExplain(-1); +} catch (\ValueError $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + $stmts->setExplain(256); +} catch (\ValueError $e) { + echo $e->getMessage(), PHP_EOL; +} + +var_dump($stmts->explain() == Sqlite3Stmt::EXPLAIN_MODE_PREPARED); +?> +--EXPECTF-- +bool(true) +array(%d) { + [0]=> + array(8) { + ["addr"]=> + int(0) + ["opcode"]=> + string(4) "Init" + ["p1"]=> + int(0) + ["p2"]=> + int(%d) + ["p3"]=> + int(0) + ["p4"]=> + NULL + ["p5"]=> + int(0) + ["comment"]=> + %a + } + [1]=> + array(8) { + ["addr"]=> + int(1) + ["opcode"]=> + string(13) "InitCoroutine" + ["p1"]=> + int(3) + ["p2"]=> + int(%d) + ["p3"]=> + int(2) + ["p4"]=> + NULL + ["p5"]=> + int(0) + ["comment"]=> + %a + } + %A + array(8) { + ["addr"]=> + int(%d) + ["opcode"]=> + string(7) "String8" + ["p1"]=> + int(0) + ["p2"]=> + int(2) + ["p3"]=> + int(0) + ["p4"]=> + string(12) "first insert" + ["p5"]=> + int(0) + ["comment"]=> + %a + } + %A + array(8) { + ["addr"]=> + int(%d) + ["opcode"]=> + string(5) "Yield" + ["p1"]=> + int(3) + ["p2"]=> + int(0) + ["p3"]=> + int(0) + ["p4"]=> + NULL + ["p5"]=> + int(0) + ["comment"]=> + %a + } + %A + array(8) { + ["addr"]=> + int(%d) + ["opcode"]=> + string(7) "String8" + ["p1"]=> + int(0) + ["p2"]=> + int(2) + ["p3"]=> + int(0) + ["p4"]=> + string(13) "second_insert" + ["p5"]=> + int(0) + ["comment"]=> + %a + } + [%d]=> + array(8) { + ["addr"]=> + int(%d) + ["opcode"]=> + string(5) "Yield" + ["p1"]=> + int(3) + ["p2"]=> + int(0) + ["p3"]=> + int(0) + ["p4"]=> + NULL + ["p5"]=> + int(0) + ["comment"]=> + %a + } + [%d]=> + array(8) { + ["addr"]=> + int(%d) + ["opcode"]=> + string(12) "EndCoroutine" + ["p1"]=> + int(3) + ["p2"]=> + int(0) + ["p3"]=> + int(0) + ["p4"]=> + NULL + ["p5"]=> + int(0) + ["comment"]=> + %a + } + [%d]=> + array(8) { + ["addr"]=> + int(%d) + ["opcode"]=> + string(9) "OpenWrite" + ["p1"]=> + int(0) + ["p2"]=> + int(2) + ["p3"]=> + int(0) + ["p4"]=> + string(1) "1" + ["p5"]=> + int(0) + ["comment"]=> + %a + } + [%d]=> + array(8) { + ["addr"]=> + int(%d) + ["opcode"]=> + string(5) "Yield" + ["p1"]=> + int(3) + ["p2"]=> + int(%d) + ["p3"]=> + int(0) + ["p4"]=> + NULL + ["p5"]=> + int(0) + ["comment"]=> + %a + } + [%d]=> + array(8) { + ["addr"]=> + int(%d) + ["opcode"]=> + string(8) "NewRowid" + ["p1"]=> + int(0) + ["p2"]=> + int(1) + ["p3"]=> + int(0) + ["p4"]=> + NULL + ["p5"]=> + int(0) + ["comment"]=> + %a + } + [%d]=> + array(8) { + ["addr"]=> + int(%d) + ["opcode"]=> + string(10) "MakeRecord" + ["p1"]=> + int(2) + ["p2"]=> + int(1) + ["p3"]=> + int(4) + ["p4"]=> + string(1) "C" + ["p5"]=> + int(0) + ["comment"]=> + %a + } + [%d]=> + array(8) { + ["addr"]=> + int(%d) + ["opcode"]=> + string(6) "Insert" + ["p1"]=> + int(0) + ["p2"]=> + int(4) + ["p3"]=> + int(1) + ["p4"]=> + string(12) "test_explain" + ["p5"]=> + int(57) + ["comment"]=> + %a + } + [%d]=> + array(8) { + ["addr"]=> + int(%d) + ["opcode"]=> + string(4) "Goto" + ["p1"]=> + int(0) + ["p2"]=> + int(%d) + ["p3"]=> + int(0) + ["p4"]=> + NULL + ["p5"]=> + int(0) + ["comment"]=> + %a + } + [%d]=> + array(8) { + ["addr"]=> + int(%d) + ["opcode"]=> + string(4) "Halt" + ["p1"]=> + int(0) + ["p2"]=> + int(0) + ["p3"]=> + int(0) + ["p4"]=> + NULL + ["p5"]=> + int(0) + ["comment"]=> + %a + } + [%d]=> + array(8) { + ["addr"]=> + int(%d) + ["opcode"]=> + string(11) "Transaction" + ["p1"]=> + int(0) + ["p2"]=> + int(1) + ["p3"]=> + int(1) + ["p4"]=> + string(1) "0" + ["p5"]=> + int(1) + ["comment"]=> + %a + } + [%d]=> + array(8) { + ["addr"]=> + int(%d) + ["opcode"]=> + string(4) "Goto" + ["p1"]=> + int(0) + ["p2"]=> + int(1) + ["p3"]=> + int(0) + ["p4"]=> + NULL + ["p5"]=> + int(0) + ["comment"]=> + %a + } +} +array(1) { + [0]=> + array(4) { + ["id"]=> + int(2) + ["parent"]=> + int(0) + ["notused"]=> + int(0) + ["detail"]=> + string(17) "SCAN test_explain" + } +} +array(2) { + [0]=> + array(1) { + ["a"]=> + string(12) "first insert" + } + [1]=> + array(1) { + ["a"]=> + string(13) "second_insert" + } +} +SQLite3Stmt::setExplain(): Argument #1 ($mode) must be one of the SQLite3Stmt::EXPLAIN_MODE_* constants +SQLite3Stmt::setExplain(): Argument #1 ($mode) must be one of the SQLite3Stmt::EXPLAIN_MODE_* constants +bool(true) From 375316d0e24259077a4709bc21e0c0c628611a18 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Thu, 19 Jun 2025 23:38:51 +0100 Subject: [PATCH 132/682] ext/sqlite3: Sqlite3Result::fetchAll() support associative and indexes arrays for results. close GH-1884 --- NEWS | 2 + UPGRADING | 5 ++ ext/sqlite3/sqlite3.c | 101 ++++++++++++++++----- ext/sqlite3/sqlite3.stub.php | 2 + ext/sqlite3/sqlite3_arginfo.h | 12 +-- ext/sqlite3/tests/sqlite3_fetch_all.phpt | 108 +++++++++++++++++++++++ 6 files changed, 205 insertions(+), 25 deletions(-) create mode 100644 ext/sqlite3/tests/sqlite3_fetch_all.phpt diff --git a/NEWS b/NEWS index ccda0d50af84e..1ce8ce754adc9 100644 --- a/NEWS +++ b/NEWS @@ -249,6 +249,8 @@ PHP NEWS (David Carlier) . Added Sqlite3Stmt::explain to produce a explain query plan from the statement. (David Carlier) + . Added Sqlite3Result::fetchAll to returns all results at once from a query. + (David Carlier) - Standard: . Fixed crypt() tests on musl when using --with-external-libcrypt diff --git a/UPGRADING b/UPGRADING index e551f0c894260..4c6e40cfdebaf 100644 --- a/UPGRADING +++ b/UPGRADING @@ -212,6 +212,11 @@ PHP 8.5 UPGRADE NOTES now have an optional $lang parameter. This support solves compatibility with .NET SOAP clients. +- Sqlite: + . Added class constants Sqlite3Stmt::EXPLAIN_MODE_PREPARED, + Sqlite3Stmt::EXPLAIN_MODE_EXPLAIN and + Sqlite3Stmt::EXPLAIN_MODE_EXPLAIN_QUERY_PLAN. + - XSL: . The $namespace argument of XSLTProcessor::getParameter(), XSLTProcessor::setParameter() and XSLTProcessor::removeParameter() diff --git a/ext/sqlite3/sqlite3.c b/ext/sqlite3/sqlite3.c index 349d2823bb1e0..5e402300980bb 100644 --- a/ext/sqlite3/sqlite3.c +++ b/ext/sqlite3/sqlite3.c @@ -39,6 +39,7 @@ static PHP_GINIT_FUNCTION(sqlite3); static int php_sqlite3_authorizer(void *autharg, int action, const char *arg1, const char *arg2, const char *arg3, const char *arg4); static void sqlite3_param_dtor(zval *data); static int php_sqlite3_compare_stmt_free(php_sqlite3_stmt **stmt_obj_ptr, sqlite3_stmt *statement); +static zend_always_inline void php_sqlite3_fetch_one(int n_cols, php_sqlite3_result *result_obj, zend_long mode, zval *result); #define SQLITE3_CHECK_INITIALIZED(db_obj, member, class_name) \ if (!(db_obj) || !(member)) { \ @@ -1991,7 +1992,7 @@ PHP_METHOD(SQLite3Result, fetchArray) { php_sqlite3_result *result_obj; zval *object = ZEND_THIS; - int i, ret; + int ret; zend_long mode = PHP_SQLITE3_BOTH; result_obj = Z_SQLITE3_RESULT_P(object); @@ -2028,26 +2029,8 @@ PHP_METHOD(SQLite3Result, fetchArray) array_init(return_value); - for (i = 0; i < n_cols; i++) { - zval data; - - sqlite_value_to_zval(result_obj->stmt_obj->stmt, i, &data); - - if (mode & PHP_SQLITE3_NUM) { - add_index_zval(return_value, i, &data); - } + php_sqlite3_fetch_one(n_cols, result_obj, mode, return_value); - if (mode & PHP_SQLITE3_ASSOC) { - if (mode & PHP_SQLITE3_NUM) { - if (Z_REFCOUNTED(data)) { - Z_ADDREF(data); - } - } - /* Note: we can't use the "add_new" variant here instead of "update" because - * when the same column name is encountered, the last result should be taken. */ - zend_symtable_update(Z_ARR_P(return_value), result_obj->column_names[i], &data); - } - } break; case SQLITE_DONE: @@ -2071,6 +2054,61 @@ static void sqlite3result_clear_column_names_cache(php_sqlite3_result *result) { result->column_count = -1; } +PHP_METHOD(SQLite3Result, fetchAll) +{ + int i, nb_cols; + bool done = false; + php_sqlite3_result *result_obj; + zval *object = ZEND_THIS; + zend_long mode = PHP_SQLITE3_BOTH; + result_obj = Z_SQLITE3_RESULT_P(object); + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(mode) + ZEND_PARSE_PARAMETERS_END(); + + SQLITE3_CHECK_INITIALIZED(result_obj->db_obj, result_obj->stmt_obj->initialised, SQLite3Result) + + nb_cols = sqlite3_column_count(result_obj->stmt_obj->stmt); + if (mode & PHP_SQLITE3_ASSOC) { + sqlite3result_clear_column_names_cache(result_obj); + result_obj->column_names = emalloc(nb_cols * sizeof(zend_string*)); + + for (i = 0; i < nb_cols; i++) { + const char *column = sqlite3_column_name(result_obj->stmt_obj->stmt, i); + result_obj->column_names[i] = zend_string_init(column, strlen(column), 0); + } + } + result_obj->column_count = nb_cols; + array_init(return_value); + + while (!done) { + int step = sqlite3_step(result_obj->stmt_obj->stmt); + + switch (step) { + case SQLITE_ROW: { + zval result; + array_init_size(&result, result_obj->column_count); + + php_sqlite3_fetch_one(result_obj->column_count, result_obj, mode, &result); + + add_next_index_zval(return_value, &result); + break; + } + case SQLITE_DONE: + done = true; + break; + default: + if (!EG(exception)) { + php_sqlite3_error(result_obj->db_obj, sqlite3_errcode(sqlite3_db_handle(result_obj->stmt_obj->stmt)), "Unable to execute statement: %s", sqlite3_errmsg(sqlite3_db_handle(result_obj->stmt_obj->stmt))); + } + zval_ptr_dtor(return_value); + RETURN_FALSE; + } + } +} + /* {{{ Resets the result set back to the first row. */ PHP_METHOD(SQLite3Result, reset) { @@ -2429,6 +2467,29 @@ static void sqlite3_param_dtor(zval *data) /* {{{ */ } /* }}} */ +static zend_always_inline void php_sqlite3_fetch_one(int n_cols, php_sqlite3_result *result_obj, zend_long mode, zval *result) +{ + for (int i = 0; i < n_cols; i ++) { + zval data; + sqlite_value_to_zval(result_obj->stmt_obj->stmt, i, &data); + + if (mode & PHP_SQLITE3_NUM) { + add_index_zval(result, i, &data); + } + + if (mode & PHP_SQLITE3_ASSOC) { + if (mode & PHP_SQLITE3_NUM) { + if (Z_REFCOUNTED(data)) { + Z_ADDREF(data); + } + } + /* Note: we can't use the "add_new" variant here instead of "update" because + * when the same column name is encountered, the last result should be taken. */ + zend_symtable_update(Z_ARR_P(result), result_obj->column_names[i], &data); + } + } +} + /* {{{ PHP_MINIT_FUNCTION */ PHP_MINIT_FUNCTION(sqlite3) { diff --git a/ext/sqlite3/sqlite3.stub.php b/ext/sqlite3/sqlite3.stub.php index 55af378b325c5..54a7b41ec5576 100644 --- a/ext/sqlite3/sqlite3.stub.php +++ b/ext/sqlite3/sqlite3.stub.php @@ -302,6 +302,8 @@ public function columnType(int $column): int|false {} /** @tentative-return-type */ public function fetchArray(int $mode = SQLITE3_BOTH): array|false {} + public function fetchAll(int $mode = SQLITE3_BOTH): array|false {} + /** @tentative-return-type */ public function reset(): bool {} diff --git a/ext/sqlite3/sqlite3_arginfo.h b/ext/sqlite3/sqlite3_arginfo.h index e306af04538b3..54ca70cad5c53 100644 --- a/ext/sqlite3/sqlite3_arginfo.h +++ b/ext/sqlite3/sqlite3_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: c3216eada9881743cbd3aa1510f1200b7ce0d942 */ + * Stub hash: da91c32c6070c808d6e1b01894b5f8beedda7b45 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_SQLite3___construct, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, filename, IS_STRING, 0) @@ -173,6 +173,10 @@ ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_MASK_EX(arginfo_class_SQLite3Result_fe ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_LONG, 0, "SQLITE3_BOTH") ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_SQLite3Result_fetchAll, 0, 0, MAY_BE_ARRAY|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_LONG, 0, "SQLITE3_BOTH") +ZEND_END_ARG_INFO() + #define arginfo_class_SQLite3Result_reset arginfo_class_SQLite3_close #define arginfo_class_SQLite3Result_finalize arginfo_class_SQLite3Stmt_close @@ -224,6 +228,7 @@ ZEND_METHOD(SQLite3Result, numColumns); ZEND_METHOD(SQLite3Result, columnName); ZEND_METHOD(SQLite3Result, columnType); ZEND_METHOD(SQLite3Result, fetchArray); +ZEND_METHOD(SQLite3Result, fetchAll); ZEND_METHOD(SQLite3Result, reset); ZEND_METHOD(SQLite3Result, finalize); @@ -284,6 +289,7 @@ static const zend_function_entry class_SQLite3Result_methods[] = { ZEND_ME(SQLite3Result, columnName, arginfo_class_SQLite3Result_columnName, ZEND_ACC_PUBLIC) ZEND_ME(SQLite3Result, columnType, arginfo_class_SQLite3Result_columnType, ZEND_ACC_PUBLIC) ZEND_ME(SQLite3Result, fetchArray, arginfo_class_SQLite3Result_fetchArray, ZEND_ACC_PUBLIC) + ZEND_ME(SQLite3Result, fetchAll, arginfo_class_SQLite3Result_fetchAll, ZEND_ACC_PUBLIC) ZEND_ME(SQLite3Result, reset, arginfo_class_SQLite3Result_reset, ZEND_ACC_PUBLIC) ZEND_ME(SQLite3Result, finalize, arginfo_class_SQLite3Result_finalize, ZEND_ACC_PUBLIC) ZEND_FE_END @@ -564,16 +570,12 @@ static zend_class_entry *register_class_SQLite3Stmt(void) zend_string *const_EXPLAIN_MODE_PREPARED_name = zend_string_init_interned("EXPLAIN_MODE_PREPARED", sizeof("EXPLAIN_MODE_PREPARED") - 1, 1); zend_declare_typed_class_constant(class_entry, const_EXPLAIN_MODE_PREPARED_name, &const_EXPLAIN_MODE_PREPARED_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release(const_EXPLAIN_MODE_PREPARED_name); -#endif -#if SQLITE_VERSION_NUMBER >= 3043000 zval const_EXPLAIN_MODE_EXPLAIN_value; ZVAL_LONG(&const_EXPLAIN_MODE_EXPLAIN_value, 1); zend_string *const_EXPLAIN_MODE_EXPLAIN_name = zend_string_init_interned("EXPLAIN_MODE_EXPLAIN", sizeof("EXPLAIN_MODE_EXPLAIN") - 1, 1); zend_declare_typed_class_constant(class_entry, const_EXPLAIN_MODE_EXPLAIN_name, &const_EXPLAIN_MODE_EXPLAIN_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release(const_EXPLAIN_MODE_EXPLAIN_name); -#endif -#if SQLITE_VERSION_NUMBER >= 3043000 zval const_EXPLAIN_MODE_EXPLAIN_QUERY_PLAN_value; ZVAL_LONG(&const_EXPLAIN_MODE_EXPLAIN_QUERY_PLAN_value, 2); diff --git a/ext/sqlite3/tests/sqlite3_fetch_all.phpt b/ext/sqlite3/tests/sqlite3_fetch_all.phpt new file mode 100644 index 0000000000000..5a318afb12561 --- /dev/null +++ b/ext/sqlite3/tests/sqlite3_fetch_all.phpt @@ -0,0 +1,108 @@ +--TEST-- +SQLite3Result::fetchAll usage +--EXTENSIONS-- +sqlite3 +--FILE-- +query('CREATE TABLE users (id INTEGER NOT NULL, num INTEGER NOT NULL, PRIMARY KEY(id))'); + +$stmt = $conn->query('insert into users (id, num) values (1, 1)'); +$stmt = $conn->query('insert into users (id, num) values (2, 2)'); + +$stmt = $conn->query('SELECT * FROM users'); +$rowall = $stmt->fetchAll(); +var_dump($rowall); +$stmt->reset(); +$rowfetch = []; +while (($row = $stmt->fetchArray())) $rowfetch[] = $row; +var_dump($rowfetch); +var_dump($rowall == $rowfetch); +$stmt->reset(); +var_dump($stmt->fetchAll(SQLITE3_NUM)); +$stmt->reset(); +var_dump($stmt->fetchAll(SQLITE3_ASSOC)); + +?> +--EXPECT-- +array(2) { + [0]=> + array(4) { + [0]=> + int(1) + ["id"]=> + int(1) + [1]=> + int(1) + ["num"]=> + int(1) + } + [1]=> + array(4) { + [0]=> + int(2) + ["id"]=> + int(2) + [1]=> + int(2) + ["num"]=> + int(2) + } +} +array(2) { + [0]=> + array(4) { + [0]=> + int(1) + ["id"]=> + int(1) + [1]=> + int(1) + ["num"]=> + int(1) + } + [1]=> + array(4) { + [0]=> + int(2) + ["id"]=> + int(2) + [1]=> + int(2) + ["num"]=> + int(2) + } +} +bool(true) +array(2) { + [0]=> + array(2) { + [0]=> + int(1) + [1]=> + int(1) + } + [1]=> + array(2) { + [0]=> + int(2) + [1]=> + int(2) + } +} +array(2) { + [0]=> + array(2) { + ["id"]=> + int(1) + ["num"]=> + int(1) + } + [1]=> + array(2) { + ["id"]=> + int(2) + ["num"]=> + int(2) + } +} From 81865ec5bdcba61b1a4bb39158960d547f4424e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Mon, 23 Jun 2025 12:14:00 +0200 Subject: [PATCH 133/682] uri: Improve exceptions for Uri\WhatWg\Url (#18855) A more specific exception message is used, while the code is simplified. --- ext/uri/php_lexbor.c | 65 +++++++++++++++++++++--------------------- ext/uri/tests/004.phpt | 4 +-- ext/uri/tests/007.phpt | 2 +- ext/uri/tests/023.phpt | 4 +-- ext/uri/tests/026.phpt | 4 +-- ext/uri/tests/051.phpt | 2 +- 6 files changed, 40 insertions(+), 41 deletions(-) diff --git a/ext/uri/php_lexbor.c b/ext/uri/php_lexbor.c index 82f3919bb6a97..44bca30f8fda7 100644 --- a/ext/uri/php_lexbor.c +++ b/ext/uri/php_lexbor.c @@ -73,10 +73,10 @@ static void lexbor_cleanup_parser(void) * When errors is NULL, the caller is not interested in the additional error information, * so the function does nothing. */ -static void fill_errors(zval *errors) +static zend_string *fill_errors(zval *errors) { if (errors == NULL) { - return; + return NULL; } ZEND_ASSERT(Z_ISUNDEF_P(errors)); @@ -84,9 +84,10 @@ static void fill_errors(zval *errors) array_init(errors); if (lexbor_parser.log == NULL) { - return; + return NULL; } + zend_string *result = NULL; lexbor_plog_entry_t *lxb_error; while ((lxb_error = lexbor_array_obj_pop(&lexbor_parser.log->list)) != NULL) { zval error; @@ -223,32 +224,29 @@ static void fill_errors(zval *errors) zend_update_property(uri_whatwg_url_validation_error_ce, Z_OBJ(error), ZEND_STRL("failure"), &failure); + if (Z_TYPE(failure) == IS_TRUE) { + result = error_str; + } + add_next_index_zval(errors, &error); } -} - -static void throw_invalid_url_exception(zval *errors) -{ - ZEND_ASSERT(errors != NULL && Z_TYPE_P(errors) == IS_ARRAY); - - zval exception; - - object_init_ex(&exception, uri_whatwg_invalid_url_exception_ce); - - zval value; - ZVAL_STRING(&value, "URL parsing failed"); - zend_update_property_ex(uri_whatwg_invalid_url_exception_ce, Z_OBJ(exception), ZSTR_KNOWN(ZEND_STR_MESSAGE), &value); - zval_ptr_dtor_str(&value); - - zend_update_property(uri_whatwg_invalid_url_exception_ce, Z_OBJ(exception), ZEND_STRL("errors"), errors); - zend_throw_exception_object(&exception); + return result; } -static void throw_invalid_url_exception_during_write(zval *errors) +static void throw_invalid_url_exception_during_write(zval *errors, const char *component) { - fill_errors(errors); - throw_invalid_url_exception(errors); + zend_string *reason = fill_errors(errors); + zend_object *exception = zend_throw_exception_ex( + uri_whatwg_invalid_url_exception_ce, + 0, + "The specified %s is malformed%s%s%s", + component, + reason ? " (" : "", + reason ? ZSTR_VAL(reason) : "", + reason ? ")" : "" + ); + zend_update_property(exception->ce, exception, ZEND_STRL("errors"), errors); } static lxb_status_t lexbor_serialize_callback(const lxb_char_t *data, size_t length, void *ctx) @@ -281,7 +279,7 @@ static zend_result lexbor_write_scheme(struct uri_internal_t *internal_uri, zval zval_string_or_null_to_lexbor_str(value, &str); if (lxb_url_api_protocol_set(lexbor_uri, &lexbor_parser, str.data, str.length) != LXB_STATUS_OK) { - throw_invalid_url_exception_during_write(errors); + throw_invalid_url_exception_during_write(errors, "scheme"); return FAILURE; } @@ -310,7 +308,7 @@ static zend_result lexbor_write_username(uri_internal_t *internal_uri, zval *val zval_string_or_null_to_lexbor_str(value, &str); if (lxb_url_api_username_set(lexbor_uri, str.data, str.length) != LXB_STATUS_OK) { - throw_invalid_url_exception_during_write(errors); + throw_invalid_url_exception_during_write(errors, "username"); return FAILURE; } @@ -339,7 +337,7 @@ static zend_result lexbor_write_password(struct uri_internal_t *internal_uri, zv zval_string_or_null_to_lexbor_str(value, &str); if (lxb_url_api_password_set(lexbor_uri, str.data, str.length) != LXB_STATUS_OK) { - throw_invalid_url_exception_during_write(errors); + throw_invalid_url_exception_during_write(errors, "password"); return FAILURE; } @@ -411,7 +409,7 @@ static zend_result lexbor_write_host(struct uri_internal_t *internal_uri, zval * zval_string_or_null_to_lexbor_str(value, &str); if (lxb_url_api_hostname_set(lexbor_uri, &lexbor_parser, str.data, str.length) != LXB_STATUS_OK) { - throw_invalid_url_exception_during_write(errors); + throw_invalid_url_exception_during_write(errors, "host"); return FAILURE; } @@ -440,7 +438,7 @@ static zend_result lexbor_write_port(struct uri_internal_t *internal_uri, zval * zval_long_or_null_to_lexbor_str(value, &str); if (lxb_url_api_port_set(lexbor_uri, &lexbor_parser, str.data, str.length) != LXB_STATUS_OK) { - throw_invalid_url_exception_during_write(errors); + throw_invalid_url_exception_during_write(errors, "port"); return FAILURE; } @@ -469,7 +467,7 @@ static zend_result lexbor_write_path(struct uri_internal_t *internal_uri, zval * zval_string_or_null_to_lexbor_str(value, &str); if (lxb_url_api_pathname_set(lexbor_uri, &lexbor_parser, str.data, str.length) != LXB_STATUS_OK) { - throw_invalid_url_exception_during_write(errors); + throw_invalid_url_exception_during_write(errors, "path"); return FAILURE; } @@ -498,7 +496,7 @@ static zend_result lexbor_write_query(struct uri_internal_t *internal_uri, zval zval_string_or_null_to_lexbor_str(value, &str); if (lxb_url_api_search_set(lexbor_uri, &lexbor_parser, str.data, str.length) != LXB_STATUS_OK) { - throw_invalid_url_exception_during_write(errors); + throw_invalid_url_exception_during_write(errors, "query string"); return FAILURE; } @@ -527,7 +525,7 @@ static zend_result lexbor_write_fragment(struct uri_internal_t *internal_uri, zv zval_string_or_null_to_lexbor_str(value, &str); if (lxb_url_api_hash_set(lexbor_uri, &lexbor_parser, str.data, str.length) != LXB_STATUS_OK) { - throw_invalid_url_exception_during_write(errors); + throw_invalid_url_exception_during_write(errors, "fragment"); return FAILURE; } @@ -569,10 +567,11 @@ lxb_url_t *lexbor_parse_uri_ex(const zend_string *uri_str, const lxb_url_t *lexb lexbor_cleanup_parser(); lxb_url_t *url = lxb_url_parse(&lexbor_parser, lexbor_base_url, (unsigned char *) ZSTR_VAL(uri_str), ZSTR_LEN(uri_str)); - fill_errors(errors); + zend_string *reason = fill_errors(errors); if (url == NULL && !silent) { - throw_invalid_url_exception(errors); + zend_object *exception = zend_throw_exception_ex(uri_whatwg_invalid_url_exception_ce, 0, "The specified URI is malformed%s%s%s", reason ? " (" : "", reason ? ZSTR_VAL(reason) : "", reason ? ")" : ""); + zend_update_property(exception->ce, exception, ZEND_STRL("errors"), errors); } return url; diff --git a/ext/uri/tests/004.phpt b/ext/uri/tests/004.phpt index 04127a7ded0d3..abbad59fee2e8 100644 --- a/ext/uri/tests/004.phpt +++ b/ext/uri/tests/004.phpt @@ -18,8 +18,8 @@ var_dump(Uri\WhatWg\Url::parse("192.168/contact.html", null)); var_dump(Uri\WhatWg\Url::parse("http://RuPaul's Drag Race All Stars 7 Winners Cast on This Season's", null)); ?> ---EXPECTF-- -URL parsing failed +--EXPECT-- +The specified URI is malformed (MissingSchemeNonRelativeUrl) NULL NULL NULL diff --git a/ext/uri/tests/007.phpt b/ext/uri/tests/007.phpt index e60e69fc113a3..cb445fcf71a43 100644 --- a/ext/uri/tests/007.phpt +++ b/ext/uri/tests/007.phpt @@ -19,7 +19,7 @@ var_dump($failures); ?> --EXPECTF-- -URL parsing failed +The specified URI is malformed (PortInvalid) array(%d) { [0]=> object(Uri\WhatWg\UrlValidationError)#%d (%d) { diff --git a/ext/uri/tests/023.phpt b/ext/uri/tests/023.phpt index b48e2df838eef..a1ca06bd6f6e5 100644 --- a/ext/uri/tests/023.phpt +++ b/ext/uri/tests/023.phpt @@ -27,5 +27,5 @@ try { --EXPECT-- string(5) "https" string(4) "http" -URL parsing failed -URL parsing failed +The specified scheme is malformed +The specified scheme is malformed diff --git a/ext/uri/tests/026.phpt b/ext/uri/tests/026.phpt index 4640ebebae52d..4763ea9d4406c 100644 --- a/ext/uri/tests/026.phpt +++ b/ext/uri/tests/026.phpt @@ -43,7 +43,7 @@ string(8) "test.com" string(8) "test.com" string(8) "test.com" NULL -URL parsing failed +The specified host is malformed (DomainInvalidCodePoint) object(Uri\WhatWg\Url)#%d (%d) { ["scheme"]=> string(5) "https" @@ -62,6 +62,6 @@ object(Uri\WhatWg\Url)#%d (%d) { ["fragment"]=> NULL } -URL parsing failed +The specified host is malformed (HostMissing) string(7) "foo.com" string(8) "test.com" diff --git a/ext/uri/tests/051.phpt b/ext/uri/tests/051.phpt index 5911f8767567c..ad4751470e23b 100644 --- a/ext/uri/tests/051.phpt +++ b/ext/uri/tests/051.phpt @@ -20,7 +20,7 @@ var_dump($softErrors); ?> --EXPECTF-- -URL parsing failed +The specified URI is malformed (Ipv4TooManyParts) string(23) "https://example.com/foo" array(%d) { [0]=> From 2ccd2b016df2c4cf8ff36a65b5875f1a7e39ac21 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sat, 14 Jun 2025 11:11:38 +0100 Subject: [PATCH 134/682] ext/calendar: jewishtojd overflow on year argument. Upper limit set to the 7th millenium (Messianic Age) in the jewish calendar, around 2239 year in the gregorian calendar. close GH-18849 --- NEWS | 3 +++ ext/calendar/calendar.c | 5 +++++ ext/calendar/jewish.c | 2 +- ext/calendar/tests/gh16234_2.phpt | 11 +++++++++++ ext/calendar/tests/gh16234_2_64.phpt | 21 +++++++++++++++++++++ 5 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 ext/calendar/tests/gh16234_2.phpt create mode 100644 ext/calendar/tests/gh16234_2_64.phpt diff --git a/NEWS b/NEWS index ea77125b205eb..fd344ee94c72e 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,9 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? ????, PHP 8.3.24 +- Calendar: + . Fixed jewishtojd overflow on year argument. (David Carlier) + - Core: . Fixed bug GH-18833 (Use after free with weakmaps dependent on destruction order). (Daniil Gentili) diff --git a/ext/calendar/calendar.c b/ext/calendar/calendar.c index 756ce0e90dc98..6da7e69529e2e 100644 --- a/ext/calendar/calendar.c +++ b/ext/calendar/calendar.c @@ -490,6 +490,11 @@ PHP_FUNCTION(jewishtojd) RETURN_THROWS(); } + if (ZEND_LONG_EXCEEDS_INT(year)) { + zend_argument_value_error(3, "must be between %d and %d", INT_MIN, INT_MAX); + RETURN_THROWS(); + } + RETURN_LONG(JewishToSdn(year, month, day)); } /* }}} */ diff --git a/ext/calendar/jewish.c b/ext/calendar/jewish.c index bdfc9b4f91016..2fbdcb059b096 100644 --- a/ext/calendar/jewish.c +++ b/ext/calendar/jewish.c @@ -714,7 +714,7 @@ zend_long JewishToSdn( int yearLength; int lengthOfAdarIAndII; - if (year <= 0 || day <= 0 || day > 30) { + if (year <= 0 || year >= 6000 || day <= 0 || day > 30) { return (0); } switch (month) { diff --git a/ext/calendar/tests/gh16234_2.phpt b/ext/calendar/tests/gh16234_2.phpt new file mode 100644 index 0000000000000..76db2b9abf269 --- /dev/null +++ b/ext/calendar/tests/gh16234_2.phpt @@ -0,0 +1,11 @@ +--TEST-- +GH-16234 jewishtojd overflow on year argument +--EXTENSIONS-- +calendar +--FILE-- + +--EXPECTF-- +DONE diff --git a/ext/calendar/tests/gh16234_2_64.phpt b/ext/calendar/tests/gh16234_2_64.phpt new file mode 100644 index 0000000000000..7da2546096509 --- /dev/null +++ b/ext/calendar/tests/gh16234_2_64.phpt @@ -0,0 +1,21 @@ +--TEST-- +GH-16234 jewishtojd overflow on year argument +--EXTENSIONS-- +calendar +--SKIPIF-- + +--FILE-- +getMessage(), PHP_EOL; +} +?> +--EXPECTF-- +jewishtojd(): Argument #3 ($year) must be between %i and %d + From 67bbf9c9610465e42378b34301505f26045aed8d Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Fri, 20 Jun 2025 15:17:51 +0200 Subject: [PATCH 135/682] ext/filter: Remove ZPP test --- ext/filter/tests/057.phpt | 52 --------------------------------------- 1 file changed, 52 deletions(-) delete mode 100644 ext/filter/tests/057.phpt diff --git a/ext/filter/tests/057.phpt b/ext/filter/tests/057.phpt deleted file mode 100644 index ec8083e2f891d..0000000000000 --- a/ext/filter/tests/057.phpt +++ /dev/null @@ -1,52 +0,0 @@ ---TEST-- -filter_input_array() and filter_var_array() with invalid $definition arguments ---EXTENSIONS-- -filter ---FILE-- -getMessage() . "\n"; - } - - try { - var_dump(filter_var_array(array(), $invalid)); - } catch (TypeError $exception) { - echo $exception->getMessage() . "\n"; - } -} -?> ---EXPECTF-- -Deprecated: filter_input_array(): Passing null to parameter #2 ($options) of type array|int is deprecated in %s on line %d - -Warning: filter_input_array(): Unknown filter with ID 0 in %s on line %d -bool(false) - -Deprecated: filter_var_array(): Passing null to parameter #2 ($options) of type array|int is deprecated in %s on line %d - -Warning: filter_var_array(): Unknown filter with ID 0 in %s on line %d -bool(false) - -Warning: filter_input_array(): Unknown filter with ID 1 in %s on line %d -bool(false) - -Warning: filter_var_array(): Unknown filter with ID 1 in %s on line %d -bool(false) - -Warning: filter_input_array(): Unknown filter with ID 0 in %s on line %d -bool(false) - -Warning: filter_var_array(): Unknown filter with ID 0 in %s on line %d -bool(false) - -Warning: filter_input_array(): Unknown filter with ID 1 in %s on line %d -bool(false) - -Warning: filter_var_array(): Unknown filter with ID 1 in %s on line %d -bool(false) -filter_input_array(): Argument #2 ($options) must be of type array|int, string given -filter_var_array(): Argument #2 ($options) must be of type array|int, string given -filter_input_array(): Argument #2 ($options) must be of type array|int, stdClass given -filter_var_array(): Argument #2 ($options) must be of type array|int, stdClass given From c03f6065fa9be4f898bddc351451ba964f8c75bb Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Fri, 20 Jun 2025 15:20:16 +0200 Subject: [PATCH 136/682] ext/calendar: Remove ZPP test --- ext/calendar/tests/unixtojd_error1.phpt | 2 -- 1 file changed, 2 deletions(-) diff --git a/ext/calendar/tests/unixtojd_error1.phpt b/ext/calendar/tests/unixtojd_error1.phpt index 6b8fad05ebd83..171d400b99e76 100644 --- a/ext/calendar/tests/unixtojd_error1.phpt +++ b/ext/calendar/tests/unixtojd_error1.phpt @@ -15,7 +15,6 @@ try { } catch (ValueError $ex) { echo $ex->getMessage(), PHP_EOL; } -var_dump(unixtojd(false)) . PHP_EOL; var_dump(unixtojd(null)) . PHP_EOL; var_dump(unixtojd(time())) . PHP_EOL; ?> @@ -23,4 +22,3 @@ var_dump(unixtojd(time())) . PHP_EOL; unixtojd(): Argument #1 ($timestamp) must be greater than or equal to 0 int(%d) int(%d) -int(%d) From c7778641dde526cc6024cc2b6aff321452473c34 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Fri, 20 Jun 2025 15:52:30 +0200 Subject: [PATCH 137/682] ext/mbstring: Remove ZPP tests --- ext/mbstring/tests/bug43994.phpt | 59 ++---- ext/mbstring/tests/bug43998.phpt | 42 +---- ext/mbstring/tests/gh16229.phpt | 2 +- .../tests/mb_ereg_replace_variation1.phpt | 65 +------ ...titute_character_variation_weak_types.phpt | 172 ------------------ 5 files changed, 29 insertions(+), 311 deletions(-) delete mode 100644 ext/mbstring/tests/mb_substitute_character_variation_weak_types.phpt diff --git a/ext/mbstring/tests/bug43994.phpt b/ext/mbstring/tests/bug43994.phpt index 26f641f6d3d7e..b4ae29ff40e7f 100644 --- a/ext/mbstring/tests/bug43994.phpt +++ b/ext/mbstring/tests/bug43994.phpt @@ -14,55 +14,24 @@ function_exists('mb_ereg') or die("skip mb_ereg() is not available in this build * pattern is supplied to mb_ereg. Similar error message to ereg(). */ -$inputs = array(false, FALSE, "", ''); +$input = ''; +echo "Without \$regs arg:\n"; +try { + var_dump( mb_ereg($input, 'hello, world') ); +} catch (\ValueError $e) { + echo $e->getMessage() . \PHP_EOL; +} -$iterator = 1; -foreach($inputs as $input) { - if(@is_array($mb_regs)){ - $mb_regs = ''; - } - echo "\n-- Iteration $iterator --\n"; - echo "Without \$regs arg:\n"; - try { - var_dump( mb_ereg($input, 'hello, world') ); - } catch (\ValueError $e) { - echo $e->getMessage() . \PHP_EOL; - } +echo "With \$regs arg:\n"; +try { + var_dump(mb_ereg($input, 'hello, world', $mb_regs)); +} catch (\ValueError $e) { + echo $e->getMessage() . \PHP_EOL; +} - echo "With \$regs arg:\n"; - try { - var_dump(mb_ereg($input, 'hello, world', $mb_regs)); - } catch (\ValueError $e) { - echo $e->getMessage() . \PHP_EOL; - } - - var_dump($mb_regs); - $iterator++; -}; +var_dump($mb_regs); ?> --EXPECT-- --- Iteration 1 -- -Without $regs arg: -mb_ereg(): Argument #1 ($pattern) must not be empty -With $regs arg: -mb_ereg(): Argument #1 ($pattern) must not be empty -NULL - --- Iteration 2 -- -Without $regs arg: -mb_ereg(): Argument #1 ($pattern) must not be empty -With $regs arg: -mb_ereg(): Argument #1 ($pattern) must not be empty -NULL - --- Iteration 3 -- -Without $regs arg: -mb_ereg(): Argument #1 ($pattern) must not be empty -With $regs arg: -mb_ereg(): Argument #1 ($pattern) must not be empty -NULL - --- Iteration 4 -- Without $regs arg: mb_ereg(): Argument #1 ($pattern) must not be empty With $regs arg: diff --git a/ext/mbstring/tests/bug43998.phpt b/ext/mbstring/tests/bug43998.phpt index 112ab88728341..5be1bd2cc9ffd 100644 --- a/ext/mbstring/tests/bug43998.phpt +++ b/ext/mbstring/tests/bug43998.phpt @@ -11,41 +11,19 @@ mbstring $sourcestring = 'Hello, World'; -$inputs = array(12345, 12.3456789000E-10, true, false, ""); -$iterator = 1; -foreach($inputs as $input) { - echo "\n-- Iteration $iterator --\n"; - try { - var_dump( mb_strtolower($sourcestring, $input) ); - } catch (\ValueError $e) { - echo $e->getMessage() . \PHP_EOL; - } - try { - var_dump( mb_strtoupper($sourcestring, $input) ); - } catch (\ValueError $e) { - echo $e->getMessage() . \PHP_EOL; - } - $iterator++; +$input = ""; +try { + var_dump( mb_strtolower($sourcestring, $input) ); +} catch (\ValueError $e) { + echo $e->getMessage() . \PHP_EOL; +} +try { + var_dump( mb_strtoupper($sourcestring, $input) ); +} catch (\ValueError $e) { + echo $e->getMessage() . \PHP_EOL; } ?> --EXPECT-- --- Iteration 1 -- -mb_strtolower(): Argument #2 ($encoding) must be a valid encoding, "12345" given -mb_strtoupper(): Argument #2 ($encoding) must be a valid encoding, "12345" given - --- Iteration 2 -- -mb_strtolower(): Argument #2 ($encoding) must be a valid encoding, "1.23456789E-9" given -mb_strtoupper(): Argument #2 ($encoding) must be a valid encoding, "1.23456789E-9" given - --- Iteration 3 -- -mb_strtolower(): Argument #2 ($encoding) must be a valid encoding, "1" given -mb_strtoupper(): Argument #2 ($encoding) must be a valid encoding, "1" given - --- Iteration 4 -- -mb_strtolower(): Argument #2 ($encoding) must be a valid encoding, "" given -mb_strtoupper(): Argument #2 ($encoding) must be a valid encoding, "" given - --- Iteration 5 -- mb_strtolower(): Argument #2 ($encoding) must be a valid encoding, "" given mb_strtoupper(): Argument #2 ($encoding) must be a valid encoding, "" given diff --git a/ext/mbstring/tests/gh16229.phpt b/ext/mbstring/tests/gh16229.phpt index 1fe558d9b1025..6e4924a091a54 100644 --- a/ext/mbstring/tests/gh16229.phpt +++ b/ext/mbstring/tests/gh16229.phpt @@ -14,7 +14,7 @@ if (!function_exists("mb_send_mail") || !mb_language("japanese")) { --FILE-- ---INI-- -error_reporting=E_ALL & ~E_NOTICE +Test mb_ereg_replace() function : usage variations - different input types --EXTENSIONS-- mbstring --SKIPIF-- @@ -17,22 +15,8 @@ $replacement = 'string_val'; $string = 'string_val'; $option = ''; -// get a class -class classA -{ - public function __toString() { - return "UTF-8"; - } -} - -// heredoc string -$heredoc = << 1, 'two' => 2); - -//array of values to iterate over -$inputs = array( - - // int data - 'int 0' => 0, - 'int 1' => 1, - 'int 12345' => 12345, - 'int -12345' => -2345, - - // float data - 'float 10.5' => 10.5, - 'float -10.5' => -10.5, - 'float 10.0e19' => 10.0e19, // Cannot be represented as int - 'float -10.0e19' => -10.0e19, // Cannot be represented as int - 'float .5' => .5, - - // array data - 'empty array' => array(), - 'int indexed array' => $index_array, - 'associative array' => $assoc_array, - 'nested arrays' => array('foo', $index_array, $assoc_array), - - // null data - 'uppercase NULL' => NULL, - 'lowercase null' => null, - - // boolean data - 'lowercase true' => true, - 'lowercase false' =>false, - 'uppercase TRUE' =>TRUE, - 'uppercase FALSE' =>FALSE, - - // empty data - 'empty string DQ' => "", - 'empty string SQ' => '', - - // string data - 'string DQ' => "string", - 'string SQ' => 'string', - 'mixed case string' => "sTrInG", - 'heredoc' => $heredoc, - - // object data - 'instance of classWithToString' => new classWithToString(), - 'instance of classWithoutToString' => new classWithoutToString(), - - // undefined data - 'undefined var' => @$undefined_var, - - // unset data - 'unset var' => @$unset_var, -); - -// loop through each element of the array for substchar - -mb_internal_encoding('utf-8'); -foreach($inputs as $key =>$value) { - echo "--$key--\n"; - try { - var_dump( mb_substitute_character($value) ); - } catch (\ValueError|\TypeError $e) { - echo get_class($e) . ': ' . $e->getMessage() . \PHP_EOL; - } -} - -fclose($fp); - -?> ---EXPECTF-- -*** Testing mb_substitute_character(): various types in weak typing mode *** ---int 0-- -bool(true) ---int 1-- -bool(true) ---int 12345-- -bool(true) ---int -12345-- -ValueError: mb_substitute_character(): Argument #1 ($substitute_character) is not a valid codepoint ---float 10.5-- - -Deprecated: Implicit conversion from float 10.5 to int loses precision in %s on line %d -bool(true) ---float -10.5-- - -Deprecated: Implicit conversion from float -10.5 to int loses precision in %s on line %d -ValueError: mb_substitute_character(): Argument #1 ($substitute_character) is not a valid codepoint ---float 10.0e19-- -ValueError: mb_substitute_character(): Argument #1 ($substitute_character) must be "none", "long", "entity" or a valid codepoint ---float -10.0e19-- -ValueError: mb_substitute_character(): Argument #1 ($substitute_character) must be "none", "long", "entity" or a valid codepoint ---float .5-- - -Deprecated: Implicit conversion from float 0.5 to int loses precision in %s on line %d -bool(true) ---empty array-- -TypeError: mb_substitute_character(): Argument #1 ($substitute_character) must be of type string|int|null, array given ---int indexed array-- -TypeError: mb_substitute_character(): Argument #1 ($substitute_character) must be of type string|int|null, array given ---associative array-- -TypeError: mb_substitute_character(): Argument #1 ($substitute_character) must be of type string|int|null, array given ---nested arrays-- -TypeError: mb_substitute_character(): Argument #1 ($substitute_character) must be of type string|int|null, array given ---uppercase NULL-- -int(0) ---lowercase null-- -int(0) ---lowercase true-- -bool(true) ---lowercase false-- -bool(true) ---uppercase TRUE-- -bool(true) ---uppercase FALSE-- -bool(true) ---empty string DQ-- -ValueError: mb_substitute_character(): Argument #1 ($substitute_character) must be "none", "long", "entity" or a valid codepoint ---empty string SQ-- -ValueError: mb_substitute_character(): Argument #1 ($substitute_character) must be "none", "long", "entity" or a valid codepoint ---string DQ-- -ValueError: mb_substitute_character(): Argument #1 ($substitute_character) must be "none", "long", "entity" or a valid codepoint ---string SQ-- -ValueError: mb_substitute_character(): Argument #1 ($substitute_character) must be "none", "long", "entity" or a valid codepoint ---mixed case string-- -ValueError: mb_substitute_character(): Argument #1 ($substitute_character) must be "none", "long", "entity" or a valid codepoint ---heredoc-- -ValueError: mb_substitute_character(): Argument #1 ($substitute_character) must be "none", "long", "entity" or a valid codepoint ---instance of classWithToString-- -ValueError: mb_substitute_character(): Argument #1 ($substitute_character) must be "none", "long", "entity" or a valid codepoint ---instance of classWithoutToString-- -TypeError: mb_substitute_character(): Argument #1 ($substitute_character) must be of type string|int|null, classWithoutToString given ---undefined var-- -int(0) ---unset var-- -int(0) From b068bef45dabcb061eef2bea07593676fdb6e1b8 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Mon, 23 Jun 2025 14:08:58 +0200 Subject: [PATCH 138/682] ext/dom: Remove bool type coercions in tests --- ext/dom/tests/bug46849.phpt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/dom/tests/bug46849.phpt b/ext/dom/tests/bug46849.phpt index a26fa2b2311db..a9cdd5bba578b 100644 --- a/ext/dom/tests/bug46849.phpt +++ b/ext/dom/tests/bug46849.phpt @@ -5,7 +5,7 @@ dom --FILE-- formatOutput = 1; +$dom->formatOutput = true; var_dump($dom->formatOutput); $dom2 = clone $dom; From 8cd4f95ea374748bc5e76774097658d27d1e99a8 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Mon, 23 Jun 2025 14:16:28 +0200 Subject: [PATCH 139/682] ext/pcntl: Remove bool type coercions in tests --- ext/pcntl/tests/async_signals.phpt | 2 +- ext/pcntl/tests/async_signals_2.phpt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/pcntl/tests/async_signals.phpt b/ext/pcntl/tests/async_signals.phpt index e9c1c96d4ea9c..dd0f602c25be2 100644 --- a/ext/pcntl/tests/async_signals.phpt +++ b/ext/pcntl/tests/async_signals.phpt @@ -5,7 +5,7 @@ pcntl posix --FILE-- Date: Mon, 23 Jun 2025 14:17:53 +0200 Subject: [PATCH 140/682] ext/posix: Remove ZPP tests --- ext/posix/tests/posix_seteuid_variation2.phpt | 38 ------------------- ext/posix/tests/posix_setgid_variation2.phpt | 38 ------------------- ext/posix/tests/posix_setuid_variation2.phpt | 38 ------------------- 3 files changed, 114 deletions(-) delete mode 100644 ext/posix/tests/posix_seteuid_variation2.phpt delete mode 100644 ext/posix/tests/posix_setgid_variation2.phpt delete mode 100644 ext/posix/tests/posix_setuid_variation2.phpt diff --git a/ext/posix/tests/posix_seteuid_variation2.phpt b/ext/posix/tests/posix_seteuid_variation2.phpt deleted file mode 100644 index 700f44e5c8f22..0000000000000 --- a/ext/posix/tests/posix_seteuid_variation2.phpt +++ /dev/null @@ -1,38 +0,0 @@ ---TEST-- -Test function posix_seteuid() by substituting argument 1 with boolean values. ---EXTENSIONS-- -posix ---SKIPIF-- - ---CREDITS-- -Marco Fabbri mrfabbri@gmail.com -Francesco Fullone ff@ideato.it -#PHPTestFest Cesena Italia on 2009-06-20 ---FILE-- - true, - 'lowercase false' =>false, - 'uppercase TRUE' =>TRUE, - 'uppercase FALSE' =>FALSE, - ); - - -foreach ( $variation_array as $var ) { - var_dump(posix_seteuid( $var ) ); -} -?> ---EXPECT-- -*** Test substituting argument 1 with boolean values *** -bool(false) -bool(false) -bool(false) -bool(false) diff --git a/ext/posix/tests/posix_setgid_variation2.phpt b/ext/posix/tests/posix_setgid_variation2.phpt deleted file mode 100644 index 8504706c54756..0000000000000 --- a/ext/posix/tests/posix_setgid_variation2.phpt +++ /dev/null @@ -1,38 +0,0 @@ ---TEST-- -Test function posix_setgid() by substituting argument 1 with boolean values. ---EXTENSIONS-- -posix ---SKIPIF-- - ---CREDITS-- -Marco Fabbri mrfabbri@gmail.com -Francesco Fullone ff@ideato.it -#PHPTestFest Cesena Italia on 2009-06-20 ---FILE-- - true, - 'lowercase false' =>false, - 'uppercase TRUE' =>TRUE, - 'uppercase FALSE' =>FALSE, - ); - - -foreach ( $variation_array as $var ) { - var_dump(posix_setgid( $var ) ); -} -?> ---EXPECT-- -*** Test substituting argument 1 with boolean values *** -bool(false) -bool(false) -bool(false) -bool(false) diff --git a/ext/posix/tests/posix_setuid_variation2.phpt b/ext/posix/tests/posix_setuid_variation2.phpt deleted file mode 100644 index 5b2f64f7859ef..0000000000000 --- a/ext/posix/tests/posix_setuid_variation2.phpt +++ /dev/null @@ -1,38 +0,0 @@ ---TEST-- -Test function posix_setuid() by substituting argument 1 with boolean values. ---EXTENSIONS-- -posix ---SKIPIF-- - ---CREDITS-- -Marco Fabbri mrfabbri@gmail.com -Francesco Fullone ff@ideato.it -#PHPTestFest Cesena Italia on 2009-06-20 ---FILE-- - true, - 'lowercase false' =>false, - 'uppercase TRUE' =>TRUE, - 'uppercase FALSE' =>FALSE, - ); - - -foreach ( $variation_array as $var ) { - var_dump(posix_setuid( $var ) ); -} -?> ---EXPECT-- -*** Test substituting argument 1 with boolean values *** -bool(false) -bool(false) -bool(false) -bool(false) From 4ff8d9f6b4a1e0fc84e4e5681fde499516e94186 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Mon, 23 Jun 2025 13:49:20 +0100 Subject: [PATCH 141/682] ext/uri: Remove bool type coercions in tests (#18921) --- ext/uri/tests/053.phpt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/uri/tests/053.phpt b/ext/uri/tests/053.phpt index 93ff77b15c0a5..2bd02365b09a0 100644 --- a/ext/uri/tests/053.phpt +++ b/ext/uri/tests/053.phpt @@ -25,13 +25,13 @@ try { } try { - $r->__construct("baz", [], false); + $r->__construct("baz", [], 0); } catch (Error $e) { echo $e->getMessage() . "\n"; } try { - $r->__construct("qax", [], false, null); + $r->__construct("qax", [], 0, null); } catch (Error $e) { echo $e->getMessage() . "\n"; } From 40be5fa99fc25007078f196dd7cdb26e805cc45f Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Mon, 23 Jun 2025 14:43:53 +0200 Subject: [PATCH 142/682] ext/sockets: Remove bool type coercions in tests --- ext/sockets/tests/socket_export_stream-1.phpt | 2 +- ext/sockets/tests/socket_export_stream-3.phpt | 2 +- ext/sockets/tests/socket_export_stream-4.phpt | 2 +- ext/sockets/tests/socket_import_stream-1.phpt | 2 +- ext/sockets/tests/socket_import_stream-3.phpt | 2 +- ext/sockets/tests/socket_import_stream-4.phpt | 5 ++--- 6 files changed, 7 insertions(+), 8 deletions(-) diff --git a/ext/sockets/tests/socket_export_stream-1.phpt b/ext/sockets/tests/socket_export_stream-1.phpt index b2a01ddcca10d..9b832a80aac33 100644 --- a/ext/sockets/tests/socket_export_stream-1.phpt +++ b/ext/sockets/tests/socket_export_stream-1.phpt @@ -5,7 +5,7 @@ sockets --FILE-- diff --git a/ext/sockets/tests/socket_export_stream-4.phpt b/ext/sockets/tests/socket_export_stream-4.phpt index 512a62379e98c..a3879a93b7cd8 100644 --- a/ext/sockets/tests/socket_export_stream-4.phpt +++ b/ext/sockets/tests/socket_export_stream-4.phpt @@ -16,7 +16,7 @@ function test($stream, $sock) { if ($stream !== null) { echo "stream_set_blocking "; try { - print_r(stream_set_blocking($stream, 0)); + print_r(stream_set_blocking($stream, false)); } catch (Error $e) { echo get_class($e), ": ", $e->getMessage(), "\n"; } diff --git a/ext/sockets/tests/socket_import_stream-1.phpt b/ext/sockets/tests/socket_import_stream-1.phpt index 80d3069fc01e7..8d5ec01cd7d01 100644 --- a/ext/sockets/tests/socket_import_stream-1.phpt +++ b/ext/sockets/tests/socket_import_stream-1.phpt @@ -5,7 +5,7 @@ sockets --FILE-- diff --git a/ext/sockets/tests/socket_import_stream-4.phpt b/ext/sockets/tests/socket_import_stream-4.phpt index 25e425961f613..efe987dfdce28 100644 --- a/ext/sockets/tests/socket_import_stream-4.phpt +++ b/ext/sockets/tests/socket_import_stream-4.phpt @@ -4,8 +4,7 @@ socket_import_stream: effects of closing sockets --SKIPIF-- @@ -16,7 +15,7 @@ function test($stream, $sock) { if ($stream !== null) { echo "stream_set_blocking "; try { - print_r(stream_set_blocking($stream, 0)); + print_r(stream_set_blocking($stream, false)); } catch (Error $e) { echo get_class($e), ": ", $e->getMessage(), "\n"; } From 4baecc1d4a2561a96df9f6f57e82d97b32a154dd Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Mon, 23 Jun 2025 14:50:49 +0200 Subject: [PATCH 143/682] ext/simplexml: Remove bool type coercions in tests --- ext/simplexml/tests/profile13.phpt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/simplexml/tests/profile13.phpt b/ext/simplexml/tests/profile13.phpt index 8c441654c8bee..be2b38f30bba4 100644 --- a/ext/simplexml/tests/profile13.phpt +++ b/ext/simplexml/tests/profile13.phpt @@ -21,9 +21,9 @@ xmlns:xsd="http://www.w3.org/2001/XMLSchema" EOF; $sxe = simplexml_load_string($xml); -var_dump($sxe->children('soap', 1)); +var_dump($sxe->children('soap', true)); -$sxe = simplexml_load_string($xml, NULL, 0, 'soap', 1); +$sxe = simplexml_load_string($xml, NULL, 0, 'soap', true); var_dump($sxe->Body); var_dump($sxe->Body->children('')); var_dump($sxe->Body->children('')->businessList); From 7f80d4dc7ddc012a7d4f4ea2ff75c279592408a2 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Mon, 23 Jun 2025 14:56:03 +0200 Subject: [PATCH 144/682] ext/session: Remove bool type coercions in tests --- ext/session/tests/010.phpt | 2 +- ext/session/tests/session_encode_variation8.phpt | 4 ++-- .../user_session_module/session_set_save_handler_basic.phpt | 2 +- .../session_set_save_handler_closures.phpt | 2 +- .../session_set_save_handler_variation1.phpt | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ext/session/tests/010.phpt b/ext/session/tests/010.phpt index 367edcfa61fd4..1a48680106d80 100644 --- a/ext/session/tests/010.phpt +++ b/ext/session/tests/010.phpt @@ -11,7 +11,7 @@ session.cache_limiter= --EXPECT-- diff --git a/ext/session/tests/session_encode_variation8.phpt b/ext/session/tests/session_encode_variation8.phpt index 3571e8c821691..326b03711580d 100644 --- a/ext/session/tests/session_encode_variation8.phpt +++ b/ext/session/tests/session_encode_variation8.phpt @@ -16,7 +16,7 @@ echo "*** Testing session_encode() : variation ***\n"; var_dump(session_start()); $_SESSION["foo"] = 1234567890; $encoded = session_encode(); -var_dump(base64_encode($encoded)); +var_dump($encoded); var_dump(session_destroy()); echo "Done"; @@ -29,7 +29,7 @@ Warning: session_start(): Cannot find session serialization handler "blah" - ses bool(false) Warning: session_encode(): Cannot encode non-existent session in %s on line %d -string(0) "" +bool(false) Warning: session_destroy(): Trying to destroy uninitialized session in %s on line %d bool(false) diff --git a/ext/session/tests/user_session_module/session_set_save_handler_basic.phpt b/ext/session/tests/user_session_module/session_set_save_handler_basic.phpt index b3932b52587a1..b864d50ab7e9f 100644 --- a/ext/session/tests/user_session_module/session_set_save_handler_basic.phpt +++ b/ext/session/tests/user_session_module/session_set_save_handler_basic.phpt @@ -16,7 +16,7 @@ echo "*** Testing session_set_save_handler() : basic functionality ***\n"; require_once "save_handler.inc"; var_dump(session_module_name()); -var_dump(session_module_name(FALSE)); +var_dump(session_module_name('')); var_dump(session_module_name("blah")); var_dump(session_module_name("foo")); diff --git a/ext/session/tests/user_session_module/session_set_save_handler_closures.phpt b/ext/session/tests/user_session_module/session_set_save_handler_closures.phpt index e84bc6fbef6d4..08f533e6cee38 100644 --- a/ext/session/tests/user_session_module/session_set_save_handler_closures.phpt +++ b/ext/session/tests/user_session_module/session_set_save_handler_closures.phpt @@ -15,7 +15,7 @@ echo "*** Testing session_set_save_handler() : using closures as callbacks ***\n require_once "save_handler_closures.inc"; var_dump(session_module_name()); -var_dump(session_module_name(FALSE)); +var_dump(session_module_name('')); var_dump(session_module_name("blah")); var_dump(session_module_name("foo")); diff --git a/ext/session/tests/user_session_module/session_set_save_handler_variation1.phpt b/ext/session/tests/user_session_module/session_set_save_handler_variation1.phpt index 417103b3d1b1c..87996a3314534 100644 --- a/ext/session/tests/user_session_module/session_set_save_handler_variation1.phpt +++ b/ext/session/tests/user_session_module/session_set_save_handler_variation1.phpt @@ -10,7 +10,7 @@ ob_start(); echo "*** Testing session_set_save_handler() : variation ***\n"; var_dump(session_module_name()); -var_dump(session_module_name(FALSE)); +var_dump(session_module_name('')); var_dump(session_module_name()); var_dump(session_module_name("blah")); var_dump(session_module_name()); From c7f0ac1bf942df3b129b558773cace58a72f2b16 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Mon, 23 Jun 2025 13:59:05 +0100 Subject: [PATCH 145/682] ext/random: Remove useless tests (#18920) --- .../tests/01_functions/mt_srand_basic.phpt | 24 ----------------- .../tests/01_functions/srand_basic.phpt | 27 ------------------- 2 files changed, 51 deletions(-) delete mode 100644 ext/random/tests/01_functions/mt_srand_basic.phpt delete mode 100644 ext/random/tests/01_functions/srand_basic.phpt diff --git a/ext/random/tests/01_functions/mt_srand_basic.phpt b/ext/random/tests/01_functions/mt_srand_basic.phpt deleted file mode 100644 index aee012d713fa4..0000000000000 --- a/ext/random/tests/01_functions/mt_srand_basic.phpt +++ /dev/null @@ -1,24 +0,0 @@ ---TEST-- -Test mt_srand() - basic function (return values) mt_srand() ---FILE-- - ---EXPECTF-- -NULL -NULL - -Deprecated: Implicit conversion from float 500.1 to int loses precision in %s on line %d -NULL -NULL -NULL -NULL -NULL diff --git a/ext/random/tests/01_functions/srand_basic.phpt b/ext/random/tests/01_functions/srand_basic.phpt deleted file mode 100644 index 99f43bc502737..0000000000000 --- a/ext/random/tests/01_functions/srand_basic.phpt +++ /dev/null @@ -1,27 +0,0 @@ ---TEST-- -Test srand() - basic function test for srand() ---FILE-- - ---EXPECTF-- -*** Testing srand() : basic functionality *** -NULL -NULL - -Deprecated: Implicit conversion from float 500.1 to int loses precision in %s on line %d -NULL -NULL -NULL -NULL -NULL From fe504d33571f7c21a3529594693460a863dbb5ed Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Mon, 23 Jun 2025 00:05:03 +0200 Subject: [PATCH 146/682] Fix leak when creating cycle in hook This is necessary because the VM frees operands with the nogc variants. We cannot just call gc_possible_root() because the object may no longer exist at that point. Fixes GH-18907 Closes GH-18917 --- NEWS | 1 + Zend/tests/gh18907.phpt | 26 ++++++++++++++++++++++++++ Zend/zend_object_handlers.c | 2 ++ 3 files changed, 29 insertions(+) create mode 100644 Zend/tests/gh18907.phpt diff --git a/NEWS b/NEWS index 94eb74cae5179..80b805983e863 100644 --- a/NEWS +++ b/NEWS @@ -8,6 +8,7 @@ PHP NEWS - Core: . Fixed bug GH-18833 (Use after free with weakmaps dependent on destruction order). (Daniil Gentili) + . Fixed bug GH-18907 (Leak when creating cycle in hook). (ilutov) - Curl: . Fix memory leaks when returning refcounted value from curl callback. diff --git a/Zend/tests/gh18907.phpt b/Zend/tests/gh18907.phpt new file mode 100644 index 0000000000000..1be881fd4941b --- /dev/null +++ b/Zend/tests/gh18907.phpt @@ -0,0 +1,26 @@ +--TEST-- +GH-18907: Leak when creating cycle inside hook +--FILE-- +prop = $this; + return 1; + } + } +} + +function test() { + var_dump((new Foo)->prop); +} + +/* Call twice to test the ZEND_IS_PROPERTY_HOOK_SIMPLE_GET() path. */ +test(); +test(); + +?> +--EXPECT-- +int(1) +int(1) diff --git a/Zend/zend_object_handlers.c b/Zend/zend_object_handlers.c index 0def95fc85227..2ddaeae96e999 100644 --- a/Zend/zend_object_handlers.c +++ b/Zend/zend_object_handlers.c @@ -719,7 +719,9 @@ static bool zend_call_get_hook( return false; } + GC_ADDREF(zobj); zend_call_known_instance_method_with_0_params(get, zobj, rv); + OBJ_RELEASE(zobj); return true; } From b50898894d885eb4a95e6ff88f90ab56f9c8c03c Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sun, 22 Jun 2025 16:03:45 +0200 Subject: [PATCH 147/682] Unbreak PRINTF_DEBUG macro usages Clearly nobody has used this in a while given the compile errors and warnings. This patch fixes them so there are no errors nor warnings anymore. Closes GH-18910. --- ext/standard/formatted_print.c | 46 ++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/ext/standard/formatted_print.c b/ext/standard/formatted_print.c index b988422df21ca..00445d3cca7c7 100644 --- a/ext/standard/formatted_print.c +++ b/ext/standard/formatted_print.c @@ -52,10 +52,10 @@ inline static void php_sprintf_appendchar(zend_string **buffer, size_t *pos, char add) { if ((*pos + 1) >= ZSTR_LEN(*buffer)) { - PRINTF_DEBUG(("%s(): ereallocing buffer to %d bytes\n", get_active_function_name(), ZSTR_LEN(*buffer))); + PRINTF_DEBUG(("%s(): ereallocing buffer to %zu bytes\n", get_active_function_name(), ZSTR_LEN(*buffer))); *buffer = zend_string_extend(*buffer, ZSTR_LEN(*buffer) << 1, 0); } - PRINTF_DEBUG(("sprintf: appending '%c', pos=\n", add, *pos)); + PRINTF_DEBUG(("sprintf: appending '%c', pos=%zu\n", add, *pos)); ZSTR_VAL(*buffer)[(*pos)++] = add; } /* }}} */ @@ -67,13 +67,13 @@ php_sprintf_appendchars(zend_string **buffer, size_t *pos, char *add, size_t len if ((*pos + len) >= ZSTR_LEN(*buffer)) { size_t nlen = ZSTR_LEN(*buffer); - PRINTF_DEBUG(("%s(): ereallocing buffer to %d bytes\n", get_active_function_name(), ZSTR_LEN(*buffer))); + PRINTF_DEBUG(("%s(): ereallocing buffer to %zu bytes\n", get_active_function_name(), ZSTR_LEN(*buffer))); do { nlen = nlen << 1; } while ((*pos + len) >= nlen); *buffer = zend_string_extend(*buffer, nlen, 0); } - PRINTF_DEBUG(("sprintf: appending \"%s\", pos=\n", add, *pos)); + PRINTF_DEBUG(("sprintf: appending \"%s\", pos=%zu\n", add, *pos)); memcpy(ZSTR_VAL(*buffer) + (*pos), add, len); *pos += len; } @@ -93,7 +93,7 @@ php_sprintf_appendstring(zend_string **buffer, size_t *pos, char *add, copy_len = (expprec ? MIN(max_width, len) : len); npad = (min_width < copy_len) ? 0 : min_width - copy_len; - PRINTF_DEBUG(("sprintf: appendstring(%x, %d, %d, \"%s\", %d, '%c', %d)\n", + PRINTF_DEBUG(("sprintf: appendstring(%p, %zu, %zu, \"%s\", %zu, '%c', %zu)\n", *buffer, *pos, ZSTR_LEN(*buffer), add, min_width, padding, alignment)); m_width = MAX(min_width, copy_len); @@ -111,7 +111,7 @@ php_sprintf_appendstring(zend_string **buffer, size_t *pos, char *add, } size <<= 1; } - PRINTF_DEBUG(("sprintf ereallocing buffer to %d bytes\n", size)); + PRINTF_DEBUG(("sprintf ereallocing buffer to %zu bytes\n", size)); *buffer = zend_string_extend(*buffer, size, 0); } if (alignment == ALIGN_RIGHT) { @@ -146,8 +146,8 @@ php_sprintf_appendint(zend_string **buffer, size_t *pos, zend_long number, zend_ulong magn, nmagn; unsigned int i = NUM_BUF_SIZE - 1, neg = 0; - PRINTF_DEBUG(("sprintf: appendint(%x, %x, %x, %d, %d, '%c', %d)\n", - *buffer, pos, &ZSTR_LEN(*buffer), number, width, padding, alignment)); + PRINTF_DEBUG(("sprintf: appendint(%p, %zu, %zu, " ZEND_LONG_FMT ", %zu, '%c', %zu)\n", + *buffer, *pos, ZSTR_LEN(*buffer), number, width, padding, alignment)); if (number < 0) { neg = 1; magn = ((zend_ulong) -(number + 1)) + 1; @@ -172,7 +172,7 @@ php_sprintf_appendint(zend_string **buffer, size_t *pos, zend_long number, } else if (always_sign) { numbuf[--i] = '+'; } - PRINTF_DEBUG(("sprintf: appending %d as \"%s\", i=%d\n", + PRINTF_DEBUG(("sprintf: appending " ZEND_LONG_FMT " as \"%s\", i=%u\n", number, &numbuf[i], i)); php_sprintf_appendstring(buffer, pos, &numbuf[i], width, 0, padding, alignment, (NUM_BUF_SIZE - 1) - i, @@ -190,8 +190,8 @@ php_sprintf_appenduint(zend_string **buffer, size_t *pos, zend_ulong magn, nmagn; unsigned int i = NUM_BUF_SIZE - 1; - PRINTF_DEBUG(("sprintf: appenduint(%x, %x, %x, %d, %d, '%c', %d)\n", - *buffer, pos, &ZSTR_LEN(*buffer), number, width, padding, alignment)); + PRINTF_DEBUG(("sprintf: appenduint(%p, %zu, %zu, " ZEND_LONG_FMT ", %zu, '%c', %zu)\n", + *buffer, *pos, ZSTR_LEN(*buffer), number, width, padding, alignment)); magn = (zend_ulong) number; /* Can't right-pad 0's on integers */ @@ -206,7 +206,7 @@ php_sprintf_appenduint(zend_string **buffer, size_t *pos, magn = nmagn; } while (magn > 0 && i > 0); - PRINTF_DEBUG(("sprintf: appending %d as \"%s\", i=%d\n", number, &numbuf[i], i)); + PRINTF_DEBUG(("sprintf: appending " ZEND_LONG_FMT " as \"%s\", i=%d\n", number, &numbuf[i], i)); php_sprintf_appendstring(buffer, pos, &numbuf[i], width, 0, padding, alignment, (NUM_BUF_SIZE - 1) - i, /* neg */ false, 0, 0); } @@ -232,8 +232,8 @@ php_sprintf_appenddouble(zend_string **buffer, size_t *pos, struct lconv *lconv; #endif - PRINTF_DEBUG(("sprintf: appenddouble(%x, %x, %x, %f, %d, '%c', %d, %c)\n", - *buffer, pos, &ZSTR_LEN(*buffer), number, width, padding, alignment, fmt)); + PRINTF_DEBUG(("sprintf: appenddouble(%p, %zu, %zu, %f, %zu, '%c', %zu, %c)\n", + *buffer, *pos, ZSTR_LEN(*buffer), number, width, padding, alignment, fmt)); if ((adjust & ADJ_PRECISION) == 0) { precision = FLOAT_PRECISION; } else if (precision > MAX_FLOAT_PRECISION) { @@ -330,8 +330,8 @@ php_sprintf_append2n(zend_string **buffer, size_t *pos, zend_long number, zend_ulong i = NUM_BUF_SIZE - 1; int andbits = (1 << n) - 1; - PRINTF_DEBUG(("sprintf: append2n(%x, %x, %x, %d, %d, '%c', %d, %d, %x)\n", - *buffer, pos, &ZSTR_LEN(*buffer), number, width, padding, alignment, n, + PRINTF_DEBUG(("sprintf: append2n(%p, %zu, %zu, " ZEND_LONG_FMT ", %zu, '%c', %zu, %d, %p)\n", + *buffer, *pos, ZSTR_LEN(*buffer), number, width, padding, alignment, n, chartable)); PRINTF_DEBUG(("sprintf: append2n 2^%d andbits=%x\n", n, andbits)); @@ -363,7 +363,7 @@ php_sprintf_getnumber(char **buffer, size_t *len) *len -= i; *buffer = endptr; } - PRINTF_DEBUG(("sprintf_getnumber: number was %d bytes long\n", i)); + PRINTF_DEBUG(("sprintf_getnumber: number was %zu bytes long\n", i)); if (num >= INT_MAX || num < 0) { return -1; @@ -431,6 +431,10 @@ php_formatted_print(char *format, size_t format_len, zval *args, int argc, int n int always_sign; int max_missing_argnum = -1; + /* For debugging */ + const char *format_orig = format; + ZEND_IGNORE_VALUE(format_orig); + result = zend_string_alloc(size, 0); currarg = 0; @@ -464,8 +468,8 @@ php_formatted_print(char *format, size_t format_len, zval *args, int argc, int n always_sign = 0; expprec = 0; - PRINTF_DEBUG(("sprintf: first looking at '%c', inpos=%d\n", - *format, format - Z_STRVAL_P(z_format))); + PRINTF_DEBUG(("sprintf: first looking at '%c', inpos=%zu\n", + *format, format - format_orig)); if (isalpha((int)*format)) { width = precision = 0; argnum = ARG_NUM_NEXT; @@ -478,8 +482,8 @@ php_formatted_print(char *format, size_t format_len, zval *args, int argc, int n /* after argnum comes modifiers */ PRINTF_DEBUG(("sprintf: looking for modifiers\n" - "sprintf: now looking at '%c', inpos=%d\n", - *format, format - Z_STRVAL_P(z_format))); + "sprintf: now looking at '%c', inpos=%zu\n", + *format, format - format_orig)); for (;; format++, format_len--) { if (*format == ' ' || *format == '0') { padding = *format; From 799ec7b8c50440f851d823d5c4b68f430234149b Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sun, 22 Jun 2025 16:29:19 +0200 Subject: [PATCH 148/682] Fix misleading errors in printf() The precision and width _can_ be zero. Closes GH-18911. --- NEWS | 3 +++ ext/standard/formatted_print.c | 6 +++--- ext/standard/tests/strings/sprintf_star.phpt | 16 +++++++++++++++- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/NEWS b/NEWS index fd344ee94c72e..1010209696243 100644 --- a/NEWS +++ b/NEWS @@ -20,6 +20,9 @@ PHP NEWS - MbString: . Fixed bug GH-18901 (integer overflow mb_split). (nielsdos) +- Standard: + . Fix misleading errors in printf(). (nielsdos) + - Streams: . Fixed GH-13264 (fgets() and stream_get_line() do not return false on filter fatal error). (Jakub Zelenka) diff --git a/ext/standard/formatted_print.c b/ext/standard/formatted_print.c index 00445d3cca7c7..1ff0f36212bbf 100644 --- a/ext/standard/formatted_print.c +++ b/ext/standard/formatted_print.c @@ -534,7 +534,7 @@ php_formatted_print(char *format, size_t format_len, zval *args, int argc, int n goto fail; } if (Z_LVAL_P(tmp) < 0 || Z_LVAL_P(tmp) > INT_MAX) { - zend_value_error("Width must be greater than zero and less than %d", INT_MAX); + zend_value_error("Width must be between 0 and %d", INT_MAX); goto fail; } width = Z_LVAL_P(tmp); @@ -542,7 +542,7 @@ php_formatted_print(char *format, size_t format_len, zval *args, int argc, int n } else if (isdigit((int)*format)) { PRINTF_DEBUG(("sprintf: getting width\n")); if ((width = php_sprintf_getnumber(&format, &format_len)) < 0) { - zend_value_error("Width must be greater than zero and less than %d", INT_MAX); + zend_value_error("Width must be between 0 and %d", INT_MAX); goto fail; } adjusting |= ADJ_WIDTH; @@ -586,7 +586,7 @@ php_formatted_print(char *format, size_t format_len, zval *args, int argc, int n expprec = 1; } else if (isdigit((int)*format)) { if ((precision = php_sprintf_getnumber(&format, &format_len)) < 0) { - zend_value_error("Precision must be greater than zero and less than %d", INT_MAX); + zend_value_error("Precision must be between 0 and %d", INT_MAX); goto fail; } adjusting |= ADJ_PRECISION; diff --git a/ext/standard/tests/strings/sprintf_star.phpt b/ext/standard/tests/strings/sprintf_star.phpt index 0e3e16c326420..0c8a211e5c437 100644 --- a/ext/standard/tests/strings/sprintf_star.phpt +++ b/ext/standard/tests/strings/sprintf_star.phpt @@ -62,6 +62,18 @@ try { echo $e->getMessage(), "\n"; } +try { + printf("%9999999999999999999999.f\n", $f); +} catch (ValueError $e) { + echo $e->getMessage(), "\n"; +} + +try { + printf("%.9999999999999999999999f\n", $f); +} catch (ValueError $e) { + echo $e->getMessage(), "\n"; +} + ?> --EXPECT-- float(1.2345678901234567) @@ -95,4 +107,6 @@ foo Precision must be an integer Precision must be between -1 and 2147483647 Precision -1 is only supported for %g, %G, %h and %H -Width must be greater than zero and less than 2147483647 +Width must be between 0 and 2147483647 +Width must be between 0 and 2147483647 +Precision must be between 0 and 2147483647 From 8e731ca622bfc2cf26375c950fdd58ccae2f999f Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sun, 22 Jun 2025 20:18:55 +0200 Subject: [PATCH 149/682] Fix GH-18639: Internal class aliases can break preloading + JIT ZEND_FUNC_INFO() can not be used on internal CE's. If preloading makes a CE that's an alias of an internal class, the invalid access happens when setting the FUNC_INFO. While we could check the class type to be of user code, we can just skip aliases altogether anyway which may be faster. Closes GH-18915. --- NEWS | 4 ++++ ext/opcache/jit/zend_jit.c | 10 +++++++++- ext/opcache/tests/gh18639.phpt | 20 ++++++++++++++++++++ 3 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 ext/opcache/tests/gh18639.phpt diff --git a/NEWS b/NEWS index 1010209696243..b453445854f95 100644 --- a/NEWS +++ b/NEWS @@ -20,6 +20,10 @@ PHP NEWS - MbString: . Fixed bug GH-18901 (integer overflow mb_split). (nielsdos) +- Opcache: + . Fixed bug GH-18639 (Internal class aliases can break preloading + JIT). + (nielsdos) + - Standard: . Fix misleading errors in printf(). (nielsdos) diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c index 9adfe1719f626..7caa3387016e7 100644 --- a/ext/opcache/jit/zend_jit.c +++ b/ext/opcache/jit/zend_jit.c @@ -4628,8 +4628,16 @@ ZEND_EXT_API int zend_jit_script(zend_script *script) || JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) { zend_class_entry *ce; zend_op_array *op_array; + zval *zv; + + ZEND_HASH_MAP_FOREACH_VAL(&script->class_table, zv) { + if (Z_TYPE_P(zv) == IS_ALIAS_PTR) { + continue; + } + + ce = Z_PTR_P(zv); + ZEND_ASSERT(ce->type == ZEND_USER_CLASS); - ZEND_HASH_MAP_FOREACH_PTR(&script->class_table, ce) { ZEND_HASH_MAP_FOREACH_PTR(&ce->function_table, op_array) { if (!ZEND_FUNC_INFO(op_array)) { void *jit_extension = zend_shared_alloc_get_xlat_entry(op_array->opcodes); diff --git a/ext/opcache/tests/gh18639.phpt b/ext/opcache/tests/gh18639.phpt new file mode 100644 index 0000000000000..28424032931ab --- /dev/null +++ b/ext/opcache/tests/gh18639.phpt @@ -0,0 +1,20 @@ +--TEST-- +GH-18639 (Internal class aliases can break preloading + JIT) +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.jit=1255 +opcache.jit_buffer_size=16M +opcache.preload={PWD}/preload_gh18567.inc +--EXTENSIONS-- +opcache +--SKIPIF-- + +--FILE-- + +--EXPECT-- +ok From 56c4ddfaf62ff3935029847bb6fb44768f4b9452 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sun, 22 Jun 2025 09:43:08 +0200 Subject: [PATCH 150/682] Fix GH-18899: JIT function crash when emitting undefined variable warning and opline is not set yet The crash happens because EX(opline) is attempted to be accessed but it's not set yet. Closes GH-18904. --- NEWS | 2 ++ ext/opcache/jit/zend_jit_ir.c | 2 ++ ext/opcache/tests/jit/gh18899.phpt | 21 +++++++++++++++++++++ 3 files changed, 25 insertions(+) create mode 100644 ext/opcache/tests/jit/gh18899.phpt diff --git a/NEWS b/NEWS index 2e969e0830f41..f71edaedb61c0 100644 --- a/NEWS +++ b/NEWS @@ -24,6 +24,8 @@ PHP NEWS - Opcache: . Fixed bug GH-18639 (Internal class aliases can break preloading + JIT). (nielsdos) + . Fixed bug GH-18899 (JIT function crash when emitting undefined variable + warning and opline is not set yet). (nielsdos) - Standard: . Fix misleading errors in printf(). (nielsdos) diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index 74fad38ffee87..6afd768321cb3 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -5981,6 +5981,7 @@ static int zend_jit_long_math_helper(zend_jit_ctx *jit, ir_IF_FALSE_cold(if_def); // zend_error_unchecked(E_WARNING, "Undefined variable $%S", CV_DEF_OF(EX_VAR_TO_NUM(opline->op1.var))); + jit_SET_EX_OPLINE(jit, opline); ir_CALL_1(IR_VOID, ir_CONST_FC_FUNC(zend_jit_undefined_op_helper), ir_CONST_U32(opline->op1.var)); ref2 = jit_EG(uninitialized_zval); @@ -5997,6 +5998,7 @@ static int zend_jit_long_math_helper(zend_jit_ctx *jit, ir_IF_FALSE_cold(if_def); // zend_error_unchecked(E_WARNING, "Undefined variable $%S", CV_DEF_OF(EX_VAR_TO_NUM(opline->op2.var))); + jit_SET_EX_OPLINE(jit, opline); ir_CALL_1(IR_VOID, ir_CONST_FC_FUNC(zend_jit_undefined_op_helper), ir_CONST_U32(opline->op2.var)); ref2 = jit_EG(uninitialized_zval); diff --git a/ext/opcache/tests/jit/gh18899.phpt b/ext/opcache/tests/jit/gh18899.phpt new file mode 100644 index 0000000000000..47c9a3e1ae379 --- /dev/null +++ b/ext/opcache/tests/jit/gh18899.phpt @@ -0,0 +1,21 @@ +--TEST-- +GH-18899 (JIT function crash when emitting undefined variable warning and opline is not set yet) +--EXTENSIONS-- +opcache +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.jit=1205 +opcache.jit_buffer_size=8M +--FILE-- +>= 8; + } +} +str_repeat("A",232).ptr2str(); +?> +--EXPECTF-- +Warning: Undefined variable $ptr in %s on line %d From 591b3249da3f7dbad91685b1dc78efa733d28b85 Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Mon, 23 Jun 2025 21:44:58 +0200 Subject: [PATCH 151/682] Do not use RTLD_DEEPBIND if dlmopen is available (#18612) DL_LOAD now doesn't use RTLD_DEEPBIND deepbind anymore on platforms where dlmopen with LM_ID_NEWLM is available: this means shared library symbol isolation (if needed) must be enabled on the user side when requiring libphp.so, by using dlmopen with LM_ID_NEWLM instead of dlopen. RTLD_DEEPBIND is still enabled when the Apache SAPI is in use. Closes GH-10670. --- NEWS | 1 + UPGRADING.INTERNALS | 6 ++++++ Zend/zend_API.c | 7 +++++++ Zend/zend_API.h | 2 ++ Zend/zend_portability.h | 7 ++++++- sapi/apache2handler/sapi_apache2.c | 1 + 6 files changed, 23 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 1ce8ce754adc9..65fb408ddaacf 100644 --- a/NEWS +++ b/NEWS @@ -58,6 +58,7 @@ PHP NEWS . Added the pipe (|>) operator. (crell) . Added support for `final` with constructor property promotion. (DanielEScherzer) + . Do not use RTLD_DEEPBIND if dlmopen is available. (Daniil Gentili) - Curl: . Added curl_multi_get_handles(). (timwolla) diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS index 432754528f09a..47482d40d0b8d 100644 --- a/UPGRADING.INTERNALS +++ b/UPGRADING.INTERNALS @@ -17,6 +17,12 @@ PHP 8.5 INTERNALS UPGRADE NOTES - Core . PG(arg_separator).input and PG(arg_separator).output are now `zend_string*` instead of `char*`. + . DL_LOAD now doesn't use RTLD_DEEPBIND deepbind anymore on platforms + where dlmopen with LM_ID_NEWLM is available: + this means shared library symbol isolation (if needed) must be enabled on + the user side when requiring libphp.so, by using dlmopen with LM_ID_NEWLM + instead of dlopen. + RTLD_DEEPBIND is still enabled when the Apache SAPI is in use. - Zend . Added zend_safe_assign_to_variable_noref() function to safely assign diff --git a/Zend/zend_API.c b/Zend/zend_API.c index e0006e7d7275f..d29fe8ae8e3c3 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -40,6 +40,8 @@ /* these variables are true statics/globals, and have to be mutex'ed on every access */ ZEND_API HashTable module_registry; +ZEND_API bool zend_dl_use_deepbind = false; + static zend_module_entry **module_request_startup_handlers; static zend_module_entry **module_request_shutdown_handlers; static zend_module_entry **module_post_deactivate_handlers; @@ -47,6 +49,11 @@ static zend_module_entry **modules_dl_loaded; static zend_class_entry **class_cleanup_handlers; +ZEND_API void zend_set_dl_use_deepbind(bool use_deepbind) +{ + zend_dl_use_deepbind = use_deepbind; +} + ZEND_API zend_result zend_get_parameters_array_ex(uint32_t param_count, zval *argument_array) /* {{{ */ { zval *param_ptr; diff --git a/Zend/zend_API.h b/Zend/zend_API.h index a644de8e15134..02ec1b18a6b69 100644 --- a/Zend/zend_API.h +++ b/Zend/zend_API.h @@ -343,6 +343,8 @@ typedef struct _zend_fcall_info_cache { ZEND_API int zend_next_free_module(void); BEGIN_EXTERN_C() +ZEND_API void zend_set_dl_use_deepbind(bool use_deepbind); + ZEND_API zend_result zend_get_parameters_array_ex(uint32_t param_count, zval *argument_array); /* internal function to efficiently copy parameters when executing __call() */ diff --git a/Zend/zend_portability.h b/Zend/zend_portability.h index 65ce533c753bd..97bd038ecf3d8 100644 --- a/Zend/zend_portability.h +++ b/Zend/zend_portability.h @@ -165,7 +165,12 @@ # if defined(RTLD_GROUP) && defined(RTLD_WORLD) && defined(RTLD_PARENT) # define DL_LOAD(libname) dlopen(libname, PHP_RTLD_MODE | RTLD_GLOBAL | RTLD_GROUP | RTLD_WORLD | RTLD_PARENT) # elif defined(RTLD_DEEPBIND) && !defined(__SANITIZE_ADDRESS__) && !__has_feature(memory_sanitizer) -# define DL_LOAD(libname) dlopen(libname, PHP_RTLD_MODE | RTLD_GLOBAL | RTLD_DEEPBIND) +# if defined(LM_ID_NEWLM) + ZEND_API extern bool zend_dl_use_deepbind; +# define DL_LOAD(libname) dlopen(libname, PHP_RTLD_MODE | RTLD_GLOBAL | (zend_dl_use_deepbind ? RTLD_DEEPBIND : 0)) +# else +# define DL_LOAD(libname) dlopen(libname, PHP_RTLD_MODE | RTLD_GLOBAL | RTLD_DEEPBIND) +# endif # else # define DL_LOAD(libname) dlopen(libname, PHP_RTLD_MODE | RTLD_GLOBAL) # endif diff --git a/sapi/apache2handler/sapi_apache2.c b/sapi/apache2handler/sapi_apache2.c index 1d85a92ebf4d8..e87223b055e12 100644 --- a/sapi/apache2handler/sapi_apache2.c +++ b/sapi/apache2handler/sapi_apache2.c @@ -466,6 +466,7 @@ php_apache_server_startup(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp { void *data = NULL; const char *userdata_key = "apache2hook_post_config"; + zend_set_dl_use_deepbind(true); /* Apache will load, unload and then reload a DSO module. This * prevents us from starting PHP until the second load. */ From 1e3d92f8a95c17d0fb19c11e17a0cd8c13c18309 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sun, 22 Jun 2025 20:28:02 +0200 Subject: [PATCH 152/682] Fix GH-14082: Segmentation fault on unknown address 0x600000000018 in ext/opcache/jit/zend_jit.c During persisting, the JIT may trigger and fill in the call graph. The call graph info is allocated on the arena which will be gone after preloading. To prevent invalid accesses during normal requests, the arena data should be cleared. This has to be done after all scripts have been persisted because shared op arrays between scripts can change the call graph. Closes GH-18916. --- NEWS | 2 + ext/opcache/ZendAccelerator.c | 47 +++++++++++++++++++++++ ext/opcache/tests/jit/gh14082.phpt | 23 +++++++++++ ext/opcache/tests/jit/preload_gh14082.inc | 9 +++++ 4 files changed, 81 insertions(+) create mode 100644 ext/opcache/tests/jit/gh14082.phpt create mode 100644 ext/opcache/tests/jit/preload_gh14082.inc diff --git a/NEWS b/NEWS index b453445854f95..34866abfb21d8 100644 --- a/NEWS +++ b/NEWS @@ -23,6 +23,8 @@ PHP NEWS - Opcache: . Fixed bug GH-18639 (Internal class aliases can break preloading + JIT). (nielsdos) + . Fixed bug GH-14082 (Segmentation fault on unknown address 0x600000000018 + in ext/opcache/jit/zend_jit.c). (nielsdos) - Standard: . Fix misleading errors in printf(). (nielsdos) diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index bd6b323871bcc..1b0101dbfd6cb 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -52,6 +52,8 @@ #ifdef HAVE_JIT # include "jit/zend_jit.h" +# include "Optimizer/zend_func_info.h" +# include "Optimizer/zend_call_graph.h" #endif #ifndef ZEND_WIN32 @@ -4363,6 +4365,39 @@ static void preload_load(void) } } +#if HAVE_JIT +static void zend_accel_clear_call_graph_ptrs(zend_op_array *op_array) +{ + ZEND_ASSERT(ZEND_USER_CODE(op_array->type)); + zend_func_info *info = ZEND_FUNC_INFO(op_array); + if (info) { + info->caller_info = NULL; + info->callee_info = NULL; + } +} + +static void accel_reset_arena_info(zend_persistent_script *script) +{ + zend_op_array *op_array; + zend_class_entry *ce; + + zend_accel_clear_call_graph_ptrs(&script->script.main_op_array); + ZEND_HASH_MAP_FOREACH_PTR(&script->script.function_table, op_array) { + zend_accel_clear_call_graph_ptrs(op_array); + } ZEND_HASH_FOREACH_END(); + ZEND_HASH_MAP_FOREACH_PTR(&script->script.class_table, ce) { + ZEND_HASH_MAP_FOREACH_PTR(&ce->function_table, op_array) { + if (op_array->scope == ce + && op_array->type == ZEND_USER_FUNCTION + && !(op_array->fn_flags & ZEND_ACC_ABSTRACT) + && !(op_array->fn_flags & ZEND_ACC_TRAIT_CLONE)) { + zend_accel_clear_call_graph_ptrs(op_array); + } + } ZEND_HASH_FOREACH_END(); + } ZEND_HASH_FOREACH_END(); +} +#endif + static zend_result accel_preload(const char *config, bool in_child) { zend_file_handle file_handle; @@ -4568,6 +4603,18 @@ static zend_result accel_preload(const char *config, bool in_child) } ZEND_HASH_FOREACH_END(); ZCSG(saved_scripts)[i] = NULL; +#if HAVE_JIT + /* During persisting, the JIT may trigger and fill in the call graph. + * The call graph info is allocated on the arena which will be gone after preloading. + * To prevent invalid accesses during normal requests, the arena data should be cleared. + * This has to be done after all scripts have been persisted because shared op arrays between + * scripts can change the call graph. */ + accel_reset_arena_info(ZCSG(preload_script)); + for (zend_persistent_script **scripts = ZCSG(saved_scripts); *scripts; scripts++) { + accel_reset_arena_info(*scripts); + } +#endif + zend_shared_alloc_save_state(); accel_interned_strings_save_state(); diff --git a/ext/opcache/tests/jit/gh14082.phpt b/ext/opcache/tests/jit/gh14082.phpt new file mode 100644 index 0000000000000..eba67a096b82c --- /dev/null +++ b/ext/opcache/tests/jit/gh14082.phpt @@ -0,0 +1,23 @@ +--TEST-- +GH-14082 (Segmentation fault on unknown address 0x600000000018 in ext/opcache/jit/zend_jit.c) +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.jit=1235 +opcache.jit_buffer_size=16M +opcache.preload={PWD}/preload_gh14082.inc +--EXTENSIONS-- +opcache +--SKIPIF-- + +--FILE-- + +--EXPECT-- +int(1) +int(1) +ok diff --git a/ext/opcache/tests/jit/preload_gh14082.inc b/ext/opcache/tests/jit/preload_gh14082.inc new file mode 100644 index 0000000000000..f5b11ac621ebe --- /dev/null +++ b/ext/opcache/tests/jit/preload_gh14082.inc @@ -0,0 +1,9 @@ + Date: Thu, 10 Apr 2025 15:15:36 +0200 Subject: [PATCH 153/682] Fix GHSA-3cr5-j632-f35r: Null byte in hostnames This fixes stream_socket_client() and fsockopen(). Specifically it adds a check to parse_ip_address_ex and it also makes sure that the \0 is not ignored in fsockopen() hostname formatting. --- ext/standard/fsock.c | 27 +++++++++++++++++-- .../tests/network/ghsa-3cr5-j632-f35r.phpt | 21 +++++++++++++++ .../tests/streams/ghsa-3cr5-j632-f35r.phpt | 26 ++++++++++++++++++ main/streams/xp_socket.c | 9 ++++--- 4 files changed, 78 insertions(+), 5 deletions(-) create mode 100644 ext/standard/tests/network/ghsa-3cr5-j632-f35r.phpt create mode 100644 ext/standard/tests/streams/ghsa-3cr5-j632-f35r.phpt diff --git a/ext/standard/fsock.c b/ext/standard/fsock.c index 9e1a53c0ec2a0..67c68468f5140 100644 --- a/ext/standard/fsock.c +++ b/ext/standard/fsock.c @@ -23,6 +23,28 @@ #include "php_network.h" #include "file.h" +static size_t php_fsockopen_format_host_port(char **message, const char *prefix, size_t prefix_len, + const char *host, size_t host_len, zend_long port) +{ + char portbuf[32]; + int portlen = snprintf(portbuf, sizeof(portbuf), ":" ZEND_LONG_FMT, port); + size_t total_len = prefix_len + host_len + portlen; + + char *result = emalloc(total_len + 1); + + if (prefix_len > 0) { + memcpy(result, prefix, prefix_len); + } + memcpy(result + prefix_len, host, host_len); + memcpy(result + prefix_len + host_len, portbuf, portlen); + + result[total_len] = '\0'; + + *message = result; + + return total_len; +} + /* {{{ php_fsockopen() */ static void php_fsockopen_stream(INTERNAL_FUNCTION_PARAMETERS, int persistent) @@ -62,11 +84,12 @@ static void php_fsockopen_stream(INTERNAL_FUNCTION_PARAMETERS, int persistent) } if (persistent) { - spprintf(&hashkey, 0, "pfsockopen__%s:" ZEND_LONG_FMT, host, port); + php_fsockopen_format_host_port(&hashkey, "pfsockopen__", strlen("pfsockopen__"), host, + host_len, port); } if (port > 0) { - hostname_len = spprintf(&hostname, 0, "%s:" ZEND_LONG_FMT, host, port); + hostname_len = php_fsockopen_format_host_port(&hostname, "", 0, host, host_len, port); } else { hostname_len = host_len; hostname = host; diff --git a/ext/standard/tests/network/ghsa-3cr5-j632-f35r.phpt b/ext/standard/tests/network/ghsa-3cr5-j632-f35r.phpt new file mode 100644 index 0000000000000..7556c3be94ccd --- /dev/null +++ b/ext/standard/tests/network/ghsa-3cr5-j632-f35r.phpt @@ -0,0 +1,21 @@ +--TEST-- +GHSA-3cr5-j632-f35r: Null byte termination in fsockopen() +--FILE-- + +--EXPECTF-- + +Warning: fsockopen(): Unable to connect to localhost:%d (The hostname must not contain null bytes) in %s +bool(false) diff --git a/ext/standard/tests/streams/ghsa-3cr5-j632-f35r.phpt b/ext/standard/tests/streams/ghsa-3cr5-j632-f35r.phpt new file mode 100644 index 0000000000000..52f9263c99aaa --- /dev/null +++ b/ext/standard/tests/streams/ghsa-3cr5-j632-f35r.phpt @@ -0,0 +1,26 @@ +--TEST-- +GHSA-3cr5-j632-f35r: Null byte termination in stream_socket_client() +--FILE-- + +--EXPECTF-- + +Warning: stream_socket_client(): Unable to connect to tcp://localhost\0.example.com:%d (The hostname must not contain null bytes) in %s +bool(false) diff --git a/main/streams/xp_socket.c b/main/streams/xp_socket.c index b17eccf2eeb18..38f11d149dea2 100644 --- a/main/streams/xp_socket.c +++ b/main/streams/xp_socket.c @@ -581,12 +581,15 @@ static inline char *parse_ip_address_ex(const char *str, size_t str_len, int *po char *colon; char *host = NULL; -#ifdef HAVE_IPV6 - char *p; + if (memchr(str, '\0', str_len)) { + *err = ZSTR_INIT_LITERAL("The hostname must not contain null bytes", 0); + return NULL; + } +#ifdef HAVE_IPV6 if (*(str) == '[' && str_len > 1) { /* IPV6 notation to specify raw address with port (i.e. [fe80::1]:80) */ - p = memchr(str + 1, ']', str_len - 2); + char *p = memchr(str + 1, ']', str_len - 2); if (!p || *(p + 1) != ':') { if (get_err) { *err = strpprintf(0, "Failed to parse IPv6 address \"%s\"", str); From 9376aeef9f8ff81f2705b8016237ec3e30bdee44 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Tue, 4 Mar 2025 17:23:01 +0100 Subject: [PATCH 154/682] Fix GHSA-hrwm-9436-5mv3: pgsql escaping no error checks This adds error checks for escape function is pgsql and pdo_pgsql extensions. It prevents possibility of storing not properly escaped data which could potentially lead to some security issues. --- ext/pdo_pgsql/pgsql_driver.c | 10 +- ext/pdo_pgsql/tests/ghsa-hrwm-9436-5mv3.phpt | 24 ++++ ext/pgsql/pgsql.c | 126 ++++++++++++++++--- ext/pgsql/tests/ghsa-hrwm-9436-5mv3.phpt | 64 ++++++++++ 4 files changed, 203 insertions(+), 21 deletions(-) create mode 100644 ext/pdo_pgsql/tests/ghsa-hrwm-9436-5mv3.phpt create mode 100644 ext/pgsql/tests/ghsa-hrwm-9436-5mv3.phpt diff --git a/ext/pdo_pgsql/pgsql_driver.c b/ext/pdo_pgsql/pgsql_driver.c index f84bfba9f8453..2ea29490856b5 100644 --- a/ext/pdo_pgsql/pgsql_driver.c +++ b/ext/pdo_pgsql/pgsql_driver.c @@ -354,11 +354,15 @@ static zend_string* pgsql_handle_quoter(pdo_dbh_t *dbh, const zend_string *unquo zend_string *quoted_str; pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; size_t tmp_len; + int err; switch (paramtype) { case PDO_PARAM_LOB: /* escapedlen returned by PQescapeBytea() accounts for trailing 0 */ escaped = PQescapeByteaConn(H->server, (unsigned char *)ZSTR_VAL(unquoted), ZSTR_LEN(unquoted), &tmp_len); + if (escaped == NULL) { + return NULL; + } quotedlen = tmp_len + 1; quoted = emalloc(quotedlen + 1); memcpy(quoted+1, escaped, quotedlen-2); @@ -370,7 +374,11 @@ static zend_string* pgsql_handle_quoter(pdo_dbh_t *dbh, const zend_string *unquo default: quoted = safe_emalloc(2, ZSTR_LEN(unquoted), 3); quoted[0] = '\''; - quotedlen = PQescapeStringConn(H->server, quoted + 1, ZSTR_VAL(unquoted), ZSTR_LEN(unquoted), NULL); + quotedlen = PQescapeStringConn(H->server, quoted + 1, ZSTR_VAL(unquoted), ZSTR_LEN(unquoted), &err); + if (err) { + efree(quoted); + return NULL; + } quoted[quotedlen + 1] = '\''; quoted[quotedlen + 2] = '\0'; quotedlen += 2; diff --git a/ext/pdo_pgsql/tests/ghsa-hrwm-9436-5mv3.phpt b/ext/pdo_pgsql/tests/ghsa-hrwm-9436-5mv3.phpt new file mode 100644 index 0000000000000..8566a26753b40 --- /dev/null +++ b/ext/pdo_pgsql/tests/ghsa-hrwm-9436-5mv3.phpt @@ -0,0 +1,24 @@ +--TEST-- +#GHSA-hrwm-9436-5mv3: pdo_pgsql extension does not check for errors during escaping +--EXTENSIONS-- +pdo +pdo_pgsql +--SKIPIF-- + +--FILE-- +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + +$invalid = "ABC\xff\x30';"; +var_dump($db->quote($invalid)); + +?> +--EXPECT-- +bool(false) diff --git a/ext/pgsql/pgsql.c b/ext/pgsql/pgsql.c index 9f04f2c843b9c..01fb9dde3bae6 100644 --- a/ext/pgsql/pgsql.c +++ b/ext/pgsql/pgsql.c @@ -3297,8 +3297,14 @@ PHP_FUNCTION(pg_escape_string) to = zend_string_safe_alloc(ZSTR_LEN(from), 2, 0, 0); if (link) { + int err; pgsql = link->conn; - ZSTR_LEN(to) = PQescapeStringConn(pgsql, ZSTR_VAL(to), ZSTR_VAL(from), ZSTR_LEN(from), NULL); + ZSTR_LEN(to) = PQescapeStringConn(pgsql, ZSTR_VAL(to), ZSTR_VAL(from), ZSTR_LEN(from), &err); + if (err) { + zend_argument_value_error(ZEND_NUM_ARGS(), "Escaping string failed"); + zend_string_efree(to); + RETURN_THROWS(); + } } else { ZSTR_LEN(to) = PQescapeString(ZSTR_VAL(to), ZSTR_VAL(from), ZSTR_LEN(from)); @@ -3341,6 +3347,10 @@ PHP_FUNCTION(pg_escape_bytea) } else { to = (char *)PQescapeBytea((unsigned char *)ZSTR_VAL(from), ZSTR_LEN(from), &to_len); } + if (to == NULL) { + zend_argument_value_error(ZEND_NUM_ARGS(), "Escape failure"); + RETURN_THROWS(); + } RETVAL_STRINGL(to, to_len-1); /* to_len includes additional '\0' */ PQfreemem(to); @@ -4257,7 +4267,7 @@ PHP_PGSQL_API zend_result php_pgsql_meta_data(PGconn *pg_link, const zend_string char *escaped; smart_str querystr = {0}; size_t new_len; - int i, num_rows; + int i, num_rows, err; zval elem; ZEND_ASSERT(ZSTR_LEN(table_name) != 0); @@ -4296,7 +4306,14 @@ PHP_PGSQL_API zend_result php_pgsql_meta_data(PGconn *pg_link, const zend_string "WHERE a.attnum > 0 AND c.relname = '"); } escaped = (char *)safe_emalloc(strlen(tmp_name2), 2, 1); - new_len = PQescapeStringConn(pg_link, escaped, tmp_name2, strlen(tmp_name2), NULL); + new_len = PQescapeStringConn(pg_link, escaped, tmp_name2, strlen(tmp_name2), &err); + if (err) { + php_error_docref(NULL, E_WARNING, "Escaping table name '%s' failed", ZSTR_VAL(table_name)); + efree(src); + efree(escaped); + smart_str_free(&querystr); + return FAILURE; + } if (new_len) { smart_str_appendl(&querystr, escaped, new_len); } @@ -4304,7 +4321,14 @@ PHP_PGSQL_API zend_result php_pgsql_meta_data(PGconn *pg_link, const zend_string smart_str_appends(&querystr, "' AND n.nspname = '"); escaped = (char *)safe_emalloc(strlen(tmp_name), 2, 1); - new_len = PQescapeStringConn(pg_link, escaped, tmp_name, strlen(tmp_name), NULL); + new_len = PQescapeStringConn(pg_link, escaped, tmp_name, strlen(tmp_name), &err); + if (err) { + php_error_docref(NULL, E_WARNING, "Escaping table namespace '%s' failed", ZSTR_VAL(table_name)); + efree(src); + efree(escaped); + smart_str_free(&querystr); + return FAILURE; + } if (new_len) { smart_str_appendl(&querystr, escaped, new_len); } @@ -4565,7 +4589,7 @@ PHP_PGSQL_API zend_result php_pgsql_convert(PGconn *pg_link, const zend_string * { zend_string *field = NULL; zval meta, *def, *type, *not_null, *has_default, *is_enum, *val, new_val; - int err = 0, skip_field; + int err = 0, escape_err = 0, skip_field; php_pgsql_data_type data_type; ZEND_ASSERT(pg_link != NULL); @@ -4818,8 +4842,13 @@ PHP_PGSQL_API zend_result php_pgsql_convert(PGconn *pg_link, const zend_string * /* PostgreSQL ignores \0 */ str = zend_string_alloc(Z_STRLEN_P(val) * 2, 0); /* better to use PGSQLescapeLiteral since PGescapeStringConn does not handle special \ */ - ZSTR_LEN(str) = PQescapeStringConn(pg_link, ZSTR_VAL(str), Z_STRVAL_P(val), Z_STRLEN_P(val), NULL); - ZVAL_STR(&new_val, php_pgsql_add_quotes(str)); + ZSTR_LEN(str) = PQescapeStringConn(pg_link, ZSTR_VAL(str), + Z_STRVAL_P(val), Z_STRLEN_P(val), &escape_err); + if (escape_err) { + err = 1; + } else { + ZVAL_STR(&new_val, php_pgsql_add_quotes(str)); + } zend_string_release_ex(str, false); } break; @@ -4842,7 +4871,15 @@ PHP_PGSQL_API zend_result php_pgsql_convert(PGconn *pg_link, const zend_string * } PGSQL_CONV_CHECK_IGNORE(); if (err) { - php_error_docref(NULL, E_NOTICE, "Expects NULL, string, long or double value for PostgreSQL '%s' (%s)", Z_STRVAL_P(type), ZSTR_VAL(field)); + if (escape_err) { + php_error_docref(NULL, E_NOTICE, + "String value escaping failed for PostgreSQL '%s' (%s)", + Z_STRVAL_P(type), ZSTR_VAL(field)); + } else { + php_error_docref(NULL, E_NOTICE, + "Expects NULL, string, long or double value for PostgreSQL '%s' (%s)", + Z_STRVAL_P(type), ZSTR_VAL(field)); + } } break; @@ -5113,6 +5150,11 @@ PHP_PGSQL_API zend_result php_pgsql_convert(PGconn *pg_link, const zend_string * zend_string *tmp_zstr; tmp = PQescapeByteaConn(pg_link, (unsigned char *)Z_STRVAL_P(val), Z_STRLEN_P(val), &to_len); + if (tmp == NULL) { + php_error_docref(NULL, E_NOTICE, "Escaping value failed for %s field (%s)", Z_STRVAL_P(type), ZSTR_VAL(field)); + err = 1; + break; + } tmp_zstr = zend_string_init((char *)tmp, to_len - 1, false); /* PQescapeBytea's to_len includes additional '\0' */ PQfreemem(tmp); @@ -5191,6 +5233,12 @@ PHP_PGSQL_API zend_result php_pgsql_convert(PGconn *pg_link, const zend_string * zend_hash_update(Z_ARRVAL_P(result), field, &new_val); } else { char *escaped = PQescapeIdentifier(pg_link, ZSTR_VAL(field), ZSTR_LEN(field)); + if (escaped == NULL) { + /* This cannot fail because of invalid string but only due to failed memory allocation */ + php_error_docref(NULL, E_NOTICE, "Escaping field '%s' failed", ZSTR_VAL(field)); + err = 1; + break; + } add_assoc_zval(result, escaped, &new_val); PQfreemem(escaped); } @@ -5269,7 +5317,7 @@ static bool do_exec(smart_str *querystr, ExecStatusType expect, PGconn *pg_link, } /* }}} */ -static inline void build_tablename(smart_str *querystr, PGconn *pg_link, const zend_string *table) /* {{{ */ +static inline zend_result build_tablename(smart_str *querystr, PGconn *pg_link, const zend_string *table) /* {{{ */ { /* schema.table should be "schema"."table" */ const char *dot = memchr(ZSTR_VAL(table), '.', ZSTR_LEN(table)); @@ -5279,6 +5327,10 @@ static inline void build_tablename(smart_str *querystr, PGconn *pg_link, const z smart_str_appendl(querystr, ZSTR_VAL(table), len); } else { char *escaped = PQescapeIdentifier(pg_link, ZSTR_VAL(table), len); + if (escaped == NULL) { + php_error_docref(NULL, E_NOTICE, "Failed to escape table name '%s'", ZSTR_VAL(table)); + return FAILURE; + } smart_str_appends(querystr, escaped); PQfreemem(escaped); } @@ -5291,11 +5343,17 @@ static inline void build_tablename(smart_str *querystr, PGconn *pg_link, const z smart_str_appendl(querystr, after_dot, len); } else { char *escaped = PQescapeIdentifier(pg_link, after_dot, len); + if (escaped == NULL) { + php_error_docref(NULL, E_NOTICE, "Failed to escape table name '%s'", ZSTR_VAL(table)); + return FAILURE; + } smart_str_appendc(querystr, '.'); smart_str_appends(querystr, escaped); PQfreemem(escaped); } } + + return SUCCESS; } /* }}} */ @@ -5316,7 +5374,9 @@ PHP_PGSQL_API zend_result php_pgsql_insert(PGconn *pg_link, const zend_string *t ZVAL_UNDEF(&converted); if (zend_hash_num_elements(Z_ARRVAL_P(var_array)) == 0) { smart_str_appends(&querystr, "INSERT INTO "); - build_tablename(&querystr, pg_link, table); + if (build_tablename(&querystr, pg_link, table) == FAILURE) { + goto cleanup; + } smart_str_appends(&querystr, " DEFAULT VALUES"); goto no_values; @@ -5332,7 +5392,9 @@ PHP_PGSQL_API zend_result php_pgsql_insert(PGconn *pg_link, const zend_string *t } smart_str_appends(&querystr, "INSERT INTO "); - build_tablename(&querystr, pg_link, table); + if (build_tablename(&querystr, pg_link, table) == FAILURE) { + goto cleanup; + } smart_str_appends(&querystr, " ("); ZEND_HASH_FOREACH_STR_KEY(Z_ARRVAL_P(var_array), fld) { @@ -5342,6 +5404,10 @@ PHP_PGSQL_API zend_result php_pgsql_insert(PGconn *pg_link, const zend_string *t } if (opt & PGSQL_DML_ESCAPE) { tmp = PQescapeIdentifier(pg_link, ZSTR_VAL(fld), ZSTR_LEN(fld) + 1); + if (tmp == NULL) { + php_error_docref(NULL, E_NOTICE, "Failed to escape field '%s'", ZSTR_VAL(fld)); + goto cleanup; + } smart_str_appends(&querystr, tmp); PQfreemem(tmp); } else { @@ -5353,15 +5419,19 @@ PHP_PGSQL_API zend_result php_pgsql_insert(PGconn *pg_link, const zend_string *t smart_str_appends(&querystr, ") VALUES ("); /* make values string */ - ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(var_array), val) { + ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(var_array), fld, val) { /* we can avoid the key_type check here, because we tested it in the other loop */ switch (Z_TYPE_P(val)) { case IS_STRING: if (opt & PGSQL_DML_ESCAPE) { - size_t new_len; - char *tmp; - tmp = (char *)safe_emalloc(Z_STRLEN_P(val), 2, 1); - new_len = PQescapeStringConn(pg_link, tmp, Z_STRVAL_P(val), Z_STRLEN_P(val), NULL); + int error; + char *tmp = safe_emalloc(Z_STRLEN_P(val), 2, 1); + size_t new_len = PQescapeStringConn(pg_link, tmp, Z_STRVAL_P(val), Z_STRLEN_P(val), &error); + if (error) { + php_error_docref(NULL, E_NOTICE, "Failed to escape field '%s' value", ZSTR_VAL(fld)); + efree(tmp); + goto cleanup; + } smart_str_appendc(&querystr, '\''); smart_str_appendl(&querystr, tmp, new_len); smart_str_appendc(&querystr, '\''); @@ -5517,6 +5587,10 @@ static inline int build_assignment_string(PGconn *pg_link, smart_str *querystr, } if (opt & PGSQL_DML_ESCAPE) { char *tmp = PQescapeIdentifier(pg_link, ZSTR_VAL(fld), ZSTR_LEN(fld) + 1); + if (tmp == NULL) { + php_error_docref(NULL, E_NOTICE, "Failed to escape field '%s'", ZSTR_VAL(fld)); + return -1; + } smart_str_appends(querystr, tmp); PQfreemem(tmp); } else { @@ -5532,8 +5606,14 @@ static inline int build_assignment_string(PGconn *pg_link, smart_str *querystr, switch (Z_TYPE_P(val)) { case IS_STRING: if (opt & PGSQL_DML_ESCAPE) { + int error; char *tmp = (char *)safe_emalloc(Z_STRLEN_P(val), 2, 1); - size_t new_len = PQescapeStringConn(pg_link, tmp, Z_STRVAL_P(val), Z_STRLEN_P(val), NULL); + size_t new_len = PQescapeStringConn(pg_link, tmp, Z_STRVAL_P(val), Z_STRLEN_P(val), &error); + if (error) { + php_error_docref(NULL, E_NOTICE, "Failed to escape field '%s' value", ZSTR_VAL(fld)); + efree(tmp); + return -1; + } smart_str_appendc(querystr, '\''); smart_str_appendl(querystr, tmp, new_len); smart_str_appendc(querystr, '\''); @@ -5601,7 +5681,9 @@ PHP_PGSQL_API zend_result php_pgsql_update(PGconn *pg_link, const zend_string *t } smart_str_appends(&querystr, "UPDATE "); - build_tablename(&querystr, pg_link, table); + if (build_tablename(&querystr, pg_link, table) == FAILURE) { + goto cleanup; + } smart_str_appends(&querystr, " SET "); if (build_assignment_string(pg_link, &querystr, Z_ARRVAL_P(var_array), 0, ",", 1, opt)) @@ -5704,7 +5786,9 @@ PHP_PGSQL_API zend_result php_pgsql_delete(PGconn *pg_link, const zend_string *t } smart_str_appends(&querystr, "DELETE FROM "); - build_tablename(&querystr, pg_link, table); + if (build_tablename(&querystr, pg_link, table) == FAILURE) { + goto cleanup; + } smart_str_appends(&querystr, " WHERE "); if (build_assignment_string(pg_link, &querystr, Z_ARRVAL_P(ids_array), 1, " AND ", sizeof(" AND ")-1, opt)) @@ -5844,7 +5928,9 @@ PHP_PGSQL_API zend_result php_pgsql_select(PGconn *pg_link, const zend_string *t } smart_str_appends(&querystr, "SELECT * FROM "); - build_tablename(&querystr, pg_link, table); + if (build_tablename(&querystr, pg_link, table) == FAILURE) { + goto cleanup; + } smart_str_appends(&querystr, " WHERE "); if (build_assignment_string(pg_link, &querystr, Z_ARRVAL_P(ids_array), 1, " AND ", sizeof(" AND ")-1, opt)) diff --git a/ext/pgsql/tests/ghsa-hrwm-9436-5mv3.phpt b/ext/pgsql/tests/ghsa-hrwm-9436-5mv3.phpt new file mode 100644 index 0000000000000..c1c5e05dce623 --- /dev/null +++ b/ext/pgsql/tests/ghsa-hrwm-9436-5mv3.phpt @@ -0,0 +1,64 @@ +--TEST-- +#GHSA-hrwm-9436-5mv3: pgsql extension does not check for errors during escaping +--EXTENSIONS-- +pgsql +--SKIPIF-- + +--FILE-- + 'test'])); // table name str escape in php_pgsql_meta_data +var_dump(pg_insert($db, "$invalid.tbl", ['bar' => 'test'])); // schema name str escape in php_pgsql_meta_data +var_dump(pg_insert($db, 'ghsa_hrmw_9436_5mv3', ['bar' => $invalid])); // converted value str escape in php_pgsql_convert +var_dump(pg_insert($db, $invalid, [])); // ident escape in build_tablename +var_dump(pg_insert($db, 'ghsa_hrmw_9436_5mv3', [$invalid => 'foo'], $flags)); // ident escape for field php_pgsql_insert +var_dump(pg_insert($db, 'ghsa_hrmw_9436_5mv3', ['bar' => $invalid], $flags)); // str escape for field value in php_pgsql_insert +var_dump(pg_update($db, 'ghsa_hrmw_9436_5mv3', ['bar' => 'val'], [$invalid => 'test'], $flags)); // ident escape in build_assignment_string +var_dump(pg_update($db, 'ghsa_hrmw_9436_5mv3', ['bar' => 'val'], ['bar' => $invalid], $flags)); // invalid str escape in build_assignment_string +var_dump(pg_escape_literal($db, $invalid)); // pg_escape_literal escape +var_dump(pg_escape_identifier($db, $invalid)); // pg_escape_identifier escape + +?> +--EXPECTF-- + +Warning: pg_insert(): Escaping table name 'ABC%s';' failed in %s on line %d +bool(false) + +Warning: pg_insert(): Escaping table namespace 'ABC%s';.tbl' failed in %s on line %d +bool(false) + +Notice: pg_insert(): String value escaping failed for PostgreSQL 'text' (bar) in %s on line %d +bool(false) + +Notice: pg_insert(): Failed to escape table name 'ABC%s';' in %s on line %d +bool(false) + +Notice: pg_insert(): Failed to escape field 'ABC%s';' in %s on line %d +bool(false) + +Notice: pg_insert(): Failed to escape field 'bar' value in %s on line %d +bool(false) + +Notice: pg_update(): Failed to escape field 'ABC%s';' in %s on line %d +bool(false) + +Notice: pg_update(): Failed to escape field 'bar' value in %s on line %d +bool(false) + +Warning: pg_escape_literal(): Failed to escape in %s on line %d +bool(false) + +Warning: pg_escape_identifier(): Failed to escape in %s on line %d +bool(false) From 29e94f89db59091aaae49efa447f6ba9994b6201 Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Mon, 23 Jun 2025 23:43:52 +0200 Subject: [PATCH 155/682] Autotools: Remove obsole Autoconf macros (#18914) These Autoconf macros have been marked as obsolete in PHP-8.4 and now also removed: - PHP_AP_EXTRACT_VERSION - PHP_BUILD_THREAD_SAFE - PHP_DEF_HAVE - PHP_OUTPUT - PHP_TEST_BUILD --- UPGRADING.INTERNALS | 8 ++++- build/php.m4 | 71 --------------------------------------------- 2 files changed, 7 insertions(+), 72 deletions(-) diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS index 47482d40d0b8d..f421242dba6c6 100644 --- a/UPGRADING.INTERNALS +++ b/UPGRADING.INTERNALS @@ -55,12 +55,18 @@ PHP 8.5 INTERNALS UPGRADE NOTES without duplicate build rules. It is up to the SAPI maintainers to ensure that appropriate build rules are created. -- Linux build system changes +- Unix build system changes . libdir is properly set when --libdir (ex: /usr/lib64) and --with-libdir (ex lib64) configure options are used to ${libdir}/php (ex: /usr/lib64/php) . PHP_ODBC_CFLAGS, PHP_ODBC_LFLAGS, PHP_ODBC_LIBS, PHP_ODBC_TYPE preprocessor macros defined by ext/odbc are now defined in php_config.h instead of the build-defs.h header. + . Autoconf macro PHP_AP_EXTRACT_VERSION has been removed. + . Autoconf macro PHP_BUILD_THREAD_SAFE has been removed (set enable_zts + manually). + . Autoconf macro PHP_DEF_HAVE has been removed (use AC_DEFINE). + . Autoconf macro PHP_OUTPUT has been removed (use AC_CONFIG_FILES). + . Autoconf macro PHP_TEST_BUILD has been removed (use AC_* macros). ======================== 3. Module changes diff --git a/build/php.m4 b/build/php.m4 index aa49766fedd7f..8cdf318083fbb 100644 --- a/build/php.m4 +++ b/build/php.m4 @@ -26,15 +26,6 @@ dnl ---------------------------------------------------------------------------- dnl Build system helper macros. dnl ---------------------------------------------------------------------------- -dnl -dnl PHP_DEF_HAVE(what) -dnl -dnl Generates 'AC_DEFINE(HAVE_WHAT, 1, [ ])'. -dnl -AC_DEFUN([PHP_DEF_HAVE], [m4_warn([obsolete], - [The macro 'PHP_DEF_HAVE' is obsolete. Use AC_DEFINE.]) -AC_DEFINE([HAVE_]translit($1,a-z_.-,A-Z___), 1, [ ])]) - dnl dnl PHP_RUN_ONCE(namespace, variable, code) dnl @@ -89,17 +80,6 @@ AC_DEFUN([PHP_SUBST_OLD],[ PHP_SUBST([$1]) ]) -dnl -dnl PHP_OUTPUT(file) -dnl -dnl Adds "file" to the list of files generated by AC_OUTPUT. This macro can be -dnl used several times. This macro is obsolete as of PHP 8.4 in favor of the -dnl default AC_CONFIG_FILES. -dnl -AC_DEFUN([PHP_OUTPUT], -[m4_warn([obsolete], [The macro 'PHP_OUTPUT' is obsolete. Use AC_CONFIG_FILES.]) -AC_CONFIG_FILES([$1])]) - dnl ---------------------------------------------------------------------------- dnl Build system base macros. dnl ---------------------------------------------------------------------------- @@ -743,13 +723,6 @@ dnl ---------------------------------------------------------------------------- dnl Build macros dnl ---------------------------------------------------------------------------- -dnl -dnl PHP_BUILD_THREAD_SAFE -dnl -AC_DEFUN([PHP_BUILD_THREAD_SAFE], [m4_warn([obsolete], - [The macro 'PHP_BUILD_THREAD_SAFE' is obsolete. Set 'enable_zts' manually.]) - enable_zts=yes]) - dnl dnl PHP_REQUIRE_CXX dnl @@ -1518,31 +1491,6 @@ AC_DEFUN([PHP_CHECK_FUNC],[ esac ]) -dnl -dnl PHP_TEST_BUILD(function, action-if-ok, action-if-not-ok [, extra-libs [, extra-source]]) -dnl -dnl This macro checks whether build works and given function exists. -dnl -AC_DEFUN([PHP_TEST_BUILD], [m4_warn([obsolete], - [The macro 'PHP_TEST_BUILD' is obsolete. Use AC_* macros.]) - old_LIBS=$LIBS - LIBS="$4 $LIBS" - AC_LINK_IFELSE([AC_LANG_SOURCE([ - $5 - char $1(void); - int main(void) { - $1(); - return 0; - } - ])],[ - LIBS=$old_LIBS - $2 - ],[ - LIBS=$old_LIBS - $3 - ]) -]) - dnl ---------------------------------------------------------------------------- dnl Platform characteristics checks. dnl ---------------------------------------------------------------------------- @@ -2039,25 +1987,6 @@ AC_DEFUN([PHP_INSTALL_HEADERS], ]) ]) -dnl -dnl PHP_AP_EXTRACT_VERSION(/path/httpd) -dnl -dnl This macro is used to get a comparable version for Apache. -dnl -AC_DEFUN([PHP_AP_EXTRACT_VERSION], [m4_warn([obsolete], - [The macro 'PHP_AP_EXTRACT_VERSION' is obsolete. Use 'apxs -q HTTPD_VERSION']) -AS_IF([test -x "$1"], [ - ac_output=$($1 -v 2>&1 | grep version | $SED -e 's/Oracle-HTTP-//') - ac_IFS=$IFS -IFS="- /. -" - set $ac_output - IFS=$ac_IFS - - APACHE_VERSION=$(expr [$]4 \* 1000000 + [$]5 \* 1000 + [$]6) -]) -]) - dnl dnl PHP_CONFIG_NICE(filename) dnl From ecc602e3bb5878228cf6b85de790f3f097bbb0fd Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Mon, 23 Jun 2025 23:44:20 +0200 Subject: [PATCH 156/682] Remove non-existing INI directive detect_unicode (#18909) The detect_unicode was removed and zend.detect_unicode was added in PHP 5.4 (bbf3d43c1ee0ad53b03c3821cd630f0746d5e954). --- ext/phar/tests/zip/notphar.phpt | 1 - pear/Makefile.frag | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/ext/phar/tests/zip/notphar.phpt b/ext/phar/tests/zip/notphar.phpt index 10d4d7d8be311..ab4f80e430bba 100644 --- a/ext/phar/tests/zip/notphar.phpt +++ b/ext/phar/tests/zip/notphar.phpt @@ -4,7 +4,6 @@ Phar: a non-executable zip with no stub named .phar.zip phar --INI-- phar.readonly=1 -detect_unicode=0 zend.multibyte=0 --FILE-- /dev/null` FETCH = `which fetch 2>/dev/null` From d6fc7430b95af036386d9910a763708725a1b9bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Tue, 24 Jun 2025 08:50:47 +0200 Subject: [PATCH 157/682] zend_string: Simplify logic in `zend_interned_strings_init()` (#18922) No need to manually init a `zend_string` to then intern it, we can directly intern it while initializing, bypassing some of the safety checks that are redundant in this case. --- Zend/zend_string.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/Zend/zend_string.c b/Zend/zend_string.c index dfe059359aa53..c864a847af39f 100644 --- a/Zend/zend_string.c +++ b/Zend/zend_string.c @@ -86,8 +86,6 @@ static zend_always_inline void zend_init_interned_strings_ht(HashTable *interned ZEND_API void zend_interned_strings_init(void) { char s[2]; - unsigned int i; - zend_string *str; interned_string_request_handler = zend_new_interned_string_request; interned_string_init_request_handler = zend_string_init_interned_request; @@ -103,15 +101,13 @@ ZEND_API void zend_interned_strings_init(void) zend_string_init_existing_interned = zend_string_init_existing_interned_permanent; /* interned empty string */ - str = zend_string_alloc(sizeof("")-1, 1); - ZSTR_VAL(str)[0] = '\000'; - zend_empty_string = zend_new_interned_string_permanent(str); + zend_empty_string = zend_string_init_interned_permanent("", 0, true); GC_ADD_FLAGS(zend_empty_string, IS_STR_VALID_UTF8); s[1] = 0; - for (i = 0; i < 256; i++) { + for (size_t i = 0; i < 256; i++) { s[0] = i; - zend_one_char_string[i] = zend_new_interned_string_permanent(zend_string_init(s, 1, 1)); + zend_one_char_string[i] = zend_string_init_interned_permanent(s, 1, true); if (i < 0x80) { GC_ADD_FLAGS(zend_one_char_string[i], IS_STR_VALID_UTF8); } @@ -119,9 +115,8 @@ ZEND_API void zend_interned_strings_init(void) /* known strings */ zend_known_strings = pemalloc(sizeof(zend_string*) * ((sizeof(known_strings) / sizeof(known_strings[0]) - 1)), 1); - for (i = 0; i < (sizeof(known_strings) / sizeof(known_strings[0])) - 1; i++) { - str = zend_string_init(known_strings[i], strlen(known_strings[i]), 1); - zend_known_strings[i] = zend_new_interned_string_permanent(str); + for (size_t i = 0; i < (sizeof(known_strings) / sizeof(known_strings[0])) - 1; i++) { + zend_known_strings[i] = zend_string_init_interned_permanent(known_strings[i], strlen(known_strings[i]), true); GC_ADD_FLAGS(zend_known_strings[i], IS_STR_VALID_UTF8); } } From c5e7490963a1c53ec12c09264d0dbe1654ec8cfc Mon Sep 17 00:00:00 2001 From: Jesse Hathaway Date: Tue, 3 Jun 2025 10:03:10 -0500 Subject: [PATCH 158/682] mail: fix exit code handling of sendmail cmd Prior to this commit the return code of the pclose function was assumed to be the exit code of the process. However, the returned value as specified in wait(2) is a bit packed integer and must be interpreted with the provided macros. This has no effect in success cases as the integer is still zero, but in failure cases the wrong value is used, since the 8 least significant bits contain the status code. After this commit we use the macros to obtain the status code, which fixes the EX_TEMPFAIL conditional. For WIN32 the TSRM popen_ex and pclose function are used. The return value of TSRM's pclose is not bit packed so we only check if the return value is non-zero, which should solve, #43327, https://bugs.php.net/bug.php?id=43327 --- ext/standard/mail.c | 32 +++++++++++++++++--- ext/standard/tests/mail/mail_variation3.phpt | 22 ++++++++++++++ ext/standard/tests/mail/mail_variation4.phpt | 22 ++++++++++++++ ext/standard/tests/mail/mail_variation5.phpt | 22 ++++++++++++++ 4 files changed, 93 insertions(+), 5 deletions(-) create mode 100644 ext/standard/tests/mail/mail_variation3.phpt create mode 100644 ext/standard/tests/mail/mail_variation4.phpt create mode 100644 ext/standard/tests/mail/mail_variation5.phpt diff --git a/ext/standard/mail.c b/ext/standard/mail.c index 0243ec897a05e..ff0ec13500946 100644 --- a/ext/standard/mail.c +++ b/ext/standard/mail.c @@ -25,6 +25,10 @@ #include "ext/date/php_date.h" #include "zend_smart_str.h" +#ifdef HAVE_SYS_WAIT_H +#include +#endif + #ifdef HAVE_SYSEXITS_H # include #endif @@ -562,6 +566,7 @@ PHPAPI bool php_mail(const char *to, const char *subject, const char *message, c } if (sendmail) { + int ret; #ifndef PHP_WIN32 if (EACCES == errno) { php_error_docref(NULL, E_WARNING, "Permission denied: unable to execute shell to run mail delivery binary '%s'", sendmail_path); @@ -582,24 +587,41 @@ PHPAPI bool php_mail(const char *to, const char *subject, const char *message, c fprintf(sendmail, "%s%s", hdr, line_sep); } fprintf(sendmail, "%s%s%s", line_sep, message, line_sep); - int ret = pclose(sendmail); +#ifdef PHP_WIN32 + ret = pclose(sendmail); #if PHP_SIGCHILD if (sig_handler) { signal(SIGCHLD, sig_handler); } #endif - -#ifdef PHP_WIN32 - if (ret == -1) #else + int wstatus = pclose(sendmail); +#if PHP_SIGCHILD + if (sig_handler) { + signal(SIGCHLD, sig_handler); + } +#endif + /* Determine the wait(2) exit status */ + if (wstatus == -1) { + MAIL_RET(false); + } else if (WIFSIGNALED(wstatus)) { + MAIL_RET(false); + } else { + if (WIFEXITED(wstatus)) { + ret = WEXITSTATUS(wstatus); + } else { + MAIL_RET(false); + } + } +#endif + #if defined(EX_TEMPFAIL) if ((ret != EX_OK)&&(ret != EX_TEMPFAIL)) #elif defined(EX_OK) if (ret != EX_OK) #else if (ret != 0) -#endif #endif { MAIL_RET(false); diff --git a/ext/standard/tests/mail/mail_variation3.phpt b/ext/standard/tests/mail/mail_variation3.phpt new file mode 100644 index 0000000000000..03908242d88e9 --- /dev/null +++ b/ext/standard/tests/mail/mail_variation3.phpt @@ -0,0 +1,22 @@ +--TEST-- +Test mail() function : variation sendmail temp fail +--INI-- +sendmail_path=exit 75 +--SKIPIF-- + +--FILE-- + +--EXPECT-- +*** Testing mail() : variation *** +bool(true) diff --git a/ext/standard/tests/mail/mail_variation4.phpt b/ext/standard/tests/mail/mail_variation4.phpt new file mode 100644 index 0000000000000..8b3b301ac0be4 --- /dev/null +++ b/ext/standard/tests/mail/mail_variation4.phpt @@ -0,0 +1,22 @@ +--TEST-- +Test mail() function : variation sigterm +--INI-- +sendmail_path="kill \$\$" +--SKIPIF-- + +--FILE-- + +--EXPECT-- +*** Testing mail() : variation *** +bool(false) diff --git a/ext/standard/tests/mail/mail_variation5.phpt b/ext/standard/tests/mail/mail_variation5.phpt new file mode 100644 index 0000000000000..a6c25feba5e1d --- /dev/null +++ b/ext/standard/tests/mail/mail_variation5.phpt @@ -0,0 +1,22 @@ +--TEST-- +Test mail() function : variation non-zero exit +--INI-- +sendmail_path="exit 123" +--SKIPIF-- + +--FILE-- + +--EXPECT-- +*** Testing mail() : variation *** +bool(false) From 6a7561203de89031147c0de6fe09cad905f82984 Mon Sep 17 00:00:00 2001 From: Jesse Hathaway Date: Tue, 3 Jun 2025 10:03:10 -0500 Subject: [PATCH 159/682] mail: add logging on errors Prior to this commit the exit code of the sendmail command, called by the mail function was lost, since the mail function only returns true or false. Add additional logging to the mail function to capture the exit code when the sendmail command fails. --- ext/standard/mail.c | 4 ++++ ext/standard/tests/mail/gh10990.phpt | 3 ++- ext/standard/tests/mail/mail_basic5.phpt | 4 +++- ext/standard/tests/mail/mail_variation1.phpt | 4 +++- ext/standard/tests/mail/mail_variation4.phpt | 4 +++- ext/standard/tests/mail/mail_variation5.phpt | 4 +++- 6 files changed, 18 insertions(+), 5 deletions(-) diff --git a/ext/standard/mail.c b/ext/standard/mail.c index ff0ec13500946..41e2a02078e78 100644 --- a/ext/standard/mail.c +++ b/ext/standard/mail.c @@ -604,13 +604,16 @@ PHPAPI bool php_mail(const char *to, const char *subject, const char *message, c #endif /* Determine the wait(2) exit status */ if (wstatus == -1) { + php_error_docref(NULL, E_WARNING, "Sendmail pclose failed %d (%s)", errno, strerror(errno)); MAIL_RET(false); } else if (WIFSIGNALED(wstatus)) { + php_error_docref(NULL, E_WARNING, "Sendmail killed by signal %d (%s)", WTERMSIG(wstatus), strsignal(WTERMSIG(wstatus))); MAIL_RET(false); } else { if (WIFEXITED(wstatus)) { ret = WEXITSTATUS(wstatus); } else { + php_error_docref(NULL, E_WARNING, "Sendmail did not exit"); MAIL_RET(false); } } @@ -624,6 +627,7 @@ PHPAPI bool php_mail(const char *to, const char *subject, const char *message, c if (ret != 0) #endif { + php_error_docref(NULL, E_WARNING, "Sendmail exited with non-zero exit code %d", ret); MAIL_RET(false); } else { MAIL_RET(true); diff --git a/ext/standard/tests/mail/gh10990.phpt b/ext/standard/tests/mail/gh10990.phpt index 4f74c17c22bda..ee28f30313858 100644 --- a/ext/standard/tests/mail/gh10990.phpt +++ b/ext/standard/tests/mail/gh10990.phpt @@ -13,5 +13,6 @@ $from = 'test@example.com'; $headers = ['From' => &$from]; var_dump(mail('test@example.com', 'Test', 'Test', $headers)); ?> ---EXPECT-- +--EXPECTF-- +Warning: mail(): Sendmail exited with non-zero exit code 127 in %sgh10990.php on line %d bool(false) diff --git a/ext/standard/tests/mail/mail_basic5.phpt b/ext/standard/tests/mail/mail_basic5.phpt index b427fbeb670a0..73be1e1eaa1ad 100644 --- a/ext/standard/tests/mail/mail_basic5.phpt +++ b/ext/standard/tests/mail/mail_basic5.phpt @@ -20,7 +20,9 @@ $message = 'A Message'; echo "-- failure --\n"; var_dump( mail($to, $subject, $message) ); ?> ---EXPECT-- +--EXPECTF-- *** Testing mail() : basic functionality *** -- failure -- + +Warning: mail(): Sendmail exited with non-zero exit code 1 in %smail_basic5.php on line %d bool(false) diff --git a/ext/standard/tests/mail/mail_variation1.phpt b/ext/standard/tests/mail/mail_variation1.phpt index 75adc62822358..16779bcf455ee 100644 --- a/ext/standard/tests/mail/mail_variation1.phpt +++ b/ext/standard/tests/mail/mail_variation1.phpt @@ -17,6 +17,8 @@ $subject = 'Test Subject'; $message = 'A Message'; var_dump( mail($to, $subject, $message) ); ?> ---EXPECT-- +--EXPECTF-- *** Testing mail() : variation *** + +Warning: mail(): Sendmail exited with non-zero exit code 127 in %smail_variation1.php on line %d bool(false) diff --git a/ext/standard/tests/mail/mail_variation4.phpt b/ext/standard/tests/mail/mail_variation4.phpt index 8b3b301ac0be4..c761e3bdbd199 100644 --- a/ext/standard/tests/mail/mail_variation4.phpt +++ b/ext/standard/tests/mail/mail_variation4.phpt @@ -17,6 +17,8 @@ $subject = 'Test Subject'; $message = 'A Message'; var_dump(mail($to, $subject, $message)); ?> ---EXPECT-- +--EXPECTF-- *** Testing mail() : variation *** + +Warning: mail(): Sendmail killed by signal %d (%s) in %smail_variation4.php on line %d bool(false) diff --git a/ext/standard/tests/mail/mail_variation5.phpt b/ext/standard/tests/mail/mail_variation5.phpt index a6c25feba5e1d..9e430f40c5dfc 100644 --- a/ext/standard/tests/mail/mail_variation5.phpt +++ b/ext/standard/tests/mail/mail_variation5.phpt @@ -17,6 +17,8 @@ $subject = 'Test Subject'; $message = 'A Message'; var_dump(mail($to, $subject, $message)); ?> ---EXPECT-- +--EXPECTF-- *** Testing mail() : variation *** + +Warning: mail(): Sendmail exited with non-zero exit code 123 in %smail_variation5.php on line %d bool(false) From fc04966c364be62a818e0c7e495ba0bc2d5bfc97 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Tue, 24 Jun 2025 12:30:35 +0200 Subject: [PATCH 160/682] Add NEWS and UPGRADING for sendmail error handling changes --- NEWS | 2 ++ UPGRADING | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/NEWS b/NEWS index 65fb408ddaacf..b3ac9d6b405bf 100644 --- a/NEWS +++ b/NEWS @@ -261,6 +261,8 @@ PHP NEWS . Added array_first() and array_last(). (nielsdos) . Fixed bug GH-18823 (setlocale's 2nd and 3rd argument ignores strict_types). (nielsdos) + . Fixed exit code handling of sendmail cmd and added warnings. + (Jesse Hathaway) - Streams: . Fixed bug GH-16889 (stream_select() timeout useless for pipes on Windows). diff --git a/UPGRADING b/UPGRADING index 4c6e40cfdebaf..1462be95a43fc 100644 --- a/UPGRADING +++ b/UPGRADING @@ -217,6 +217,12 @@ PHP 8.5 UPGRADE NOTES Sqlite3Stmt::EXPLAIN_MODE_EXPLAIN and Sqlite3Stmt::EXPLAIN_MODE_EXPLAIN_QUERY_PLAN. +- Standard: + . mail() now returns the actual sendmail error and detects if the sendmail + process was terminated unexpectedly. In such cases, a warning is emitted + and the function returns false. Previously, these errors were silently + ignored. This change affects only the sendmail transport. + - XSL: . The $namespace argument of XSLTProcessor::getParameter(), XSLTProcessor::setParameter() and XSLTProcessor::removeParameter() From 537ae4f990d35ff6d54d7af31b1d2cfa064c021c Mon Sep 17 00:00:00 2001 From: Saki Takamachi <34942839+SakiTakamachi@users.noreply.github.com> Date: Tue, 24 Jun 2025 20:40:09 +0900 Subject: [PATCH 161/682] removed `ZEND_IS_XDIGIT()` (#18926) --- UPGRADING.INTERNALS | 2 ++ Zend/zend_operators.h | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS index f421242dba6c6..acc9612b72e30 100644 --- a/UPGRADING.INTERNALS +++ b/UPGRADING.INTERNALS @@ -43,6 +43,8 @@ PHP 8.5 INTERNALS UPGRADE NOTES . Added the zend_update_exception_properties() function for instantiating Exception child classes. It updates the $message, $code, and $previous properties. + . ZEND_IS_XDIGIT() macro was removed because it was unused and its name + did not match its actual behavior. ======================== 2. Build system changes diff --git a/Zend/zend_operators.h b/Zend/zend_operators.h index db86345b69968..a7537d1b3ef33 100644 --- a/Zend/zend_operators.h +++ b/Zend/zend_operators.h @@ -156,7 +156,6 @@ static zend_always_inline zend_long zend_dval_to_lval_safe(double d) } #define ZEND_IS_DIGIT(c) ((c) >= '0' && (c) <= '9') -#define ZEND_IS_XDIGIT(c) (((c) >= 'A' && (c) <= 'F') || ((c) >= 'a' && (c) <= 'f')) static zend_always_inline uint8_t is_numeric_string_ex(const char *str, size_t length, zend_long *lval, double *dval, bool allow_errors, int *oflow_info, bool *trailing_data) From 39cf27689bc13cf94c047675901c3fb43ba44879 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Tue, 24 Jun 2025 15:22:25 +0200 Subject: [PATCH 162/682] php_gdb: Print some numeric fields as hexadecimal (#18925) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * php_gdb: Print `zend_string*`’s `h` field as hexadecimal A decimal representation of a hash value is not particularly meaningful and makes it harder to compare hash values. * php_gdb: Print `HashTable*`’s `nTableMask` field as hexadecimal --- main/debug_gdb_scripts.c | 11 ++++++----- scripts/gdb/php_gdb.py | 11 ++++++----- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/main/debug_gdb_scripts.c b/main/debug_gdb_scripts.c index 3806f6aab60dd..e3c522bc04843 100644 --- a/main/debug_gdb_scripts.c +++ b/main/debug_gdb_scripts.c @@ -719,6 +719,8 @@ asm( ".ascii \" for field in self.val.type.fields():\\n\"\n" ".ascii \" if field.name == 'val':\\n\"\n" ".ascii \" yield ('val', format_zstr(self.val))\\n\"\n" + ".ascii \" elif field.name == 'h':\\n\"\n" + ".ascii \" yield (field.name, \\\"0x%x\\\" % self.val[field.name])\\n\"\n" ".ascii \" else:\\n\"\n" ".ascii \" yield (field.name, format_nested(self.val[field.name]))\\n\"\n" ".ascii \"\\n\"\n" @@ -751,12 +753,11 @@ asm( ".ascii \" def children(self):\\n\"\n" ".ascii \" for field in self.val.type.fields():\\n\"\n" ".ascii \" if field.name is None:\\n\"\n" - ".ascii \" name = ''\\n\"\n" - ".ascii \" val = self.val[field]\\n\"\n" + ".ascii \" yield ('', format_nested(self.val[field]))\\n\"\n" + ".ascii \" elif field.name == 'nTableMask':\\n\"\n" + ".ascii \" yield (field.name, \\\"0x%x\\\" % self.val[field.name])\\n\"\n" ".ascii \" else:\\n\"\n" - ".ascii \" name = field.name\\n\"\n" - ".ascii \" val = self.val[field.name]\\n\"\n" - ".ascii \" yield (name, format_nested(val))\\n\"\n" + ".ascii \" yield (field.name, format_nested(self.val[field.name]))\\n\"\n" ".ascii \"\\n\"\n" ".ascii \"pp_set.add_printer('zend_array', '^_zend_array$', ZendArrayPrettyPrinter)\\n\"\n" ".ascii \"\\n\"\n" diff --git a/scripts/gdb/php_gdb.py b/scripts/gdb/php_gdb.py index ae8396a2ac0c2..1c5da0154aecd 100644 --- a/scripts/gdb/php_gdb.py +++ b/scripts/gdb/php_gdb.py @@ -49,6 +49,8 @@ def children(self): for field in self.val.type.fields(): if field.name == 'val': yield ('val', format_zstr(self.val)) + elif field.name == 'h': + yield (field.name, "0x%x" % self.val[field.name]) else: yield (field.name, format_nested(self.val[field.name])) @@ -81,12 +83,11 @@ def to_string(self): def children(self): for field in self.val.type.fields(): if field.name is None: - name = '' - val = self.val[field] + yield ('', format_nested(self.val[field])) + elif field.name == 'nTableMask': + yield (field.name, "0x%x" % self.val[field.name]) else: - name = field.name - val = self.val[field.name] - yield (name, format_nested(val)) + yield (field.name, format_nested(self.val[field.name])) pp_set.add_printer('zend_array', '^_zend_array$', ZendArrayPrettyPrinter) From 5ed8b2be5533fbd4db95d9724d268eb9c9741f14 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Tue, 24 Jun 2025 19:06:47 +0200 Subject: [PATCH 163/682] Fix GH-18897: printf: empty precision is interpreted as precision 6, not as precision 0 (#18912) Like in other languages, and especially C where printf originates from, a missing precision should be treated as a 0 precision. Because the ADJ_PRECISION flag was not set, the double formatting code resetted the precision to the default float precision of 6. --- NEWS | 2 ++ UPGRADING | 5 +++++ ext/standard/formatted_print.c | 1 + ext/standard/tests/strings/gh18897.phpt | 10 ++++++++++ 4 files changed, 18 insertions(+) create mode 100644 ext/standard/tests/strings/gh18897.phpt diff --git a/NEWS b/NEWS index b3ac9d6b405bf..8f129d715d873 100644 --- a/NEWS +++ b/NEWS @@ -263,6 +263,8 @@ PHP NEWS (nielsdos) . Fixed exit code handling of sendmail cmd and added warnings. (Jesse Hathaway) + . Fixed bug GH-18897 (printf: empty precision is interpreted as precision 6, + not as precision 0). (nielsdos) - Streams: . Fixed bug GH-16889 (stream_select() timeout useless for pipes on Windows). diff --git a/UPGRADING b/UPGRADING index 1462be95a43fc..4dca8f2b2c213 100644 --- a/UPGRADING +++ b/UPGRADING @@ -116,6 +116,11 @@ PHP 8.5 UPGRADE NOTES . SplFileObject::fwrite's parameter $length is now nullable. The default value changed from 0 to null. +- Standard: + . Using a printf-family function with a formatter that did not specify the + precision previously incorrectly reset the precision instead of treating + it as a precision of 0. See GH-18897. + ======================================== 2. New Features ======================================== diff --git a/ext/standard/formatted_print.c b/ext/standard/formatted_print.c index 9a74ae6c877f3..b2c287c02900c 100644 --- a/ext/standard/formatted_print.c +++ b/ext/standard/formatted_print.c @@ -591,6 +591,7 @@ php_formatted_print(char *format, size_t format_len, zval *args, int argc, int n expprec = 1; } else { precision = 0; + adjusting |= ADJ_PRECISION; } } else { precision = 0; diff --git a/ext/standard/tests/strings/gh18897.phpt b/ext/standard/tests/strings/gh18897.phpt new file mode 100644 index 0000000000000..328ea7161e023 --- /dev/null +++ b/ext/standard/tests/strings/gh18897.phpt @@ -0,0 +1,10 @@ +--TEST-- +GH-18897 (printf: empty precision is interpreted as precision 6, not as precision 0) +--FILE-- + +--EXPECT-- +3 +3 From ca49a7bec2a0a8d77bfa4b6d375ca0ffa4edc5ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Tue, 24 Jun 2025 20:14:40 +0200 Subject: [PATCH 164/682] RFC: Turn `clone()` into a function (#18919) RFC: https://wiki.php.net/rfc/clone_with_v2 Co-authored-by: Volker Dusch --- NEWS | 1 + UPGRADING | 2 + Zend/Optimizer/zend_func_infos.h | 1 + Zend/tests/assert/expect_015.phpt | 2 +- Zend/tests/clone/ast.phpt | 31 +++++++++++++++ Zend/tests/clone/bug36071.phpt | 14 +++---- Zend/tests/clone/bug42817.phpt | 14 +++---- Zend/tests/clone/bug42818.phpt | 13 +++--- Zend/tests/clone/clone_001.phpt | 13 +++--- Zend/tests/clone/clone_003.phpt | 12 +++--- Zend/tests/clone/clone_005.phpt | 55 ++++++++++++++++++++++++++ Zend/tests/magic_methods/bug73288.phpt | 1 + Zend/zend_API.c | 1 + Zend/zend_ast.c | 2 - Zend/zend_ast.h | 1 - Zend/zend_builtin_functions.c | 43 ++++++++++++++++++++ Zend/zend_builtin_functions.stub.php | 3 ++ Zend/zend_builtin_functions_arginfo.h | 8 +++- Zend/zend_compile.c | 30 +++++++------- Zend/zend_language_parser.y | 11 +++++- Zend/zend_string.h | 1 + Zend/zend_vm_def.h | 4 +- Zend/zend_vm_execute.h | 16 ++++++-- build/gen_stub.php | 4 ++ ext/opcache/tests/func_info.phpt | 2 +- 25 files changed, 227 insertions(+), 58 deletions(-) create mode 100644 Zend/tests/clone/ast.phpt create mode 100644 Zend/tests/clone/clone_005.phpt diff --git a/NEWS b/NEWS index 8f129d715d873..930dbec180221 100644 --- a/NEWS +++ b/NEWS @@ -59,6 +59,7 @@ PHP NEWS . Added support for `final` with constructor property promotion. (DanielEScherzer) . Do not use RTLD_DEEPBIND if dlmopen is available. (Daniil Gentili) + . Make `clone() a function. (timwolla, edorian) - Curl: . Added curl_multi_get_handles(). (timwolla) diff --git a/UPGRADING b/UPGRADING index 4dca8f2b2c213..98537007f65cd 100644 --- a/UPGRADING +++ b/UPGRADING @@ -374,6 +374,8 @@ PHP 8.5 UPGRADE NOTES . get_exception_handler() allows retrieving the current user-defined exception handler function. RFC: https://wiki.php.net/rfc/get-error-exception-handler + . The clone language construct is now a function. + RFC: https://wiki.php.net/rfc/clone_with_v2 - Curl: . curl_multi_get_handles() allows retrieving all CurlHandles current diff --git a/Zend/Optimizer/zend_func_infos.h b/Zend/Optimizer/zend_func_infos.h index 3655e5fd21c35..c36b7490de62c 100644 --- a/Zend/Optimizer/zend_func_infos.h +++ b/Zend/Optimizer/zend_func_infos.h @@ -1,6 +1,7 @@ /* This is a generated file, edit the .stub.php files instead. */ static const func_info_t func_infos[] = { + F1("clone", MAY_BE_OBJECT), F1("zend_version", MAY_BE_STRING), FN("func_get_args", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_OF_ANY), F1("get_class_vars", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_STRING|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF), diff --git a/Zend/tests/assert/expect_015.phpt b/Zend/tests/assert/expect_015.phpt index 695c4c166a83c..9f8e9b77003bc 100644 --- a/Zend/tests/assert/expect_015.phpt +++ b/Zend/tests/assert/expect_015.phpt @@ -183,7 +183,7 @@ assert(0 && ($a = function () { $x = $a ?? $b; [$a, $b, $c] = [1, 2 => 'x', 'z' => 'c']; @foo(); - $y = clone $x; + $y = \clone($x); yield 1 => 2; yield from $x; })) diff --git a/Zend/tests/clone/ast.phpt b/Zend/tests/clone/ast.phpt new file mode 100644 index 0000000000000..89a1a0a481000 --- /dev/null +++ b/Zend/tests/clone/ast.phpt @@ -0,0 +1,31 @@ +--TEST-- +Ast Printing +--FILE-- +getMessage(), PHP_EOL; +} + +try { + assert(false && $y = clone($x)); +} catch (Error $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + assert(false && $y = clone(...)); +} catch (Error $e) { + echo $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +assert(false && ($y = \clone($x))) +assert(false && ($y = \clone($x))) +assert(false && ($y = \clone(...))) diff --git a/Zend/tests/clone/bug36071.phpt b/Zend/tests/clone/bug36071.phpt index 945118fef3754..e1a4baa7226ee 100644 --- a/Zend/tests/clone/bug36071.phpt +++ b/Zend/tests/clone/bug36071.phpt @@ -4,11 +4,11 @@ Bug #36071 (Engine Crash related with 'clone') error_reporting=4095 --FILE-- b = 0; +try { + $a = clone 0; +} catch (Error $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} ?> ---EXPECTF-- -Fatal error: Uncaught Error: __clone method called on non-object in %sbug36071.php:2 -Stack trace: -#0 {main} - thrown in %sbug36071.php on line 2 +--EXPECT-- +TypeError: clone(): Argument #1 ($object) must be of type object, int given diff --git a/Zend/tests/clone/bug42817.phpt b/Zend/tests/clone/bug42817.phpt index a681d861d0c8f..b5f53222d7ee5 100644 --- a/Zend/tests/clone/bug42817.phpt +++ b/Zend/tests/clone/bug42817.phpt @@ -2,11 +2,11 @@ Bug #42817 (clone() on a non-object does not result in a fatal error) --FILE-- b, $c); +try { + $a = clone(null); +} catch (Error $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} ?> ---EXPECTF-- -Fatal error: Uncaught Error: __clone method called on non-object in %sbug42817.php:2 -Stack trace: -#0 {main} - thrown in %sbug42817.php on line 2 +--EXPECT-- +TypeError: clone(): Argument #1 ($object) must be of type object, null given diff --git a/Zend/tests/clone/bug42818.phpt b/Zend/tests/clone/bug42818.phpt index b37ce13fd174a..08ba05fcfaa22 100644 --- a/Zend/tests/clone/bug42818.phpt +++ b/Zend/tests/clone/bug42818.phpt @@ -2,10 +2,11 @@ Bug #42818 ($foo = clone(array()); leaks memory) --FILE-- getMessage(), PHP_EOL; +} ?> ---EXPECTF-- -Fatal error: Uncaught Error: __clone method called on non-object in %sbug42818.php:2 -Stack trace: -#0 {main} - thrown in %sbug42818.php on line 2 +--EXPECT-- +TypeError: clone(): Argument #1 ($object) must be of type object, array given diff --git a/Zend/tests/clone/clone_001.phpt b/Zend/tests/clone/clone_001.phpt index 87024c3cd5614..91fa6f551176d 100644 --- a/Zend/tests/clone/clone_001.phpt +++ b/Zend/tests/clone/clone_001.phpt @@ -3,11 +3,12 @@ Using clone statement on non-object --FILE-- getMessage(), PHP_EOL; +} ?> ---EXPECTF-- -Fatal error: Uncaught Error: __clone method called on non-object in %s:%d -Stack trace: -#0 {main} - thrown in %s on line %d +--EXPECT-- +TypeError: clone(): Argument #1 ($object) must be of type object, array given diff --git a/Zend/tests/clone/clone_003.phpt b/Zend/tests/clone/clone_003.phpt index f163616a876dc..b8bb2833dc257 100644 --- a/Zend/tests/clone/clone_003.phpt +++ b/Zend/tests/clone/clone_003.phpt @@ -3,13 +3,13 @@ Using clone statement on undefined variable --FILE-- getMessage(), PHP_EOL; +} ?> --EXPECTF-- Warning: Undefined variable $b in %s on line %d - -Fatal error: Uncaught Error: __clone method called on non-object in %s:%d -Stack trace: -#0 {main} - thrown in %s on line %d +TypeError: clone(): Argument #1 ($object) must be of type object, null given diff --git a/Zend/tests/clone/clone_005.phpt b/Zend/tests/clone/clone_005.phpt new file mode 100644 index 0000000000000..e0366cae67cb5 --- /dev/null +++ b/Zend/tests/clone/clone_005.phpt @@ -0,0 +1,55 @@ +--TEST-- +Clone as a function. +--FILE-- +clone_me()[0]; + +var_dump($f !== $clone); + +?> +--EXPECTF-- +object(stdClass)#%d (0) { +} +array(3) { + [0]=> + object(stdClass)#%d (0) { + } + [1]=> + object(stdClass)#%d (0) { + } + [2]=> + object(stdClass)#%d (0) { + } +} +array(3) { + [0]=> + object(stdClass)#%d (0) { + } + [1]=> + object(stdClass)#%d (0) { + } + [2]=> + object(stdClass)#%d (0) { + } +} +bool(true) diff --git a/Zend/tests/magic_methods/bug73288.phpt b/Zend/tests/magic_methods/bug73288.phpt index 52e8eedeaf013..5e1334cacd07c 100644 --- a/Zend/tests/magic_methods/bug73288.phpt +++ b/Zend/tests/magic_methods/bug73288.phpt @@ -23,6 +23,7 @@ function test_clone() { $b = clone $c->x; } +// No catch, because we want to test Exception::__toString(). test_clone(); ?> --EXPECTF-- diff --git a/Zend/zend_API.c b/Zend/zend_API.c index d29fe8ae8e3c3..df8b4252c42ad 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -3648,6 +3648,7 @@ static void zend_disable_function(const char *function_name, size_t function_nam if (UNEXPECTED( (function_name_length == strlen("exit") && !memcmp(function_name, "exit", strlen("exit"))) || (function_name_length == strlen("die") && !memcmp(function_name, "die", strlen("die"))) + || (function_name_length == strlen("clone") && !memcmp(function_name, "clone", strlen("clone"))) )) { zend_error(E_WARNING, "Cannot disable function %s()", function_name); return; diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index cdc86faa95aa3..728695bd9e930 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -2345,8 +2345,6 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio } smart_str_appendc(str, '`'); break; - case ZEND_AST_CLONE: - PREFIX_OP("clone ", 270, 271); case ZEND_AST_PRINT: PREFIX_OP("print ", 60, 61); case ZEND_AST_INCLUDE_OR_EVAL: diff --git a/Zend/zend_ast.h b/Zend/zend_ast.h index c82ca66c9f573..08400cff5dd8e 100644 --- a/Zend/zend_ast.h +++ b/Zend/zend_ast.h @@ -89,7 +89,6 @@ enum _zend_ast_kind { ZEND_AST_ISSET, ZEND_AST_SILENCE, ZEND_AST_SHELL_EXEC, - ZEND_AST_CLONE, ZEND_AST_PRINT, ZEND_AST_INCLUDE_OR_EVAL, ZEND_AST_UNARY_OP, diff --git a/Zend/zend_builtin_functions.c b/Zend/zend_builtin_functions.c index 7a07ceadce2e2..48e5c70897294 100644 --- a/Zend/zend_builtin_functions.c +++ b/Zend/zend_builtin_functions.c @@ -69,6 +69,49 @@ zend_result zend_startup_builtin_functions(void) /* {{{ */ } /* }}} */ +ZEND_FUNCTION(clone) +{ + zend_object *zobj; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_OBJ(zobj) + ZEND_PARSE_PARAMETERS_END(); + + /* clone() also exists as the ZEND_CLONE OPcode and both implementations must be kept in sync. */ + + zend_class_entry *scope = zend_get_executed_scope(); + + zend_class_entry *ce = zobj->ce; + zend_function *clone = ce->clone; + + if (UNEXPECTED(zobj->handlers->clone_obj == NULL)) { + zend_throw_error(NULL, "Trying to clone an uncloneable object of class %s", ZSTR_VAL(ce->name)); + RETURN_THROWS(); + } + + if (clone && !(clone->common.fn_flags & ZEND_ACC_PUBLIC)) { + if (clone->common.scope != scope) { + if (UNEXPECTED(clone->common.fn_flags & ZEND_ACC_PRIVATE) + || UNEXPECTED(!zend_check_protected(zend_get_function_root_class(clone), scope))) { + zend_throw_error(NULL, "Call to %s %s::__clone() from %s%s", + zend_visibility_string(clone->common.fn_flags), ZSTR_VAL(clone->common.scope->name), + scope ? "scope " : "global scope", + scope ? ZSTR_VAL(scope->name) : "" + ); + RETURN_THROWS(); + } + } + } + + zend_object *cloned; + cloned = zobj->handlers->clone_obj(zobj); + + ZEND_ASSERT(cloned || EG(exception)); + if (EXPECTED(cloned)) { + RETURN_OBJ(cloned); + } +} + ZEND_FUNCTION(exit) { zend_string *str = NULL; diff --git a/Zend/zend_builtin_functions.stub.php b/Zend/zend_builtin_functions.stub.php index 7f316835aea6b..256c405c71c28 100644 --- a/Zend/zend_builtin_functions.stub.php +++ b/Zend/zend_builtin_functions.stub.php @@ -7,6 +7,9 @@ class stdClass { } +/** @refcount 1 */ +function _clone(object $object): object {} + function exit(string|int $status = 0): never {} /** @alias exit */ diff --git a/Zend/zend_builtin_functions_arginfo.h b/Zend/zend_builtin_functions_arginfo.h index 9498b8292f892..1c595ecd5777c 100644 --- a/Zend/zend_builtin_functions_arginfo.h +++ b/Zend/zend_builtin_functions_arginfo.h @@ -1,5 +1,9 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: a24761186f1ddf758e648b0a764826537cbd33b9 */ + * Stub hash: 12327caa3fe940ccef68ed99f9278982dc0173a5 */ + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_clone, 0, 1, IS_OBJECT, 0) + ZEND_ARG_TYPE_INFO(0, object, IS_OBJECT, 0) +ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_exit, 0, 0, IS_NEVER, 0) ZEND_ARG_TYPE_MASK(0, status, MAY_BE_STRING|MAY_BE_LONG, "0") @@ -243,6 +247,7 @@ static const zend_frameless_function_info frameless_function_infos_class_exists[ { 0 }, }; +ZEND_FUNCTION(clone); ZEND_FUNCTION(exit); ZEND_FUNCTION(zend_version); ZEND_FUNCTION(func_num_args); @@ -306,6 +311,7 @@ ZEND_FUNCTION(gc_disable); ZEND_FUNCTION(gc_status); static const zend_function_entry ext_functions[] = { + ZEND_FE(clone, arginfo_clone) ZEND_FE(exit, arginfo_exit) ZEND_RAW_FENTRY("die", zif_exit, arginfo_die, 0, NULL, NULL) ZEND_FE(zend_version, arginfo_zend_version) diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 28bea1a21d759..f3f6d1b75aec1 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -4930,6 +4930,20 @@ static zend_result zend_compile_func_sprintf(znode *result, zend_ast_list *args) return SUCCESS; } +static zend_result zend_compile_func_clone(znode *result, zend_ast_list *args) +{ + znode arg_node; + + if (args->children != 1) { + return FAILURE; + } + + zend_compile_expr(&arg_node, args->child[0]); + zend_emit_op_tmp(result, ZEND_CLONE, &arg_node, NULL); + + return SUCCESS; +} + static zend_result zend_try_compile_special_func_ex(znode *result, zend_string *lcname, zend_ast_list *args, zend_function *fbc, uint32_t type) /* {{{ */ { if (zend_string_equals_literal(lcname, "strlen")) { @@ -4998,6 +5012,8 @@ static zend_result zend_try_compile_special_func_ex(znode *result, zend_string * return zend_compile_func_array_key_exists(result, args); } else if (zend_string_equals_literal(lcname, "sprintf")) { return zend_compile_func_sprintf(result, args); + } else if (zend_string_equals(lcname, ZSTR_KNOWN(ZEND_STR_CLONE))) { + return zend_compile_func_clone(result, args); } else { return FAILURE; } @@ -5391,17 +5407,6 @@ static void zend_compile_new(znode *result, zend_ast *ast) /* {{{ */ } /* }}} */ -static void zend_compile_clone(znode *result, zend_ast *ast) /* {{{ */ -{ - zend_ast *obj_ast = ast->child[0]; - - znode obj_node; - zend_compile_expr(&obj_node, obj_ast); - - zend_emit_op_tmp(result, ZEND_CLONE, &obj_node, NULL); -} -/* }}} */ - static void zend_compile_global_var(zend_ast *ast) /* {{{ */ { zend_ast *var_ast = ast->child[0]; @@ -11717,9 +11722,6 @@ static void zend_compile_expr_inner(znode *result, zend_ast *ast) /* {{{ */ case ZEND_AST_NEW: zend_compile_new(result, ast); return; - case ZEND_AST_CLONE: - zend_compile_clone(result, ast); - return; case ZEND_AST_ASSIGN_OP: zend_compile_compound_assign(result, ast); return; diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index 816b8126cbf25..016c6e5c9d098 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -1228,7 +1228,16 @@ expr: { $$ = zend_ast_create(ZEND_AST_ASSIGN, $1, $3); } | variable '=' ampersand variable { $$ = zend_ast_create(ZEND_AST_ASSIGN_REF, $1, $4); } - | T_CLONE expr { $$ = zend_ast_create(ZEND_AST_CLONE, $2); } + | T_CLONE '(' T_ELLIPSIS ')' { + zend_ast *name = zend_ast_create_zval_from_str(ZSTR_KNOWN(ZEND_STR_CLONE)); + name->attr = ZEND_NAME_FQ; + $$ = zend_ast_create(ZEND_AST_CALL, name, zend_ast_create_fcc()); + } + | T_CLONE expr { + zend_ast *name = zend_ast_create_zval_from_str(ZSTR_KNOWN(ZEND_STR_CLONE)); + name->attr = ZEND_NAME_FQ; + $$ = zend_ast_create(ZEND_AST_CALL, name, zend_ast_create_list(1, ZEND_AST_ARG_LIST, $2)); + } | variable T_PLUS_EQUAL expr { $$ = zend_ast_create_assign_op(ZEND_ADD, $1, $3); } | variable T_MINUS_EQUAL expr diff --git a/Zend/zend_string.h b/Zend/zend_string.h index 0b2a484016ec3..f60e4dec4e71f 100644 --- a/Zend/zend_string.h +++ b/Zend/zend_string.h @@ -575,6 +575,7 @@ EMPTY_SWITCH_DEFAULT_CASE() _(ZEND_STR_UNKNOWN, "unknown") \ _(ZEND_STR_UNKNOWN_CAPITALIZED, "Unknown") \ _(ZEND_STR_EXIT, "exit") \ + _(ZEND_STR_CLONE, "clone") \ _(ZEND_STR_EVAL, "eval") \ _(ZEND_STR_INCLUDE, "include") \ _(ZEND_STR_REQUIRE, "require") \ diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 617e114dd05db..be7bc8b37b7dd 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -6006,6 +6006,8 @@ ZEND_VM_COLD_CONST_HANDLER(110, ZEND_CLONE, CONST|TMPVAR|UNUSED|THIS|CV, ANY) SAVE_OPLINE(); obj = GET_OP1_OBJ_ZVAL_PTR_UNDEF(BP_VAR_R); + /* ZEND_CLONE also exists as the clone() function and both implementations must be kept in sync. */ + do { if (OP1_TYPE == IS_CONST || (OP1_TYPE != IS_UNUSED && UNEXPECTED(Z_TYPE_P(obj) != IS_OBJECT))) { @@ -6022,7 +6024,7 @@ ZEND_VM_COLD_CONST_HANDLER(110, ZEND_CLONE, CONST|TMPVAR|UNUSED|THIS|CV, ANY) HANDLE_EXCEPTION(); } } - zend_throw_error(NULL, "__clone method called on non-object"); + zend_type_error("clone(): Argument #1 ($object) must be of type object, %s given", zend_zval_value_name(obj)); FREE_OP1(); HANDLE_EXCEPTION(); } diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 791e4b4e88437..3a13f4244d361 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -5180,6 +5180,8 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CLONE_SPEC_CONST_ SAVE_OPLINE(); obj = RT_CONSTANT(opline, opline->op1); + /* ZEND_CLONE also exists as the clone() function and both implementations must be kept in sync. */ + do { if (IS_CONST == IS_CONST || (IS_CONST != IS_UNUSED && UNEXPECTED(Z_TYPE_P(obj) != IS_OBJECT))) { @@ -5196,7 +5198,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CLONE_SPEC_CONST_ HANDLE_EXCEPTION(); } } - zend_throw_error(NULL, "__clone method called on non-object"); + zend_type_error("clone(): Argument #1 ($object) must be of type object, %s given", zend_zval_value_name(obj)); HANDLE_EXCEPTION(); } @@ -15428,6 +15430,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CLONE_SPEC_TMPVAR_HANDLER(ZEND SAVE_OPLINE(); obj = _get_zval_ptr_var(opline->op1.var EXECUTE_DATA_CC); + /* ZEND_CLONE also exists as the clone() function and both implementations must be kept in sync. */ + do { if ((IS_TMP_VAR|IS_VAR) == IS_CONST || ((IS_TMP_VAR|IS_VAR) != IS_UNUSED && UNEXPECTED(Z_TYPE_P(obj) != IS_OBJECT))) { @@ -15444,7 +15448,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CLONE_SPEC_TMPVAR_HANDLER(ZEND HANDLE_EXCEPTION(); } } - zend_throw_error(NULL, "__clone method called on non-object"); + zend_type_error("clone(): Argument #1 ($object) must be of type object, %s given", zend_zval_value_name(obj)); zval_ptr_dtor_nogc(EX_VAR(opline->op1.var)); HANDLE_EXCEPTION(); } @@ -33523,6 +33527,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CLONE_SPEC_UNUSED_HANDLER(ZEND SAVE_OPLINE(); obj = &EX(This); + /* ZEND_CLONE also exists as the clone() function and both implementations must be kept in sync. */ + do { if (IS_UNUSED == IS_CONST || (IS_UNUSED != IS_UNUSED && UNEXPECTED(Z_TYPE_P(obj) != IS_OBJECT))) { @@ -33539,7 +33545,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CLONE_SPEC_UNUSED_HANDLER(ZEND HANDLE_EXCEPTION(); } } - zend_throw_error(NULL, "__clone method called on non-object"); + zend_type_error("clone(): Argument #1 ($object) must be of type object, %s given", zend_zval_value_name(obj)); HANDLE_EXCEPTION(); } @@ -41042,6 +41048,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CLONE_SPEC_CV_HANDLER(ZEND_OPC SAVE_OPLINE(); obj = EX_VAR(opline->op1.var); + /* ZEND_CLONE also exists as the clone() function and both implementations must be kept in sync. */ + do { if (IS_CV == IS_CONST || (IS_CV != IS_UNUSED && UNEXPECTED(Z_TYPE_P(obj) != IS_OBJECT))) { @@ -41058,7 +41066,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CLONE_SPEC_CV_HANDLER(ZEND_OPC HANDLE_EXCEPTION(); } } - zend_throw_error(NULL, "__clone method called on non-object"); + zend_type_error("clone(): Argument #1 ($object) must be of type object, %s given", zend_zval_value_name(obj)); HANDLE_EXCEPTION(); } diff --git a/build/gen_stub.php b/build/gen_stub.php index fcef8213d0b55..d9327b67d042f 100755 --- a/build/gen_stub.php +++ b/build/gen_stub.php @@ -1009,6 +1009,9 @@ class FunctionName implements FunctionOrMethodName { private /* readonly */ Name $name; public function __construct(Name $name) { + if ($name->name === '_clone') { + $name = new Name('clone', $name->getAttributes()); + } $this->name = $name; } @@ -3049,6 +3052,7 @@ class PropertyInfo extends VariableLike "parent" => "ZEND_STR_PARENT", "username" => "ZEND_STR_USERNAME", "password" => "ZEND_STR_PASSWORD", + "clone" => "ZEND_STR_CLONE", ]; /** diff --git a/ext/opcache/tests/func_info.phpt b/ext/opcache/tests/func_info.phpt index 8b1f9ef436c4b..9596aa23199d2 100644 --- a/ext/opcache/tests/func_info.phpt +++ b/ext/opcache/tests/func_info.phpt @@ -16,7 +16,7 @@ foreach (get_defined_functions()["internal"] as $function) { if (in_array($function, ["extract", "compact", "get_defined_vars"])) { continue; } - $contents .= " \$result = {$function}();\n"; + $contents .= " \$result = \\{$function}();\n"; } $contents .= "}\n"; From aea3ade74fdaf5e764061858fdc1d3e5378b862b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladimir=20Vrzi=C4=87?= Date: Mon, 16 Sep 2024 17:59:40 +0200 Subject: [PATCH 165/682] ext/pcntl: Added rusage parameter to pcntl_waitid This functionality is not part of the POSIX interface. - On FreeBSD, the wait6 system call provides it - On Linux, the raw waitid system call provides it (glibc does not) close GH-15921 --- NEWS | 3 + UPGRADING | 2 + ext/pcntl/config.m4 | 5 ++ ext/pcntl/pcntl.c | 46 +++++++++++++-- ext/pcntl/pcntl.stub.php | 7 ++- ext/pcntl/pcntl_arginfo.h | 3 +- ext/pcntl/tests/pcntl_waitid.phpt | 10 ++-- ext/pcntl/tests/pcntl_waitid_rusage.phpt | 72 ++++++++++++++++++++++++ 8 files changed, 137 insertions(+), 11 deletions(-) create mode 100644 ext/pcntl/tests/pcntl_waitid_rusage.phpt diff --git a/NEWS b/NEWS index 930dbec180221..6ff942f86b2b7 100644 --- a/NEWS +++ b/NEWS @@ -134,6 +134,9 @@ PHP NEWS - Output: . Fixed calculation of aligned buffer size. (cmb) +- PCNTL: + . Extend pcntl_waitid with rusage parameter. (vrza) + - PCRE: . Upgraded to pre2lib from 10.44 to 10.45. (nielsdos) . Remove PCRE2_EXTRA_ALLOW_LOOKAROUND_BSK from pcre compile options. diff --git a/UPGRADING b/UPGRADING index 98537007f65cd..4d968ef4daf80 100644 --- a/UPGRADING +++ b/UPGRADING @@ -289,6 +289,8 @@ PHP 8.5 UPGRADE NOTES - PCNTL: . pcntl_exec() now has a formal return type of false. + . pcntl_waitid() takes an additional resource_usage argument to + gather various platform specific metrics about the child process. - PDO_PGSQL: . PDO::pgsqlCopyFromArray also supports inputs as Iterable. diff --git a/ext/pcntl/config.m4 b/ext/pcntl/config.m4 index ce26a6efd2ead..3efa8a53cad1e 100644 --- a/ext/pcntl/config.m4 +++ b/ext/pcntl/config.m4 @@ -25,6 +25,8 @@ if test "$PHP_PCNTL" != "no"; then wait3 wait4 waitid + wait6 + syscall ])) AC_CHECK_FUNCS([WIFCONTINUED],, @@ -43,6 +45,9 @@ if test "$PHP_PCNTL" != "no"; then ]),,, [#include ]) + AC_CHECK_DECLS([SYS_waitid],,, + [#include ]) + dnl if unsupported, -1 means automatically ENOSYS in this context AC_CACHE_CHECK([if sched_getcpu is supported], [php_cv_func_sched_getcpu], [AC_RUN_IFELSE([AC_LANG_SOURCE([ diff --git a/ext/pcntl/pcntl.c b/ext/pcntl/pcntl.c index 0481c0966c87a..1f061870ed453 100644 --- a/ext/pcntl/pcntl.c +++ b/ext/pcntl/pcntl.c @@ -125,7 +125,15 @@ typedef psetid_t cpu_set_t; #include #endif -#ifdef HAVE_PIDFD_OPEN +#if defined(__linux__) && defined(HAVE_DECL_SYS_WAITID) && HAVE_DECL_SYS_WAITID == 1 && defined(HAVE_SYSCALL) +#define HAVE_LINUX_RAW_SYSCALL_WAITID 1 +#endif + +#if defined(HAVE_LINUX_RAW_SYSCALL_WAITID) +#include +#endif + +#if defined(HAVE_PIDFD_OPEN) || defined(HAVE_LINUX_RAW_SYSCALL_WAITID) #include #endif @@ -401,19 +409,49 @@ PHP_FUNCTION(pcntl_waitid) bool id_is_null = 1; zval *user_siginfo = NULL; zend_long options = WEXITED; + zval *z_rusage = NULL; - ZEND_PARSE_PARAMETERS_START(0, 4) + siginfo_t siginfo; + int status; + + ZEND_PARSE_PARAMETERS_START(0, 5) Z_PARAM_OPTIONAL Z_PARAM_LONG(idtype) Z_PARAM_LONG_OR_NULL(id, id_is_null) Z_PARAM_ZVAL(user_siginfo) Z_PARAM_LONG(options) + Z_PARAM_ZVAL(z_rusage) ZEND_PARSE_PARAMETERS_END(); errno = 0; - siginfo_t siginfo; + memset(&siginfo, 0, sizeof(siginfo_t)); - int status = waitid((idtype_t) idtype, (id_t) id, &siginfo, (int) options); +#if defined(HAVE_WAIT6) || defined(HAVE_LINUX_RAW_SYSCALL_WAITID) + if (z_rusage) { + z_rusage = zend_try_array_init(z_rusage); + if (!z_rusage) { + RETURN_THROWS(); + } + struct rusage rusage; +# if defined(HAVE_WAIT6) /* FreeBSD */ + struct __wrusage wrusage; + memset(&wrusage, 0, sizeof(struct __wrusage)); + pid_t pid = wait6((idtype_t) idtype, (id_t) id, &status, (int) options, &wrusage, &siginfo); + status = pid > 0 ? 0 : pid; + memcpy(&rusage, &wrusage.wru_self, sizeof(struct rusage)); +# else /* Linux */ + memset(&rusage, 0, sizeof(struct rusage)); + status = syscall(SYS_waitid, (idtype_t) idtype, (id_t) id, &siginfo, (int) options, &rusage); +# endif + if (status == 0) { + PHP_RUSAGE_TO_ARRAY(rusage, z_rusage); + } + } else { /* POSIX */ + status = waitid((idtype_t) idtype, (id_t) id, &siginfo, (int) options); + } +#else /* POSIX */ + status = waitid((idtype_t) idtype, (id_t) id, &siginfo, (int) options); +#endif if (status == -1) { PCNTL_G(last_error) = errno; diff --git a/ext/pcntl/pcntl.stub.php b/ext/pcntl/pcntl.stub.php index c62a40139d9ed..3f3800c50abe5 100644 --- a/ext/pcntl/pcntl.stub.php +++ b/ext/pcntl/pcntl.stub.php @@ -1006,8 +1006,11 @@ function pcntl_fork(): int {} function pcntl_waitpid(int $process_id, &$status, int $flags = 0, &$resource_usage = []): int {} #if defined (HAVE_WAITID) && defined (HAVE_POSIX_IDTYPES) && defined (HAVE_DECL_WEXITED) && HAVE_DECL_WEXITED == 1 - /** @param array $info */ - function pcntl_waitid(int $idtype = P_ALL, ?int $id = null, &$info = [], int $flags = WEXITED): bool {} + /** + * @param array $info + * @param array $resource_usage + */ + function pcntl_waitid(int $idtype = P_ALL, ?int $id = null, &$info = [], int $flags = WEXITED, &$resource_usage = []): bool {} #endif /** diff --git a/ext/pcntl/pcntl_arginfo.h b/ext/pcntl/pcntl_arginfo.h index bc6581d41e17e..8b2367a7c7042 100644 --- a/ext/pcntl/pcntl_arginfo.h +++ b/ext/pcntl/pcntl_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: c637fe2de641cfd8f0ff83a908ac59bf63a68e44 */ + * Stub hash: 5e4b066d70fa264c7de3ba4b2113369c34c33e43 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_pcntl_fork, 0, 0, IS_LONG, 0) ZEND_END_ARG_INFO() @@ -17,6 +17,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_pcntl_waitid, 0, 0, _IS_BOOL, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, id, IS_LONG, 1, "null") ZEND_ARG_INFO_WITH_DEFAULT_VALUE(1, info, "[]") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, flags, IS_LONG, 0, "WEXITED") + ZEND_ARG_INFO_WITH_DEFAULT_VALUE(1, resource_usage, "[]") ZEND_END_ARG_INFO() #endif diff --git a/ext/pcntl/tests/pcntl_waitid.phpt b/ext/pcntl/tests/pcntl_waitid.phpt index b00f26f81fa2e..e297cd107a473 100644 --- a/ext/pcntl/tests/pcntl_waitid.phpt +++ b/ext/pcntl/tests/pcntl_waitid.phpt @@ -26,10 +26,12 @@ if ($pid == -1) { } else { pcntl_signal(SIGUSR1, function ($_signo, $_siginfo) { exit(42); }); posix_kill(posix_getpid(), SIGSTOP); - pcntl_signal_dispatch(); - sleep(42); - pcntl_signal_dispatch(); - exit(6); + $nanoseconds = 100; + while (true) { + pcntl_signal_dispatch(); + time_nanosleep(0, $nanoseconds); + $nanoseconds *= 2; + } } ?> --EXPECTF-- diff --git a/ext/pcntl/tests/pcntl_waitid_rusage.phpt b/ext/pcntl/tests/pcntl_waitid_rusage.phpt new file mode 100644 index 0000000000000..8df148357d1c8 --- /dev/null +++ b/ext/pcntl/tests/pcntl_waitid_rusage.phpt @@ -0,0 +1,72 @@ +--TEST-- +pcntl_waitid() and rusage +--EXTENSIONS-- +pcntl +posix +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +bool(true) +int(%d) +int(%d) +bool(true) +int(%d) +int(%d) +bool(true) +int(%d) +int(42) +bool(false) +string(5) "array" +int(0) +bool(false) +string(5) "array" +int(0) +END From 41d3440658ed22193597239f3526ca099703e5ec Mon Sep 17 00:00:00 2001 From: DanielEScherzer Date: Tue, 24 Jun 2025 13:05:52 -0700 Subject: [PATCH 166/682] NEWS: add a missing backtick [skip ci] --- NEWS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 6ff942f86b2b7..fe91a1f30ef74 100644 --- a/NEWS +++ b/NEWS @@ -59,7 +59,7 @@ PHP NEWS . Added support for `final` with constructor property promotion. (DanielEScherzer) . Do not use RTLD_DEEPBIND if dlmopen is available. (Daniil Gentili) - . Make `clone() a function. (timwolla, edorian) + . Make `clone()` a function. (timwolla, edorian) - Curl: . Added curl_multi_get_handles(). (timwolla) From e7678cdaa4f11897d5d0afd10414c37a76abcb89 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Tue, 24 Jun 2025 19:38:00 +0200 Subject: [PATCH 167/682] sqlite3: Use Z_TRY_ADDREF --- ext/sqlite3/sqlite3.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ext/sqlite3/sqlite3.c b/ext/sqlite3/sqlite3.c index 5e402300980bb..99c4bea5105ee 100644 --- a/ext/sqlite3/sqlite3.c +++ b/ext/sqlite3/sqlite3.c @@ -2479,9 +2479,7 @@ static zend_always_inline void php_sqlite3_fetch_one(int n_cols, php_sqlite3_res if (mode & PHP_SQLITE3_ASSOC) { if (mode & PHP_SQLITE3_NUM) { - if (Z_REFCOUNTED(data)) { - Z_ADDREF(data); - } + Z_TRY_ADDREF(data); } /* Note: we can't use the "add_new" variant here instead of "update" because * when the same column name is encountered, the last result should be taken. */ From 6d154758150d1a2f62fe33c3927fe5ad84de30b8 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Tue, 24 Jun 2025 19:41:14 +0200 Subject: [PATCH 168/682] sqlite3: Split off column name cache generation Also no need to reset the cache unconditionally in fetchAll(). --- ext/sqlite3/sqlite3.c | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/ext/sqlite3/sqlite3.c b/ext/sqlite3/sqlite3.c index 99c4bea5105ee..21b6840a8d1b2 100644 --- a/ext/sqlite3/sqlite3.c +++ b/ext/sqlite3/sqlite3.c @@ -1987,6 +1987,16 @@ PHP_METHOD(SQLite3Result, columnType) } /* }}} */ +static void sqlite3result_fill_column_names_cache(php_sqlite3_result *result_obj, int nb_cols) +{ + result_obj->column_names = safe_emalloc(nb_cols, sizeof(zend_string*), 0); + + for (int i = 0; i < nb_cols; i++) { + const char *column = sqlite3_column_name(result_obj->stmt_obj->stmt, i); + result_obj->column_names[i] = zend_string_init(column, strlen(column), 0); + } +} + /* {{{ Fetch a result row as both an associative or numerically indexed array or both. */ PHP_METHOD(SQLite3Result, fetchArray) { @@ -2019,12 +2029,7 @@ PHP_METHOD(SQLite3Result, fetchArray) /* Cache column names to speed up repeated fetchArray calls. */ if (mode & PHP_SQLITE3_ASSOC && !result_obj->column_names) { - result_obj->column_names = emalloc(n_cols * sizeof(zend_string*)); - - for (int i = 0; i < n_cols; i++) { - const char *column = sqlite3_column_name(result_obj->stmt_obj->stmt, i); - result_obj->column_names[i] = zend_string_init(column, strlen(column), 0); - } + sqlite3result_fill_column_names_cache(result_obj, n_cols); } array_init(return_value); @@ -2056,7 +2061,7 @@ static void sqlite3result_clear_column_names_cache(php_sqlite3_result *result) { PHP_METHOD(SQLite3Result, fetchAll) { - int i, nb_cols; + int nb_cols; bool done = false; php_sqlite3_result *result_obj; zval *object = ZEND_THIS; @@ -2071,14 +2076,8 @@ PHP_METHOD(SQLite3Result, fetchAll) SQLITE3_CHECK_INITIALIZED(result_obj->db_obj, result_obj->stmt_obj->initialised, SQLite3Result) nb_cols = sqlite3_column_count(result_obj->stmt_obj->stmt); - if (mode & PHP_SQLITE3_ASSOC) { - sqlite3result_clear_column_names_cache(result_obj); - result_obj->column_names = emalloc(nb_cols * sizeof(zend_string*)); - - for (i = 0; i < nb_cols; i++) { - const char *column = sqlite3_column_name(result_obj->stmt_obj->stmt, i); - result_obj->column_names[i] = zend_string_init(column, strlen(column), 0); - } + if (mode & PHP_SQLITE3_ASSOC && !result_obj->column_names) { + sqlite3result_fill_column_names_cache(result_obj, nb_cols); } result_obj->column_count = nb_cols; array_init(return_value); From 6233dc6210b159762a97b7759ea0883d027feac1 Mon Sep 17 00:00:00 2001 From: Shivam Mathur Date: Wed, 25 Jun 2025 01:57:07 +0530 Subject: [PATCH 169/682] Switch to windows-2022 in CI (#18927) * Switch to windows-2022 in CI windows-2019 runner will be dropped by GitHub on 2025-06-30. * xfail test cases that fail on windows-2022 --- .github/scripts/windows/build.bat | 4 +- .github/scripts/windows/find-vs-toolset.bat | 49 +++++++++++++++++++++ .github/scripts/windows/test.bat | 3 +- .github/workflows/push.yml | 2 +- .github/workflows/root.yml | 2 +- Zend/tests/bug70258.phpt | 3 ++ Zend/tests/gh11189.phpt | 3 ++ Zend/tests/gh11189_1.phpt | 3 ++ 8 files changed, 65 insertions(+), 4 deletions(-) create mode 100644 .github/scripts/windows/find-vs-toolset.bat diff --git a/.github/scripts/windows/build.bat b/.github/scripts/windows/build.bat index ebe08c86b5ea9..139cce8be816e 100644 --- a/.github/scripts/windows/build.bat +++ b/.github/scripts/windows/build.bat @@ -43,7 +43,9 @@ if not exist "%SDK_RUNNER%" ( exit /b 3 ) -cmd /c %SDK_RUNNER% -t .github\scripts\windows\build_task.bat +for /f "delims=" %%T in ('call .github\scripts\windows\find-vs-toolset.bat %PHP_BUILD_CRT%') do set "VS_TOOLSET=%%T" +echo Got VS Toolset %VS_TOOLSET% +cmd /c %SDK_RUNNER% -s %VS_TOOLSET% -t .github\scripts\windows\build_task.bat if %errorlevel% neq 0 exit /b 3 exit /b 0 diff --git a/.github/scripts/windows/find-vs-toolset.bat b/.github/scripts/windows/find-vs-toolset.bat new file mode 100644 index 0000000000000..2d9e68e730318 --- /dev/null +++ b/.github/scripts/windows/find-vs-toolset.bat @@ -0,0 +1,49 @@ +@echo off + +setlocal enabledelayedexpansion + +if "%~1"=="" ( + echo ERROR: Usage: %~nx0 [vc14^|vc15^|vs16^|vs17] + exit /b 1 +) + +set "toolsets_vc14=14.0" +set "toolsets_vc15=" +set "toolsets_vs16=" +set "toolsets_vs17=" + + +for /f "usebackq tokens=*" %%I in (`vswhere.exe -latest -find "VC\Tools\MSVC"`) do set "MSVCDIR=%%I" + +if not defined MSVCDIR ( + echo ERROR: could not locate VC\Tools\MSVC + exit /b 1 +) + +for /f "delims=" %%D in ('dir /b /ad "%MSVCDIR%"') do ( + for /f "tokens=1,2 delims=." %%A in ("%%D") do ( + set "maj=%%A" & set "min=%%B" + if "!maj!"=="14" ( + if !min! LEQ 9 ( + set "toolsets_vc14=%%D" + ) else if !min! LEQ 19 ( + set "toolsets_vc15=%%D" + ) else if !min! LEQ 29 ( + set "toolsets_vs16=%%D" + ) else ( + set "toolsets_vs17=%%D" + ) + ) + ) +) + +set "KEY=%~1" +set "VAR=toolsets_%KEY%" +call set "RESULT=%%%VAR%%%" +if defined RESULT ( + echo %RESULT% + exit /b 0 +) else ( + echo ERROR: no toolset found for %KEY% + exit /b 1 +) diff --git a/.github/scripts/windows/test.bat b/.github/scripts/windows/test.bat index 510e9bc78f4ed..7ef60534cc780 100644 --- a/.github/scripts/windows/test.bat +++ b/.github/scripts/windows/test.bat @@ -11,7 +11,8 @@ if not exist "%SDK_RUNNER%" ( exit /b 3 ) -cmd /c %SDK_RUNNER% -t .github\scripts\windows\test_task.bat +for /f "delims=" %%T in ('call .github\scripts\windows\find-vs-toolset.bat %PHP_BUILD_CRT%') do set "VS_TOOLSET=%%T" +cmd /c %SDK_RUNNER% -s %VS_TOOLSET% -t .github\scripts\windows\test_task.bat if %errorlevel% neq 0 exit /b 3 exit /b 0 diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index 214798af62b5c..5353ef7d0ea44 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -147,7 +147,7 @@ jobs: WINDOWS: if: github.repository == 'php/php-src' || github.event_name == 'pull_request' name: WINDOWS_X64_ZTS - runs-on: windows-2019 + runs-on: windows-2022 env: PHP_BUILD_CACHE_BASE_DIR: C:\build-cache PHP_BUILD_OBJ_DIR: C:\obj diff --git a/.github/workflows/root.yml b/.github/workflows/root.yml index a98bb39ba0d92..78e0d47aa1d15 100644 --- a/.github/workflows/root.yml +++ b/.github/workflows/root.yml @@ -58,7 +58,7 @@ jobs: ubuntu_version: ${{ (((matrix.branch.version[0] == 8 && matrix.branch.version[1] >= 5) || matrix.branch.version[0] >= 9) && '24.04') || '22.04' }} - windows_version: ${{ ((matrix.branch.version[0] == 8 && matrix.branch.version[1] >= 4) || matrix.branch.version[0] >= 9) && '2022' || '2019' }} + windows_version: '2022' skip_laravel: ${{ matrix.branch.version[0] == 8 && matrix.branch.version[1] == 1 }} skip_symfony: ${{ matrix.branch.version[0] == 8 && matrix.branch.version[1] == 1 }} skip_wordpress: ${{ matrix.branch.version[0] == 8 && matrix.branch.version[1] == 1 }} diff --git a/Zend/tests/bug70258.phpt b/Zend/tests/bug70258.phpt index 40915a286ef9e..d346dbdf3a35b 100644 --- a/Zend/tests/bug70258.phpt +++ b/Zend/tests/bug70258.phpt @@ -4,6 +4,9 @@ Bug #70258 (Segfault if do_resize fails to allocated memory) memory_limit=2M --SKIPIF-- --INI-- diff --git a/Zend/tests/gh11189_1.phpt b/Zend/tests/gh11189_1.phpt index 53727908e5e2a..17b9967bc3182 100644 --- a/Zend/tests/gh11189_1.phpt +++ b/Zend/tests/gh11189_1.phpt @@ -2,6 +2,9 @@ GH-11189: Exceeding memory limit in zend_hash_do_resize leaves the array in an invalid state (not packed array) --SKIPIF-- --INI-- From 2965fb843d732ff014000c8c293d515d17684bd5 Mon Sep 17 00:00:00 2001 From: David CARLIER Date: Tue, 24 Jun 2025 21:55:00 +0100 Subject: [PATCH 170/682] ext/pcntl: following up #15921 rework SYS_pidfd_open configure detection. (#18931) --- ext/pcntl/config.m4 | 4 +++- ext/pcntl/pcntl.c | 16 +++++++++------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/ext/pcntl/config.m4 b/ext/pcntl/config.m4 index 3efa8a53cad1e..cfe6e80ca1103 100644 --- a/ext/pcntl/config.m4 +++ b/ext/pcntl/config.m4 @@ -13,7 +13,6 @@ if test "$PHP_PCNTL" != "no"; then forkx getcpuid getpriority - pidfd_open pset_bind pthread_set_qos_class_self_np rfork @@ -48,6 +47,9 @@ if test "$PHP_PCNTL" != "no"; then AC_CHECK_DECLS([SYS_waitid],,, [#include ]) + AC_CHECK_DECLS([SYS_pidfd_open],,, + [#include ]) + dnl if unsupported, -1 means automatically ENOSYS in this context AC_CACHE_CHECK([if sched_getcpu is supported], [php_cv_func_sched_getcpu], [AC_RUN_IFELSE([AC_LANG_SOURCE([ diff --git a/ext/pcntl/pcntl.c b/ext/pcntl/pcntl.c index 1f061870ed453..886d4b6b595ff 100644 --- a/ext/pcntl/pcntl.c +++ b/ext/pcntl/pcntl.c @@ -125,18 +125,20 @@ typedef psetid_t cpu_set_t; #include #endif -#if defined(__linux__) && defined(HAVE_DECL_SYS_WAITID) && HAVE_DECL_SYS_WAITID == 1 && defined(HAVE_SYSCALL) -#define HAVE_LINUX_RAW_SYSCALL_WAITID 1 +#if defined(__linux__) && defined(HAVE_SYSCALL) +# include +# if defined(HAVE_DECL_SYS_WAITID) && HAVE_DECL_SYS_WAITID == 1 +# define HAVE_LINUX_RAW_SYSCALL_WAITID 1 +# endif +# if defined(HAVE_DECL_SYS_PIDFD_OPEN) && HAVE_DECL_SYS_PIDFD_OPEN == 1 +# define HAVE_LINUX_RAW_SYSCALL_PIDFD_OPEN 1 +# endif #endif #if defined(HAVE_LINUX_RAW_SYSCALL_WAITID) #include #endif -#if defined(HAVE_PIDFD_OPEN) || defined(HAVE_LINUX_RAW_SYSCALL_WAITID) -#include -#endif - #ifdef HAVE_FORKX #include #endif @@ -1607,7 +1609,7 @@ PHP_FUNCTION(pcntl_forkx) #endif /* }}} */ -#ifdef HAVE_PIDFD_OPEN +#ifdef HAVE_LINUX_RAW_SYSCALL_PIDFD_OPEN // The `pidfd_open` syscall is available since 5.3 // and `setns` since 3.0. PHP_FUNCTION(pcntl_setns) From 359a21f102b5ca9e517253281228da68a053b3ae Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Tue, 24 Jun 2025 18:55:11 +0200 Subject: [PATCH 171/682] Fix RCN violations in array functions When the array functions perform their operation in-place, the `@refcount 1` annotation is wrong and causes a failure under `ZEND_VERIFY_FUNC_INFO`. The test file tests all functions that have the in-place optimization, even those that didn't have the refcount annotation, just to prevent future regressions. Closes GH-18929. --- NEWS | 1 + Zend/Optimizer/zend_func_infos.h | 7 --- ext/standard/basic_functions.stub.php | 7 --- ext/standard/basic_functions_arginfo.h | 2 +- ext/standard/tests/array/rcn_in_place.phpt | 57 ++++++++++++++++++++++ 5 files changed, 59 insertions(+), 15 deletions(-) create mode 100644 ext/standard/tests/array/rcn_in_place.phpt diff --git a/NEWS b/NEWS index 34866abfb21d8..624bcda9bdf44 100644 --- a/NEWS +++ b/NEWS @@ -28,6 +28,7 @@ PHP NEWS - Standard: . Fix misleading errors in printf(). (nielsdos) + . Fix RCN violations in array functions. (nielsdos) - Streams: . Fixed GH-13264 (fgets() and stream_get_line() do not return false on filter diff --git a/Zend/Optimizer/zend_func_infos.h b/Zend/Optimizer/zend_func_infos.h index 125212d2292e6..c9229d638f433 100644 --- a/Zend/Optimizer/zend_func_infos.h +++ b/Zend/Optimizer/zend_func_infos.h @@ -450,8 +450,6 @@ static const func_info_t func_infos[] = { F1("compact", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_STRING|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF), FN("array_fill", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_OF_ANY), F1("array_fill_keys", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_KEY_STRING|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF), - F1("array_replace", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_KEY_STRING|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF), - F1("array_replace_recursive", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_KEY_STRING|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF), FN("array_keys", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_OF_LONG|MAY_BE_ARRAY_OF_STRING), FN("array_values", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF), F1("array_count_values", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_KEY_STRING|MAY_BE_ARRAY_OF_LONG), @@ -460,13 +458,8 @@ static const func_info_t func_infos[] = { F1("array_flip", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_KEY_STRING|MAY_BE_ARRAY_OF_LONG|MAY_BE_ARRAY_OF_STRING), F1("array_change_key_case", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_KEY_STRING|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF), F1("array_intersect_key", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_KEY_STRING|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF), - F1("array_intersect_ukey", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_KEY_STRING|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF), - F1("array_intersect", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_KEY_STRING|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF), - F1("array_uintersect", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_KEY_STRING|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF), F1("array_intersect_assoc", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_KEY_STRING|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF), F1("array_uintersect_assoc", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_KEY_STRING|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF), - F1("array_intersect_uassoc", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_KEY_STRING|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF), - F1("array_uintersect_uassoc", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_KEY_STRING|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF), F1("array_diff_key", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_KEY_STRING|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF), F1("array_diff_ukey", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_KEY_STRING|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF), F1("array_udiff", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_KEY_STRING|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF), diff --git a/ext/standard/basic_functions.stub.php b/ext/standard/basic_functions.stub.php index 3bdc3827b4d36..0bbdeeb72a2c3 100755 --- a/ext/standard/basic_functions.stub.php +++ b/ext/standard/basic_functions.stub.php @@ -1678,13 +1678,11 @@ function array_merge_recursive(array ...$arrays): array {} /** * @compile-time-eval - * @refcount 1 */ function array_replace(array $array, array ...$replacements): array {} /** * @compile-time-eval - * @refcount 1 */ function array_replace_recursive(array $array, array ...$replacements): array {} @@ -1757,19 +1755,16 @@ function array_intersect_key(array $array, array ...$arrays): array {} /** * @param array|callable $rest - * @refcount 1 */ function array_intersect_ukey(array $array, ...$rest): array {} /** * @compile-time-eval - * @refcount 1 */ function array_intersect(array $array, array ...$arrays): array {} /** * @param array|callable $rest - * @refcount 1 */ function array_uintersect(array $array, ...$rest): array {} @@ -1787,13 +1782,11 @@ function array_uintersect_assoc(array $array, ...$rest): array {} /** * @param array|callable $rest - * @refcount 1 */ function array_intersect_uassoc(array $array, ...$rest): array {} /** * @param array|callable $rest - * @refcount 1 */ function array_uintersect_uassoc(array $array, ...$rest): array {} diff --git a/ext/standard/basic_functions_arginfo.h b/ext/standard/basic_functions_arginfo.h index 1361cf6062797..23ee75fe18dc3 100644 --- a/ext/standard/basic_functions_arginfo.h +++ b/ext/standard/basic_functions_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 2a3d8da0b92134dcca74f2ac70454bd27768f20e */ + * Stub hash: 60960e59f6310521a958b6fb0917650854d19612 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_set_time_limit, 0, 1, _IS_BOOL, 0) ZEND_ARG_TYPE_INFO(0, seconds, IS_LONG, 0) diff --git a/ext/standard/tests/array/rcn_in_place.phpt b/ext/standard/tests/array/rcn_in_place.phpt new file mode 100644 index 0000000000000..e6a7b5b6d695f --- /dev/null +++ b/ext/standard/tests/array/rcn_in_place.phpt @@ -0,0 +1,57 @@ +--TEST-- +RCN check for in-place array modifications +--FILE-- + 0)); +var_dump(array_intersect(range(0, 1), [])); +var_dump(array_uintersect(range(0, 1), [], fn () => 0)); +var_dump(array_intersect_uassoc(range(0, 1), [], fn () => 0)); +var_dump(array_uintersect_uassoc(range(0, 1), [], fn () => 0, fn () => 0)); +?> +--EXPECT-- +array(2) { + [0]=> + int(0) + [1]=> + int(1) +} +array(2) { + [0]=> + int(0) + [1]=> + int(1) +} +array(2) { + [0]=> + int(0) + [1]=> + int(1) +} +array(2) { + [0]=> + int(0) + [1]=> + int(1) +} +array(2) { + [0]=> + int(0) + [1]=> + int(1) +} +array(0) { +} +array(0) { +} +array(0) { +} +array(0) { +} +array(0) { +} From 9cb3d8d200f0c822b17bda35a2a67a97b039d3e1 Mon Sep 17 00:00:00 2001 From: Ahmed Lekssays Date: Tue, 3 Jun 2025 09:00:55 +0000 Subject: [PATCH 172/682] Fix GHSA-453j-q27h-5p8x Libxml versions prior to 2.13 cannot correctly handle a call to xmlNodeSetName() with a name longer than 2G. It will leave the node object in an invalid state with a NULL name. This later causes a NULL pointer dereference when using the name during message serialization. To solve this, implement a workaround that resets the name to the sentinel name if this situation arises. Versions of libxml of 2.13 and higher are not affected. This can be exploited if a SoapVar is created with a fully qualified name that is longer than 2G. This would be possible if some application code uses a namespace prefix from an untrusted source like from a remote SOAP service. Co-authored-by: Niels Dossche <7771979+nielsdos@users.noreply.github.com> --- ext/soap/soap.c | 6 ++-- ext/soap/tests/soap_qname_crash.phpt | 48 ++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 2 deletions(-) create mode 100644 ext/soap/tests/soap_qname_crash.phpt diff --git a/ext/soap/soap.c b/ext/soap/soap.c index fbf6546beb824..3bc713ca76bd8 100644 --- a/ext/soap/soap.c +++ b/ext/soap/soap.c @@ -4019,8 +4019,10 @@ static xmlNodePtr serialize_zval(zval *val, sdlParamPtr param, char *paramName, } xmlParam = master_to_xml(enc, val, style, parent); zval_ptr_dtor(&defval); - if (!strcmp((char*)xmlParam->name, "BOGUS")) { - xmlNodeSetName(xmlParam, BAD_CAST(paramName)); + if (xmlParam != NULL) { + if (xmlParam->name == NULL || strcmp((char*)xmlParam->name, "BOGUS") == 0) { + xmlNodeSetName(xmlParam, BAD_CAST(paramName)); + } } return xmlParam; } diff --git a/ext/soap/tests/soap_qname_crash.phpt b/ext/soap/tests/soap_qname_crash.phpt new file mode 100644 index 0000000000000..bcf01d574fab4 --- /dev/null +++ b/ext/soap/tests/soap_qname_crash.phpt @@ -0,0 +1,48 @@ +--TEST-- +Test SoapClient with excessively large QName prefix in SoapVar +--EXTENSIONS-- +soap +--SKIPIF-- + +--INI-- +memory_limit=6144M +--FILE-- + 'http://127.0.0.1/', + 'uri' => 'urn:dummy', + 'trace' => 1, + 'exceptions' => true, +]; +$client = new TestSoapClient(null, $options); +$client->__soapCall("DummyFunction", [$var]); +?> +--EXPECT-- +Attempting to create SoapVar with very large QName +Attempting encoding + +value From ea6a7a97255b939539cf92d43ce4c5c8b24b3199 Mon Sep 17 00:00:00 2001 From: Shivam Mathur Date: Wed, 25 Jun 2025 03:15:10 +0530 Subject: [PATCH 173/682] Fix CI for windows-2022 This is a continuation of GH-18927 to fix CI for windows-2022 --- Zend/tests/bug40770.phpt | 3 +++ Zend/tests/gh12073.phpt | 3 +++ tests/basic/timeout_variation_0.phpt | 3 +++ tests/basic/timeout_variation_7.phpt | 3 +++ tests/func/005a.phpt | 3 +++ tests/lang/bug45392.phpt | 3 +++ 6 files changed, 18 insertions(+) diff --git a/Zend/tests/bug40770.phpt b/Zend/tests/bug40770.phpt index f37d96d5ff333..bdbae4cf8f1a2 100644 --- a/Zend/tests/bug40770.phpt +++ b/Zend/tests/bug40770.phpt @@ -4,6 +4,9 @@ Bug #40770 (Apache child exits when PHP memory limit reached) memory_limit=8M --SKIPIF-- --FILE-- diff --git a/tests/basic/timeout_variation_7.phpt b/tests/basic/timeout_variation_7.phpt index 0401240ba953d..3d40b540677db 100644 --- a/tests/basic/timeout_variation_7.phpt +++ b/tests/basic/timeout_variation_7.phpt @@ -2,6 +2,9 @@ Timeout within for loop --SKIPIF-- --FILE-- diff --git a/tests/func/005a.phpt b/tests/func/005a.phpt index cf1e5713770a9..2f527d773adbe 100644 --- a/tests/func/005a.phpt +++ b/tests/func/005a.phpt @@ -2,6 +2,9 @@ Testing register_shutdown_function() with timeout. (Bug: #21513) --SKIPIF-- --FILE-- diff --git a/tests/lang/bug45392.phpt b/tests/lang/bug45392.phpt index 692fa0cdcf6fd..1a01bac3261a9 100644 --- a/tests/lang/bug45392.phpt +++ b/tests/lang/bug45392.phpt @@ -2,6 +2,9 @@ Bug #45392 (ob_start()/ob_end_clean() and memory_limit) --SKIPIF-- Date: Wed, 25 Jun 2025 03:15:10 +0530 Subject: [PATCH 174/682] Fix CI for windows-2022 This is a continuation of GH-18927 to fix CI for windows-2022 --- Zend/tests/bug40770.phpt | 3 +++ Zend/tests/gh12073.phpt | 3 +++ Zend/tests/traits/bugs/gh13177.phpt | 6 ++++++ tests/basic/timeout_variation_0.phpt | 3 +++ tests/basic/timeout_variation_7.phpt | 3 +++ tests/func/005a.phpt | 3 +++ tests/lang/bug45392.phpt | 3 +++ 7 files changed, 24 insertions(+) diff --git a/Zend/tests/bug40770.phpt b/Zend/tests/bug40770.phpt index f37d96d5ff333..bdbae4cf8f1a2 100644 --- a/Zend/tests/bug40770.phpt +++ b/Zend/tests/bug40770.phpt @@ -4,6 +4,9 @@ Bug #40770 (Apache child exits when PHP memory limit reached) memory_limit=8M --SKIPIF-- --FILE-- --FILE-- diff --git a/tests/basic/timeout_variation_7.phpt b/tests/basic/timeout_variation_7.phpt index 0401240ba953d..3d40b540677db 100644 --- a/tests/basic/timeout_variation_7.phpt +++ b/tests/basic/timeout_variation_7.phpt @@ -2,6 +2,9 @@ Timeout within for loop --SKIPIF-- --FILE-- diff --git a/tests/func/005a.phpt b/tests/func/005a.phpt index cf1e5713770a9..2f527d773adbe 100644 --- a/tests/func/005a.phpt +++ b/tests/func/005a.phpt @@ -2,6 +2,9 @@ Testing register_shutdown_function() with timeout. (Bug: #21513) --SKIPIF-- --FILE-- diff --git a/tests/lang/bug45392.phpt b/tests/lang/bug45392.phpt index 692fa0cdcf6fd..1a01bac3261a9 100644 --- a/tests/lang/bug45392.phpt +++ b/tests/lang/bug45392.phpt @@ -2,6 +2,9 @@ Bug #45392 (ob_start()/ob_end_clean() and memory_limit) --SKIPIF-- Date: Wed, 25 Jun 2025 18:36:20 +0900 Subject: [PATCH 175/682] Allowed the use of formats like `@param array<>` (#18924) --- build/gen_stub.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/gen_stub.php b/build/gen_stub.php index d9327b67d042f..a3dd14a562346 100755 --- a/build/gen_stub.php +++ b/build/gen_stub.php @@ -4533,7 +4533,7 @@ public function getVariableName(): string { if ($this->name === "param") { // Allow for parsing extended types like callable(string):mixed in docblocks - preg_match('/^\s*(?[\w\|\\\\]+(?\((?(?:(?&parens)|[^(){}[\]]*+))++\)|\{(?&inparens)\}|\[(?&inparens)\])*+(?::(?&type))?)\s*\$(?\w+).*$/', $value, $matches); + preg_match('/^\s*(?[\w\|\\\\]+(?\((?(?:(?&parens)|[^(){}[\]<>]*+))++\)|\{(?&inparens)\}|\[(?&inparens)\]|<(?&inparens)>)*+(?::(?&type))?)\s*\$(?\w+).*$/', $value, $matches); } elseif ($this->name === "prefer-ref") { preg_match('/^\s*\$(?\w+).*$/', $value, $matches); } From 7b33b1c916a3da1c1d00c1c65fa0cf12a80d2a34 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Thu, 26 Jun 2025 11:24:54 +0200 Subject: [PATCH 176/682] Update NEWS with entries for security fixes --- NEWS | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 29400e6ef5b5c..8c8b28fb98186 100644 --- a/NEWS +++ b/NEWS @@ -1,8 +1,18 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -?? ??? ????, PHP 8.1.33 +03 Jul 2025, PHP 8.1.33 +- PGSQL: + . Fixed GHSA-hrwm-9436-5mv3 (pgsql extension does not check for errors during + escaping). (CVE-2025-1735) (Jakub Zelenka) + +- SOAP: + . Fixed GHSA-453j-q27h-5p8x (NULL Pointer Dereference in PHP SOAP Extension + via Large XML Namespace Prefix). (CVE-2025-6491) (Lekssays, nielsdos) +- Standard: + . Fixed GHSA-3cr5-j632-f35r (Null byte termination in hostnames). + (CVE-2025-1220) (Jakub Zelenka) 13 Mar 2025, PHP 8.1.32 From e98879f19c1a7c7808eac656b0a4f984a2cc284c Mon Sep 17 00:00:00 2001 From: DanielEScherzer Date: Thu, 26 Jun 2025 10:52:33 -0700 Subject: [PATCH 177/682] main.c: fix a typo, add some capitalization [skip ci] (#18905) --- main/main.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/main/main.c b/main/main.c index 18c8e2dfac7ec..3518e4137ecef 100644 --- a/main/main.c +++ b/main/main.c @@ -1317,10 +1317,10 @@ static ZEND_COLD void php_error_cb(int orig_type, zend_string *error_filename, c case E_CORE_WARNING: case E_COMPILE_WARNING: case E_USER_WARNING: - /* throw an exception if we are in EH_THROW mode and the type is warning. - * fatal errors are real errors and cannot be made exceptions. - * exclude deprecated for the sake of BC to old damaged code. - * notices are no errors and are not treated as such like E_WARNINGS. + /* Throw an exception if we are in EH_THROW mode and the type is warning. + * Fatal errors are real errors and cannot be made exceptions. + * Exclude deprecated for the sake of BC to old damaged code. + * Notices are not errors and are not treated as such like E_WARNINGS. * DO NOT overwrite a pending exception. */ if (!EG(exception)) { From 171501b93f84bbb9038e59c444b8cd3e50a6729f Mon Sep 17 00:00:00 2001 From: DanielEScherzer Date: Thu, 26 Jun 2025 11:27:15 -0700 Subject: [PATCH 178/682] Replace `@deprecated` with `#[\Deprecated]` for internal constants (#18780) Only covers constants declared via stub files, others will be handled separately in a later commit. Does not include the intl extension, since that had some errors relating to the cpp code; that extension will be updated separately. --- ext/curl/curl.stub.php | 2 +- ext/curl/curl_arginfo.h | 16 +- ext/curl/interface.c | 1 + ext/curl/tests/bug46711.phpt | 2 +- ext/date/php_date.stub.php | 6 +- ext/date/php_date_arginfo.h | 41 ++- .../tests/date_sunrise_and_sunset_basic.phpt | 4 +- ext/date/tests/gh14732.phpt | 4 +- ext/date/tests/gh16454.phpt | 8 +- ext/date/tests/gh18481.phpt | 8 +- ext/dom/php_dom.c | 1 + ext/dom/php_dom.stub.php | 2 +- ext/dom/php_dom_arginfo.h | 16 +- ext/dom/tests/DOM_PHP_ERR_deprecated.phpt | 2 +- ext/enchant/enchant.stub.php | 4 +- ext/enchant/enchant_arginfo.h | 28 ++- ext/filter/filter.c | 1 + ext/filter/filter.stub.php | 4 +- ext/filter/filter_arginfo.h | 29 ++- ext/filter/tests/025.phpt | 14 +- ext/filter/tests/026.phpt | 18 +- ext/filter/tests/042.phpt | 4 +- ext/filter/tests/bug69203.phpt | 2 +- ext/mysqli/mysqli.stub.php | 36 +-- ext/mysqli/mysqli_arginfo.h | 236 +++++++++++++++++- ext/mysqli/tests/deprecated_constants.phpt | 14 +- ext/pgsql/pgsql.stub.php | 2 +- ext/pgsql/pgsql_arginfo.h | 15 +- ext/random/random.stub.php | 2 +- ext/random/random_arginfo.h | 15 +- .../01_functions/array_rand_mt_rand_php.phpt | 2 +- ext/random/tests/01_functions/bug75514.phpt | 2 +- .../tests/01_functions/mt_rand_value.phpt | 2 +- .../03_randomizer/compatibility_mt_rand.phpt | 4 +- .../tests/03_randomizer/methods/getBytes.phpt | 2 +- .../methods/getBytesFromString.phpt | 2 +- .../tests/03_randomizer/methods/getFloat.phpt | 2 +- .../tests/03_randomizer/methods/getInt.phpt | 2 +- .../03_randomizer/methods/nextFloat.phpt | 2 +- .../tests/03_randomizer/methods/nextInt.phpt | 2 +- .../03_randomizer/methods/pickArrayKeys.phpt | 2 +- .../03_randomizer/methods/shuffleArray.phpt | 2 +- .../03_randomizer/methods/shuffleBytes.phpt | 2 +- ext/random/tests/03_randomizer/serialize.phpt | 2 +- .../check_all.phpt | 4 +- ext/soap/soap.c | 1 + ext/soap/soap.stub.php | 2 +- ext/soap/soap_arginfo.h | 16 +- ext/soap/tests/server003.phpt | 2 +- ext/standard/basic_functions.stub.php | 10 +- ext/standard/basic_functions_arginfo.h | 67 ++++- ext/standard/file.c | 1 + ext/standard/file.stub.php | 4 +- ext/standard/file_arginfo.h | 29 ++- ext/standard/tests/assert/assert.phpt | 10 +- ext/standard/tests/assert/assert03.phpt | 6 +- ext/standard/tests/assert/assert04.phpt | 6 +- ext/standard/tests/assert/assert_basic2.phpt | 6 +- ext/standard/tests/assert/assert_basic3.phpt | 2 +- ext/standard/tests/assert/assert_basic4.phpt | 8 +- ext/standard/tests/assert/assert_basic5.phpt | 4 +- ext/standard/tests/assert/assert_basic6.phpt | 8 +- .../tests/assert/assert_closures.phpt | 2 +- .../assert/assert_closures_multiple.phpt | 2 +- ext/standard/tests/assert/assert_error2.phpt | 2 +- .../tests/assert/assert_variation.phpt | 24 +- ext/standard/tests/assert/bug80290.phpt | 2 +- .../file/file_binary_text_deprecated.phpt | 4 +- 68 files changed, 640 insertions(+), 149 deletions(-) diff --git a/ext/curl/curl.stub.php b/ext/curl/curl.stub.php index cbcb52bc7b0a2..00532f45793f5 100644 --- a/ext/curl/curl.stub.php +++ b/ext/curl/curl.stub.php @@ -13,9 +13,9 @@ const CURLOPT_AUTOREFERER = UNKNOWN; /** * @var int - * @deprecated has no effect since 5.1.2 * @cvalue CURLOPT_BINARYTRANSFER */ +#[\Deprecated(since: '8.4', message: 'as it had no effect since 5.1.2')] const CURLOPT_BINARYTRANSFER = UNKNOWN; /** * @var int diff --git a/ext/curl/curl_arginfo.h b/ext/curl/curl_arginfo.h index ef267cd803ba2..cff18b2916859 100644 --- a/ext/curl/curl_arginfo.h +++ b/ext/curl/curl_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 792cdfa8a8ce190d73dffe679c51a41a2ee46cd7 */ + * Stub hash: c087ac501d0abe14ed87968023d837f358e6fee8 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_curl_close, 0, 1, IS_VOID, 0) ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0) @@ -973,6 +973,20 @@ static void register_curl_symbols(int module_number) REGISTER_LONG_CONSTANT("CURL_HTTP_VERSION_3ONLY", CURL_HTTP_VERSION_3ONLY, CONST_PERSISTENT); #endif REGISTER_LONG_CONSTANT("CURLOPT_SAFE_UPLOAD", CURLOPT_SAFE_UPLOAD, CONST_PERSISTENT); + + zend_constant *const_CURLOPT_BINARYTRANSFER = zend_hash_str_find_ptr(EG(zend_constants), "CURLOPT_BINARYTRANSFER", sizeof("CURLOPT_BINARYTRANSFER") - 1); + + zend_attribute *attribute_Deprecated_const_CURLOPT_BINARYTRANSFER_0 = zend_add_global_constant_attribute(const_CURLOPT_BINARYTRANSFER, ZSTR_KNOWN(ZEND_STR_DEPRECATED_CAPITALIZED), 2); + zval attribute_Deprecated_const_CURLOPT_BINARYTRANSFER_0_arg0; + zend_string *attribute_Deprecated_const_CURLOPT_BINARYTRANSFER_0_arg0_str = zend_string_init("8.4", strlen("8.4"), 1); + ZVAL_STR(&attribute_Deprecated_const_CURLOPT_BINARYTRANSFER_0_arg0, attribute_Deprecated_const_CURLOPT_BINARYTRANSFER_0_arg0_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_CURLOPT_BINARYTRANSFER_0->args[0].value, &attribute_Deprecated_const_CURLOPT_BINARYTRANSFER_0_arg0); + attribute_Deprecated_const_CURLOPT_BINARYTRANSFER_0->args[0].name = ZSTR_KNOWN(ZEND_STR_SINCE); + zval attribute_Deprecated_const_CURLOPT_BINARYTRANSFER_0_arg1; + zend_string *attribute_Deprecated_const_CURLOPT_BINARYTRANSFER_0_arg1_str = zend_string_init("as it had no effect since 5.1.2", strlen("as it had no effect since 5.1.2"), 1); + ZVAL_STR(&attribute_Deprecated_const_CURLOPT_BINARYTRANSFER_0_arg1, attribute_Deprecated_const_CURLOPT_BINARYTRANSFER_0_arg1_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_CURLOPT_BINARYTRANSFER_0->args[1].value, &attribute_Deprecated_const_CURLOPT_BINARYTRANSFER_0_arg1); + attribute_Deprecated_const_CURLOPT_BINARYTRANSFER_0->args[1].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); } static zend_class_entry *register_class_CurlHandle(void) diff --git a/ext/curl/interface.c b/ext/curl/interface.c index 710f98a13fbb0..3470ad2f72327 100644 --- a/ext/curl/interface.c +++ b/ext/curl/interface.c @@ -65,6 +65,7 @@ # pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif +#include "zend_attributes.h" #include "curl_arginfo.h" ZEND_DECLARE_MODULE_GLOBALS(curl) diff --git a/ext/curl/tests/bug46711.phpt b/ext/curl/tests/bug46711.phpt index a0b211a7876ab..0540a79bca5b3 100644 --- a/ext/curl/tests/bug46711.phpt +++ b/ext/curl/tests/bug46711.phpt @@ -21,7 +21,7 @@ var_dump($opt); // with this bug, $opt[58] becomes NULL ?> --EXPECTF-- -Deprecated: Constant CURLOPT_BINARYTRANSFER is deprecated in %s on line %d +Deprecated: Constant CURLOPT_BINARYTRANSFER is deprecated since 8.4, as it had no effect since 5.1.2 in %s on line %d array(2) { [58]=> bool(true) diff --git a/ext/date/php_date.stub.php b/ext/date/php_date.stub.php index 7f60be40b1bc8..4f77fc9223e27 100644 --- a/ext/date/php_date.stub.php +++ b/ext/date/php_date.stub.php @@ -83,22 +83,22 @@ /** * @var int * @cvalue SUNFUNCS_RET_TIMESTAMP - * @deprecated */ +#[\Deprecated(since: '8.4', message: 'as date_sunrise() and date_sunset() were deprecated in 8.1')] const SUNFUNCS_RET_TIMESTAMP = UNKNOWN; /** * @var int * @cvalue SUNFUNCS_RET_STRING - * @deprecated */ +#[\Deprecated(since: '8.4', message: 'as date_sunrise() and date_sunset() were deprecated in 8.1')] const SUNFUNCS_RET_STRING = UNKNOWN; /** * @var int * @cvalue SUNFUNCS_RET_DOUBLE - * @deprecated */ +#[\Deprecated(since: '8.4', message: 'as date_sunrise() and date_sunset() were deprecated in 8.1')] const SUNFUNCS_RET_DOUBLE = UNKNOWN; function strtotime(string $datetime, ?int $baseTimestamp = null): int|false {} diff --git a/ext/date/php_date_arginfo.h b/ext/date/php_date_arginfo.h index 2a0fd6e5ac009..0b249c738f715 100644 --- a/ext/date/php_date_arginfo.h +++ b/ext/date/php_date_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 4e61617ca7c877aa3811d674d47850f23157074b */ + * Stub hash: c9dba59a68085579d18948963a979d63eecff204 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_strtotime, 0, 1, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, datetime, IS_STRING, 0) @@ -860,6 +860,45 @@ static void register_php_date_symbols(int module_number) ZVAL_STR(&attribute_Deprecated_func_date_sunset_0_arg1, attribute_Deprecated_func_date_sunset_0_arg1_str); ZVAL_COPY_VALUE(&attribute_Deprecated_func_date_sunset_0->args[1].value, &attribute_Deprecated_func_date_sunset_0_arg1); attribute_Deprecated_func_date_sunset_0->args[1].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); + zend_constant *const_SUNFUNCS_RET_TIMESTAMP = zend_hash_str_find_ptr(EG(zend_constants), "SUNFUNCS_RET_TIMESTAMP", sizeof("SUNFUNCS_RET_TIMESTAMP") - 1); + + zend_attribute *attribute_Deprecated_const_SUNFUNCS_RET_TIMESTAMP_0 = zend_add_global_constant_attribute(const_SUNFUNCS_RET_TIMESTAMP, ZSTR_KNOWN(ZEND_STR_DEPRECATED_CAPITALIZED), 2); + zval attribute_Deprecated_const_SUNFUNCS_RET_TIMESTAMP_0_arg0; + zend_string *attribute_Deprecated_const_SUNFUNCS_RET_TIMESTAMP_0_arg0_str = zend_string_init("8.4", strlen("8.4"), 1); + ZVAL_STR(&attribute_Deprecated_const_SUNFUNCS_RET_TIMESTAMP_0_arg0, attribute_Deprecated_const_SUNFUNCS_RET_TIMESTAMP_0_arg0_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_SUNFUNCS_RET_TIMESTAMP_0->args[0].value, &attribute_Deprecated_const_SUNFUNCS_RET_TIMESTAMP_0_arg0); + attribute_Deprecated_const_SUNFUNCS_RET_TIMESTAMP_0->args[0].name = ZSTR_KNOWN(ZEND_STR_SINCE); + zval attribute_Deprecated_const_SUNFUNCS_RET_TIMESTAMP_0_arg1; + zend_string *attribute_Deprecated_const_SUNFUNCS_RET_TIMESTAMP_0_arg1_str = zend_string_init("as date_sunrise() and date_sunset() were deprecated in 8.1", strlen("as date_sunrise() and date_sunset() were deprecated in 8.1"), 1); + ZVAL_STR(&attribute_Deprecated_const_SUNFUNCS_RET_TIMESTAMP_0_arg1, attribute_Deprecated_const_SUNFUNCS_RET_TIMESTAMP_0_arg1_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_SUNFUNCS_RET_TIMESTAMP_0->args[1].value, &attribute_Deprecated_const_SUNFUNCS_RET_TIMESTAMP_0_arg1); + attribute_Deprecated_const_SUNFUNCS_RET_TIMESTAMP_0->args[1].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); + zend_constant *const_SUNFUNCS_RET_STRING = zend_hash_str_find_ptr(EG(zend_constants), "SUNFUNCS_RET_STRING", sizeof("SUNFUNCS_RET_STRING") - 1); + + zend_attribute *attribute_Deprecated_const_SUNFUNCS_RET_STRING_0 = zend_add_global_constant_attribute(const_SUNFUNCS_RET_STRING, ZSTR_KNOWN(ZEND_STR_DEPRECATED_CAPITALIZED), 2); + zval attribute_Deprecated_const_SUNFUNCS_RET_STRING_0_arg0; + zend_string *attribute_Deprecated_const_SUNFUNCS_RET_STRING_0_arg0_str = zend_string_init("8.4", strlen("8.4"), 1); + ZVAL_STR(&attribute_Deprecated_const_SUNFUNCS_RET_STRING_0_arg0, attribute_Deprecated_const_SUNFUNCS_RET_STRING_0_arg0_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_SUNFUNCS_RET_STRING_0->args[0].value, &attribute_Deprecated_const_SUNFUNCS_RET_STRING_0_arg0); + attribute_Deprecated_const_SUNFUNCS_RET_STRING_0->args[0].name = ZSTR_KNOWN(ZEND_STR_SINCE); + zval attribute_Deprecated_const_SUNFUNCS_RET_STRING_0_arg1; + zend_string *attribute_Deprecated_const_SUNFUNCS_RET_STRING_0_arg1_str = zend_string_init("as date_sunrise() and date_sunset() were deprecated in 8.1", strlen("as date_sunrise() and date_sunset() were deprecated in 8.1"), 1); + ZVAL_STR(&attribute_Deprecated_const_SUNFUNCS_RET_STRING_0_arg1, attribute_Deprecated_const_SUNFUNCS_RET_STRING_0_arg1_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_SUNFUNCS_RET_STRING_0->args[1].value, &attribute_Deprecated_const_SUNFUNCS_RET_STRING_0_arg1); + attribute_Deprecated_const_SUNFUNCS_RET_STRING_0->args[1].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); + zend_constant *const_SUNFUNCS_RET_DOUBLE = zend_hash_str_find_ptr(EG(zend_constants), "SUNFUNCS_RET_DOUBLE", sizeof("SUNFUNCS_RET_DOUBLE") - 1); + + zend_attribute *attribute_Deprecated_const_SUNFUNCS_RET_DOUBLE_0 = zend_add_global_constant_attribute(const_SUNFUNCS_RET_DOUBLE, ZSTR_KNOWN(ZEND_STR_DEPRECATED_CAPITALIZED), 2); + zval attribute_Deprecated_const_SUNFUNCS_RET_DOUBLE_0_arg0; + zend_string *attribute_Deprecated_const_SUNFUNCS_RET_DOUBLE_0_arg0_str = zend_string_init("8.4", strlen("8.4"), 1); + ZVAL_STR(&attribute_Deprecated_const_SUNFUNCS_RET_DOUBLE_0_arg0, attribute_Deprecated_const_SUNFUNCS_RET_DOUBLE_0_arg0_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_SUNFUNCS_RET_DOUBLE_0->args[0].value, &attribute_Deprecated_const_SUNFUNCS_RET_DOUBLE_0_arg0); + attribute_Deprecated_const_SUNFUNCS_RET_DOUBLE_0->args[0].name = ZSTR_KNOWN(ZEND_STR_SINCE); + zval attribute_Deprecated_const_SUNFUNCS_RET_DOUBLE_0_arg1; + zend_string *attribute_Deprecated_const_SUNFUNCS_RET_DOUBLE_0_arg1_str = zend_string_init("as date_sunrise() and date_sunset() were deprecated in 8.1", strlen("as date_sunrise() and date_sunset() were deprecated in 8.1"), 1); + ZVAL_STR(&attribute_Deprecated_const_SUNFUNCS_RET_DOUBLE_0_arg1, attribute_Deprecated_const_SUNFUNCS_RET_DOUBLE_0_arg1_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_SUNFUNCS_RET_DOUBLE_0->args[1].value, &attribute_Deprecated_const_SUNFUNCS_RET_DOUBLE_0_arg1); + attribute_Deprecated_const_SUNFUNCS_RET_DOUBLE_0->args[1].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); } static zend_class_entry *register_class_DateTimeInterface(void) diff --git a/ext/date/tests/date_sunrise_and_sunset_basic.phpt b/ext/date/tests/date_sunrise_and_sunset_basic.phpt index e5f5efa73e177..d2ed26dea2b6d 100644 --- a/ext/date/tests/date_sunrise_and_sunset_basic.phpt +++ b/ext/date/tests/date_sunrise_and_sunset_basic.phpt @@ -25,12 +25,12 @@ var_dump(gettype(date_sunset(time()))); --EXPECTF-- Basic test for date_sunrise() and date_sunset() -Deprecated: Constant SUNFUNCS_RET_STRING is deprecated in %s on line %d +Deprecated: Constant SUNFUNCS_RET_STRING is deprecated since 8.4, as date_sunrise() and date_sunset() were deprecated in 8.1 in %s on line %d Deprecated: Function date_sunrise() is deprecated since 8.1, use date_sun_info() instead in %s on line %d %s %s %d %d, sunrise time : %d:%d -Deprecated: Constant SUNFUNCS_RET_STRING is deprecated in %s on line %d +Deprecated: Constant SUNFUNCS_RET_STRING is deprecated since 8.4, as date_sunrise() and date_sunset() were deprecated in 8.1 in %s on line %d Deprecated: Function date_sunset() is deprecated since 8.1, use date_sun_info() instead in %s on line %d %s %s %d %d, sunset time : %d:%d diff --git a/ext/date/tests/gh14732.phpt b/ext/date/tests/gh14732.phpt index 63c626963a9d5..19b5f3b481f4b 100644 --- a/ext/date/tests/gh14732.phpt +++ b/ext/date/tests/gh14732.phpt @@ -31,12 +31,12 @@ date_sun_info(): Argument #2 ($latitude) must be finite date_sun_info(): Argument #3 ($longitude) must be finite date_sun_info(): Argument #3 ($longitude) must be finite -Deprecated: Constant SUNFUNCS_RET_STRING is deprecated in %s on line %d +Deprecated: Constant SUNFUNCS_RET_STRING is deprecated since 8.4, as date_sunrise() and date_sunset() were deprecated in 8.1 in %s on line %d Deprecated: Function date_sunset() is deprecated since 8.1, use date_sun_info() instead in %s on line %d bool(false) -Deprecated: Constant SUNFUNCS_RET_STRING is deprecated in %s on line %d +Deprecated: Constant SUNFUNCS_RET_STRING is deprecated since 8.4, as date_sunrise() and date_sunset() were deprecated in 8.1 in %s on line %d Deprecated: Function date_sunrise() is deprecated since 8.1, use date_sun_info() instead in %s on line %d bool(false) diff --git a/ext/date/tests/gh16454.phpt b/ext/date/tests/gh16454.phpt index e3a35a1ba1998..0acad74dce726 100644 --- a/ext/date/tests/gh16454.phpt +++ b/ext/date/tests/gh16454.phpt @@ -8,22 +8,22 @@ var_dump(date_sunset(0, SUNFUNCS_RET_STRING, 61, -150, 90, PHP_FLOAT_MAX)); var_dump(date_sunset(0, SUNFUNCS_RET_STRING, 61, -150, 90, -PHP_FLOAT_MAX)); ?> --EXPECTF-- -Deprecated: Constant SUNFUNCS_RET_STRING is deprecated in %s on line %d +Deprecated: Constant SUNFUNCS_RET_STRING is deprecated since 8.4, as date_sunrise() and date_sunset() were deprecated in 8.1 in %s on line %d Deprecated: Function date_sunrise() is deprecated since 8.1, use date_sun_info() instead in %s on line %d bool(false) -Deprecated: Constant SUNFUNCS_RET_STRING is deprecated in %s on line %d +Deprecated: Constant SUNFUNCS_RET_STRING is deprecated since 8.4, as date_sunrise() and date_sunset() were deprecated in 8.1 in %s on line %d Deprecated: Function date_sunrise() is deprecated since 8.1, use date_sun_info() instead in %s on line %d bool(false) -Deprecated: Constant SUNFUNCS_RET_STRING is deprecated in %s on line %d +Deprecated: Constant SUNFUNCS_RET_STRING is deprecated since 8.4, as date_sunrise() and date_sunset() were deprecated in 8.1 in %s on line %d Deprecated: Function date_sunset() is deprecated since 8.1, use date_sun_info() instead in %s on line %d bool(false) -Deprecated: Constant SUNFUNCS_RET_STRING is deprecated in %s on line %d +Deprecated: Constant SUNFUNCS_RET_STRING is deprecated since 8.4, as date_sunrise() and date_sunset() were deprecated in 8.1 in %s on line %d Deprecated: Function date_sunset() is deprecated since 8.1, use date_sun_info() instead in %s on line %d bool(false) diff --git a/ext/date/tests/gh18481.phpt b/ext/date/tests/gh18481.phpt index 074f244c3a3a3..8c997357e2750 100644 --- a/ext/date/tests/gh18481.phpt +++ b/ext/date/tests/gh18481.phpt @@ -8,22 +8,22 @@ foreach ([-NAN, NAN, INF, -INF] as $offset) { } ?> --EXPECTF-- -Deprecated: Constant SUNFUNCS_RET_STRING is deprecated in %s on line %d +Deprecated: Constant SUNFUNCS_RET_STRING is deprecated since 8.4, as date_sunrise() and date_sunset() were deprecated in 8.1 in %s on line %d Deprecated: Function date_sunrise() is deprecated since 8.1, use date_sun_info() instead in %s on line %d bool(false) -Deprecated: Constant SUNFUNCS_RET_STRING is deprecated in %s on line %d +Deprecated: Constant SUNFUNCS_RET_STRING is deprecated since 8.4, as date_sunrise() and date_sunset() were deprecated in 8.1 in %s on line %d Deprecated: Function date_sunrise() is deprecated since 8.1, use date_sun_info() instead in %s on line %d bool(false) -Deprecated: Constant SUNFUNCS_RET_STRING is deprecated in %s on line %d +Deprecated: Constant SUNFUNCS_RET_STRING is deprecated since 8.4, as date_sunrise() and date_sunset() were deprecated in 8.1 in %s on line %d Deprecated: Function date_sunrise() is deprecated since 8.1, use date_sun_info() instead in %s on line %d bool(false) -Deprecated: Constant SUNFUNCS_RET_STRING is deprecated in %s on line %d +Deprecated: Constant SUNFUNCS_RET_STRING is deprecated since 8.4, as date_sunrise() and date_sunset() were deprecated in 8.1 in %s on line %d Deprecated: Function date_sunrise() is deprecated since 8.1, use date_sun_info() instead in %s on line %d bool(false) diff --git a/ext/dom/php_dom.c b/ext/dom/php_dom.c index 6e85ea887e4ec..f1571ca78c4f2 100644 --- a/ext/dom/php_dom.c +++ b/ext/dom/php_dom.c @@ -23,6 +23,7 @@ #include "php.h" #if defined(HAVE_LIBXML) && defined(HAVE_DOM) #include "zend_enum.h" +#include "zend_attributes.h" #include "php_dom.h" #include "obj_map.h" #include "nodelist.h" diff --git a/ext/dom/php_dom.stub.php b/ext/dom/php_dom.stub.php index 43d26ec7a3c7d..686ebe87b71ca 100644 --- a/ext/dom/php_dom.stub.php +++ b/ext/dom/php_dom.stub.php @@ -147,9 +147,9 @@ /** * @var int - * @deprecated is no longer used since 8.4 * @cvalue PHP_ERR */ + #[\Deprecated(since: '8.4', message: 'as it is no longer used')] const DOM_PHP_ERR = UNKNOWN; /** * @var int diff --git a/ext/dom/php_dom_arginfo.h b/ext/dom/php_dom_arginfo.h index 5c21b909b0e18..cf73a85226e4f 100644 --- a/ext/dom/php_dom_arginfo.h +++ b/ext/dom/php_dom_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 0fcee2fa666dc88faf084578dde157409a6f5594 */ + * Stub hash: 1c8b81daeaf360b0ecab9ebbdf4f8865f521f43d */ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_dom_import_simplexml, 0, 1, DOMAttr|DOMElement, 0) ZEND_ARG_TYPE_INFO(0, node, IS_OBJECT, 0) @@ -1870,6 +1870,20 @@ static void register_php_dom_symbols(int module_number) REGISTER_LONG_CONSTANT("Dom\\NAMESPACE_ERR", NAMESPACE_ERR, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("Dom\\VALIDATION_ERR", VALIDATION_ERR, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("Dom\\HTML_NO_DEFAULT_NS", DOM_HTML_NO_DEFAULT_NS, CONST_PERSISTENT); + + zend_constant *const_DOM_PHP_ERR = zend_hash_str_find_ptr(EG(zend_constants), "DOM_PHP_ERR", sizeof("DOM_PHP_ERR") - 1); + + zend_attribute *attribute_Deprecated_const_DOM_PHP_ERR_0 = zend_add_global_constant_attribute(const_DOM_PHP_ERR, ZSTR_KNOWN(ZEND_STR_DEPRECATED_CAPITALIZED), 2); + zval attribute_Deprecated_const_DOM_PHP_ERR_0_arg0; + zend_string *attribute_Deprecated_const_DOM_PHP_ERR_0_arg0_str = zend_string_init("8.4", strlen("8.4"), 1); + ZVAL_STR(&attribute_Deprecated_const_DOM_PHP_ERR_0_arg0, attribute_Deprecated_const_DOM_PHP_ERR_0_arg0_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_DOM_PHP_ERR_0->args[0].value, &attribute_Deprecated_const_DOM_PHP_ERR_0_arg0); + attribute_Deprecated_const_DOM_PHP_ERR_0->args[0].name = ZSTR_KNOWN(ZEND_STR_SINCE); + zval attribute_Deprecated_const_DOM_PHP_ERR_0_arg1; + zend_string *attribute_Deprecated_const_DOM_PHP_ERR_0_arg1_str = zend_string_init("as it is no longer used", strlen("as it is no longer used"), 1); + ZVAL_STR(&attribute_Deprecated_const_DOM_PHP_ERR_0_arg1, attribute_Deprecated_const_DOM_PHP_ERR_0_arg1_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_DOM_PHP_ERR_0->args[1].value, &attribute_Deprecated_const_DOM_PHP_ERR_0_arg1); + attribute_Deprecated_const_DOM_PHP_ERR_0->args[1].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); } static zend_class_entry *register_class_DOMDocumentType(zend_class_entry *class_entry_DOMNode) diff --git a/ext/dom/tests/DOM_PHP_ERR_deprecated.phpt b/ext/dom/tests/DOM_PHP_ERR_deprecated.phpt index 76bd2757e7b5c..1dad517927206 100644 --- a/ext/dom/tests/DOM_PHP_ERR_deprecated.phpt +++ b/ext/dom/tests/DOM_PHP_ERR_deprecated.phpt @@ -7,5 +7,5 @@ dom var_dump(DOM_PHP_ERR); ?> --EXPECTF-- -Deprecated: Constant DOM_PHP_ERR is deprecated in %s on line %d +Deprecated: Constant DOM_PHP_ERR is deprecated since 8.4, as it is no longer used in %s on line %d int(0) diff --git a/ext/enchant/enchant.stub.php b/ext/enchant/enchant.stub.php index eafce22eac7d3..6ddbee768f14d 100644 --- a/ext/enchant/enchant.stub.php +++ b/ext/enchant/enchant.stub.php @@ -5,14 +5,14 @@ /** * @var int * @cvalue PHP_ENCHANT_MYSPELL - * @deprecated */ +#[\Deprecated(since: '8.0', message: 'as enchant_broker_get_dict_path() and enchant_broker_set_dict_path() are deprecated')] const ENCHANT_MYSPELL = UNKNOWN; /** * @var int * @cvalue PHP_ENCHANT_ISPELL - * @deprecated */ +#[\Deprecated(since: '8.0', message: 'as enchant_broker_get_dict_path() and enchant_broker_set_dict_path() are deprecated')] const ENCHANT_ISPELL = UNKNOWN; #ifdef HAVE_ENCHANT_GET_VERSION /** diff --git a/ext/enchant/enchant_arginfo.h b/ext/enchant/enchant_arginfo.h index a06f713f9cedf..0cd8707e1a172 100644 --- a/ext/enchant/enchant_arginfo.h +++ b/ext/enchant/enchant_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 9dd3fce23840ced1c265f8ec1dd3929298fdfe37 */ + * Stub hash: 31974eb901477da53ede7476953d461d32f772ba */ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_enchant_broker_init, 0, 0, EnchantBroker, MAY_BE_FALSE) ZEND_END_ARG_INFO() @@ -224,6 +224,32 @@ static void register_enchant_symbols(int module_number) ZVAL_STR(&attribute_Deprecated_func_enchant_dict_is_in_session_0_arg1, attribute_Deprecated_func_enchant_dict_is_in_session_0_arg1_str); ZVAL_COPY_VALUE(&attribute_Deprecated_func_enchant_dict_is_in_session_0->args[1].value, &attribute_Deprecated_func_enchant_dict_is_in_session_0_arg1); attribute_Deprecated_func_enchant_dict_is_in_session_0->args[1].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); + zend_constant *const_ENCHANT_MYSPELL = zend_hash_str_find_ptr(EG(zend_constants), "ENCHANT_MYSPELL", sizeof("ENCHANT_MYSPELL") - 1); + + zend_attribute *attribute_Deprecated_const_ENCHANT_MYSPELL_0 = zend_add_global_constant_attribute(const_ENCHANT_MYSPELL, ZSTR_KNOWN(ZEND_STR_DEPRECATED_CAPITALIZED), 2); + zval attribute_Deprecated_const_ENCHANT_MYSPELL_0_arg0; + zend_string *attribute_Deprecated_const_ENCHANT_MYSPELL_0_arg0_str = zend_string_init("8.0", strlen("8.0"), 1); + ZVAL_STR(&attribute_Deprecated_const_ENCHANT_MYSPELL_0_arg0, attribute_Deprecated_const_ENCHANT_MYSPELL_0_arg0_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_ENCHANT_MYSPELL_0->args[0].value, &attribute_Deprecated_const_ENCHANT_MYSPELL_0_arg0); + attribute_Deprecated_const_ENCHANT_MYSPELL_0->args[0].name = ZSTR_KNOWN(ZEND_STR_SINCE); + zval attribute_Deprecated_const_ENCHANT_MYSPELL_0_arg1; + zend_string *attribute_Deprecated_const_ENCHANT_MYSPELL_0_arg1_str = zend_string_init("as enchant_broker_get_dict_path() and enchant_broker_set_dict_path() are deprecated", strlen("as enchant_broker_get_dict_path() and enchant_broker_set_dict_path() are deprecated"), 1); + ZVAL_STR(&attribute_Deprecated_const_ENCHANT_MYSPELL_0_arg1, attribute_Deprecated_const_ENCHANT_MYSPELL_0_arg1_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_ENCHANT_MYSPELL_0->args[1].value, &attribute_Deprecated_const_ENCHANT_MYSPELL_0_arg1); + attribute_Deprecated_const_ENCHANT_MYSPELL_0->args[1].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); + zend_constant *const_ENCHANT_ISPELL = zend_hash_str_find_ptr(EG(zend_constants), "ENCHANT_ISPELL", sizeof("ENCHANT_ISPELL") - 1); + + zend_attribute *attribute_Deprecated_const_ENCHANT_ISPELL_0 = zend_add_global_constant_attribute(const_ENCHANT_ISPELL, ZSTR_KNOWN(ZEND_STR_DEPRECATED_CAPITALIZED), 2); + zval attribute_Deprecated_const_ENCHANT_ISPELL_0_arg0; + zend_string *attribute_Deprecated_const_ENCHANT_ISPELL_0_arg0_str = zend_string_init("8.0", strlen("8.0"), 1); + ZVAL_STR(&attribute_Deprecated_const_ENCHANT_ISPELL_0_arg0, attribute_Deprecated_const_ENCHANT_ISPELL_0_arg0_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_ENCHANT_ISPELL_0->args[0].value, &attribute_Deprecated_const_ENCHANT_ISPELL_0_arg0); + attribute_Deprecated_const_ENCHANT_ISPELL_0->args[0].name = ZSTR_KNOWN(ZEND_STR_SINCE); + zval attribute_Deprecated_const_ENCHANT_ISPELL_0_arg1; + zend_string *attribute_Deprecated_const_ENCHANT_ISPELL_0_arg1_str = zend_string_init("as enchant_broker_get_dict_path() and enchant_broker_set_dict_path() are deprecated", strlen("as enchant_broker_get_dict_path() and enchant_broker_set_dict_path() are deprecated"), 1); + ZVAL_STR(&attribute_Deprecated_const_ENCHANT_ISPELL_0_arg1, attribute_Deprecated_const_ENCHANT_ISPELL_0_arg1_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_ENCHANT_ISPELL_0->args[1].value, &attribute_Deprecated_const_ENCHANT_ISPELL_0_arg1); + attribute_Deprecated_const_ENCHANT_ISPELL_0->args[1].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); } static zend_class_entry *register_class_EnchantBroker(void) diff --git a/ext/filter/filter.c b/ext/filter/filter.c index 50eefb440d67a..7f3624eb4c07a 100644 --- a/ext/filter/filter.c +++ b/ext/filter/filter.c @@ -27,6 +27,7 @@ ZEND_DECLARE_MODULE_GLOBALS(filter) +#include "zend_attributes.h" #include "filter_private.h" #include "filter_arginfo.h" diff --git a/ext/filter/filter.stub.php b/ext/filter/filter.stub.php index 030de50f51890..a8790bb7cd615 100644 --- a/ext/filter/filter.stub.php +++ b/ext/filter/filter.stub.php @@ -121,14 +121,14 @@ /** * @var int * @cvalue FILTER_SANITIZE_STRING - * @deprecated */ +#[\Deprecated(since: '8.1', message: 'use htmlspecialchars() instead')] const FILTER_SANITIZE_STRING = UNKNOWN; /** * @var int * @cvalue FILTER_SANITIZE_STRING - * @deprecated */ +#[\Deprecated(since: '8.1', message: 'use htmlspecialchars() instead')] const FILTER_SANITIZE_STRIPPED = UNKNOWN; /** * @var int diff --git a/ext/filter/filter_arginfo.h b/ext/filter/filter_arginfo.h index a05806c5e1201..8cc562e5de236 100644 --- a/ext/filter/filter_arginfo.h +++ b/ext/filter/filter_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: c3f3240137eaa89316276920acf35f975b2dd8f9 */ + * Stub hash: a0f9a546d59bb27854af79a92e353f118ca6bdaf */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_filter_has_var, 0, 2, _IS_BOOL, 0) ZEND_ARG_TYPE_INFO(0, input_type, IS_LONG, 0) @@ -114,4 +114,31 @@ static void register_filter_symbols(int module_number) REGISTER_LONG_CONSTANT("FILTER_FLAG_GLOBAL_RANGE", FILTER_FLAG_GLOBAL_RANGE, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("FILTER_FLAG_HOSTNAME", FILTER_FLAG_HOSTNAME, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("FILTER_FLAG_EMAIL_UNICODE", FILTER_FLAG_EMAIL_UNICODE, CONST_PERSISTENT); + + zend_constant *const_FILTER_SANITIZE_STRING = zend_hash_str_find_ptr(EG(zend_constants), "FILTER_SANITIZE_STRING", sizeof("FILTER_SANITIZE_STRING") - 1); + + zend_attribute *attribute_Deprecated_const_FILTER_SANITIZE_STRING_0 = zend_add_global_constant_attribute(const_FILTER_SANITIZE_STRING, ZSTR_KNOWN(ZEND_STR_DEPRECATED_CAPITALIZED), 2); + zval attribute_Deprecated_const_FILTER_SANITIZE_STRING_0_arg0; + zend_string *attribute_Deprecated_const_FILTER_SANITIZE_STRING_0_arg0_str = zend_string_init("8.1", strlen("8.1"), 1); + ZVAL_STR(&attribute_Deprecated_const_FILTER_SANITIZE_STRING_0_arg0, attribute_Deprecated_const_FILTER_SANITIZE_STRING_0_arg0_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_FILTER_SANITIZE_STRING_0->args[0].value, &attribute_Deprecated_const_FILTER_SANITIZE_STRING_0_arg0); + attribute_Deprecated_const_FILTER_SANITIZE_STRING_0->args[0].name = ZSTR_KNOWN(ZEND_STR_SINCE); + zval attribute_Deprecated_const_FILTER_SANITIZE_STRING_0_arg1; + zend_string *attribute_Deprecated_const_FILTER_SANITIZE_STRING_0_arg1_str = zend_string_init("use htmlspecialchars() instead", strlen("use htmlspecialchars() instead"), 1); + ZVAL_STR(&attribute_Deprecated_const_FILTER_SANITIZE_STRING_0_arg1, attribute_Deprecated_const_FILTER_SANITIZE_STRING_0_arg1_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_FILTER_SANITIZE_STRING_0->args[1].value, &attribute_Deprecated_const_FILTER_SANITIZE_STRING_0_arg1); + attribute_Deprecated_const_FILTER_SANITIZE_STRING_0->args[1].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); + zend_constant *const_FILTER_SANITIZE_STRIPPED = zend_hash_str_find_ptr(EG(zend_constants), "FILTER_SANITIZE_STRIPPED", sizeof("FILTER_SANITIZE_STRIPPED") - 1); + + zend_attribute *attribute_Deprecated_const_FILTER_SANITIZE_STRIPPED_0 = zend_add_global_constant_attribute(const_FILTER_SANITIZE_STRIPPED, ZSTR_KNOWN(ZEND_STR_DEPRECATED_CAPITALIZED), 2); + zval attribute_Deprecated_const_FILTER_SANITIZE_STRIPPED_0_arg0; + zend_string *attribute_Deprecated_const_FILTER_SANITIZE_STRIPPED_0_arg0_str = zend_string_init("8.1", strlen("8.1"), 1); + ZVAL_STR(&attribute_Deprecated_const_FILTER_SANITIZE_STRIPPED_0_arg0, attribute_Deprecated_const_FILTER_SANITIZE_STRIPPED_0_arg0_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_FILTER_SANITIZE_STRIPPED_0->args[0].value, &attribute_Deprecated_const_FILTER_SANITIZE_STRIPPED_0_arg0); + attribute_Deprecated_const_FILTER_SANITIZE_STRIPPED_0->args[0].name = ZSTR_KNOWN(ZEND_STR_SINCE); + zval attribute_Deprecated_const_FILTER_SANITIZE_STRIPPED_0_arg1; + zend_string *attribute_Deprecated_const_FILTER_SANITIZE_STRIPPED_0_arg1_str = zend_string_init("use htmlspecialchars() instead", strlen("use htmlspecialchars() instead"), 1); + ZVAL_STR(&attribute_Deprecated_const_FILTER_SANITIZE_STRIPPED_0_arg1, attribute_Deprecated_const_FILTER_SANITIZE_STRIPPED_0_arg1_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_FILTER_SANITIZE_STRIPPED_0->args[1].value, &attribute_Deprecated_const_FILTER_SANITIZE_STRIPPED_0_arg1); + attribute_Deprecated_const_FILTER_SANITIZE_STRIPPED_0->args[1].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); } diff --git a/ext/filter/tests/025.phpt b/ext/filter/tests/025.phpt index d8e06e3ac20cc..05459fcb1cb27 100644 --- a/ext/filter/tests/025.phpt +++ b/ext/filter/tests/025.phpt @@ -16,24 +16,24 @@ var_dump(filter_var(".", FILTER_SANITIZE_STRING)); echo "Done\n"; ?> --EXPECTF-- -Deprecated: Constant FILTER_SANITIZE_STRING is deprecated in %s on line %d +Deprecated: Constant FILTER_SANITIZE_STRING is deprecated since 8.1, use htmlspecialchars() instead in %s on line %d string(0) "" -Deprecated: Constant FILTER_SANITIZE_STRING is deprecated in %s on line %d +Deprecated: Constant FILTER_SANITIZE_STRING is deprecated since 8.1, use htmlspecialchars() instead in %s on line %d string(0) "" -Deprecated: Constant FILTER_SANITIZE_STRING is deprecated in %s on line %d +Deprecated: Constant FILTER_SANITIZE_STRING is deprecated since 8.1, use htmlspecialchars() instead in %s on line %d string(12) "!@#$%^&*()'"" -Deprecated: Constant FILTER_SANITIZE_STRING is deprecated in %s on line %d +Deprecated: Constant FILTER_SANITIZE_STRING is deprecated since 8.1, use htmlspecialchars() instead in %s on line %d string(24) "!@#$%^&*()'"" -Deprecated: Constant FILTER_SANITIZE_STRING is deprecated in %s on line %d +Deprecated: Constant FILTER_SANITIZE_STRING is deprecated since 8.1, use htmlspecialchars() instead in %s on line %d string(11) "`1234567890" -Deprecated: Constant FILTER_SANITIZE_STRING is deprecated in %s on line %d +Deprecated: Constant FILTER_SANITIZE_STRING is deprecated since 8.1, use htmlspecialchars() instead in %s on line %d string(5) "`123`" -Deprecated: Constant FILTER_SANITIZE_STRING is deprecated in %s on line %d +Deprecated: Constant FILTER_SANITIZE_STRING is deprecated since 8.1, use htmlspecialchars() instead in %s on line %d string(1) "." Done diff --git a/ext/filter/tests/026.phpt b/ext/filter/tests/026.phpt index db43df7949a0d..856f5325cb3b9 100644 --- a/ext/filter/tests/026.phpt +++ b/ext/filter/tests/026.phpt @@ -20,30 +20,30 @@ var_dump(filter_var("", FILTER_SANITIZE_STRIPPED, FILTER_FLAG_STRIP_HIGH)); echo "Done\n"; ?> --EXPECTF-- -Deprecated: Constant FILTER_SANITIZE_STRIPPED is deprecated in %s on line %d +Deprecated: Constant FILTER_SANITIZE_STRIPPED is deprecated since 8.1, use htmlspecialchars() instead in %s on line %d string(40) "Let me see you Stripped down to the bone" -Deprecated: Constant FILTER_SANITIZE_STRIPPED is deprecated in %s on line %d +Deprecated: Constant FILTER_SANITIZE_STRIPPED is deprecated since 8.1, use htmlspecialchars() instead in %s on line %d string(11) "!@#$%^&*()>" -Deprecated: Constant FILTER_SANITIZE_STRIPPED is deprecated in %s on line %d +Deprecated: Constant FILTER_SANITIZE_STRIPPED is deprecated since 8.1, use htmlspecialchars() instead in %s on line %d string(0) "" -Deprecated: Constant FILTER_SANITIZE_STRIPPED is deprecated in %s on line %d +Deprecated: Constant FILTER_SANITIZE_STRIPPED is deprecated since 8.1, use htmlspecialchars() instead in %s on line %d string(40) "Let me see you Stripped down to the bone" -Deprecated: Constant FILTER_SANITIZE_STRIPPED is deprecated in %s on line %d +Deprecated: Constant FILTER_SANITIZE_STRIPPED is deprecated since 8.1, use htmlspecialchars() instead in %s on line %d string(11) "!@#$%^&*()>" -Deprecated: Constant FILTER_SANITIZE_STRIPPED is deprecated in %s on line %d +Deprecated: Constant FILTER_SANITIZE_STRIPPED is deprecated since 8.1, use htmlspecialchars() instead in %s on line %d string(0) "" -Deprecated: Constant FILTER_SANITIZE_STRIPPED is deprecated in %s on line %d +Deprecated: Constant FILTER_SANITIZE_STRIPPED is deprecated since 8.1, use htmlspecialchars() instead in %s on line %d string(40) "Let me see you Stripped down to the bone" -Deprecated: Constant FILTER_SANITIZE_STRIPPED is deprecated in %s on line %d +Deprecated: Constant FILTER_SANITIZE_STRIPPED is deprecated since 8.1, use htmlspecialchars() instead in %s on line %d string(11) "!@#$%^&*()>" -Deprecated: Constant FILTER_SANITIZE_STRIPPED is deprecated in %s on line %d +Deprecated: Constant FILTER_SANITIZE_STRIPPED is deprecated since 8.1, use htmlspecialchars() instead in %s on line %d string(0) "" Done diff --git a/ext/filter/tests/042.phpt b/ext/filter/tests/042.phpt index 0795392f7acaa..28f346afeefc4 100644 --- a/ext/filter/tests/042.phpt +++ b/ext/filter/tests/042.phpt @@ -15,8 +15,8 @@ $a = filter_var($var, FILTER_SANITIZE_STRING, array("flags" => FILTER_FLAG_STRIP echo $a . "\n"; ?> --EXPECTF-- -Deprecated: Constant FILTER_SANITIZE_STRING is deprecated in %s on line %d +Deprecated: Constant FILTER_SANITIZE_STRING is deprecated since 8.1, use htmlspecialchars() instead in %s on line %d XYZalert(/ext/filter+bypass/);ABC -Deprecated: Constant FILTER_SANITIZE_STRING is deprecated in %s on line %d +Deprecated: Constant FILTER_SANITIZE_STRING is deprecated since 8.1, use htmlspecialchars() instead in %s on line %d XYZalert(/ext/filter+bypass/);ABC diff --git a/ext/filter/tests/bug69203.phpt b/ext/filter/tests/bug69203.phpt index 3453c7c0adc6f..85356ba2a1349 100644 --- a/ext/filter/tests/bug69203.phpt +++ b/ext/filter/tests/bug69203.phpt @@ -10,7 +10,7 @@ var_dump(filter_var("\x7f", FILTER_SANITIZE_ENCODED, FILTER_FLAG_STRIP_HIGH)); var_dump(filter_var("\x7f", FILTER_SANITIZE_SPECIAL_CHARS, FILTER_FLAG_STRIP_HIGH)); ?> --EXPECTF-- -Deprecated: Constant FILTER_SANITIZE_STRING is deprecated in %s on line %d +Deprecated: Constant FILTER_SANITIZE_STRING is deprecated since 8.1, use htmlspecialchars() instead in %s on line %d string(0) "" string(0) "" string(0) "" diff --git a/ext/mysqli/mysqli.stub.php b/ext/mysqli/mysqli.stub.php index de287b347deef..06db6ac26860b 100644 --- a/ext/mysqli/mysqli.stub.php +++ b/ext/mysqli/mysqli.stub.php @@ -136,8 +136,8 @@ /** * @var int * @cvalue MYSQLI_STORE_RESULT_COPY_DATA - * @deprecated */ +#[\Deprecated(since: '8.4', message: 'as the mysqli_store_result() parameter is unused since 8.1')] const MYSQLI_STORE_RESULT_COPY_DATA = UNKNOWN; /* for mysqli_fetch_assoc */ @@ -423,14 +423,14 @@ /** * @var int * @cvalue MYSQL_NO_DATA - * @deprecated */ +#[\Deprecated(since: '8.1', message: 'as it was unused')] const MYSQLI_NO_DATA = UNKNOWN; /** * @var int * @cvalue MYSQL_DATA_TRUNCATED - * @deprecated */ +#[\Deprecated(since: '8.1', message: 'as it was unused')] const MYSQLI_DATA_TRUNCATED = UNKNOWN; /* reporting */ @@ -469,87 +469,87 @@ /** * @var int * @cvalue SERVER_QUERY_NO_GOOD_INDEX_USED - * @deprecated */ +#[\Deprecated(since: '8.1', message: 'as it was unused')] const MYSQLI_SERVER_QUERY_NO_GOOD_INDEX_USED = UNKNOWN; /** * @var int * @cvalue SERVER_QUERY_NO_INDEX_USED - * @deprecated */ +#[\Deprecated(since: '8.1', message: 'as it was unused')] const MYSQLI_SERVER_QUERY_NO_INDEX_USED = UNKNOWN; /** * @var int * @cvalue SERVER_QUERY_WAS_SLOW - * @deprecated */ +#[\Deprecated(since: '8.1', message: 'as it was unused')] const MYSQLI_SERVER_QUERY_WAS_SLOW = UNKNOWN; /** * @var int * @cvalue SERVER_PS_OUT_PARAMS - * @deprecated */ +#[\Deprecated(since: '8.1', message: 'as it was unused')] const MYSQLI_SERVER_PS_OUT_PARAMS = UNKNOWN; /** * @var int * @cvalue REFRESH_GRANT - * @deprecated */ +#[\Deprecated(since: '8.4', message: 'as mysqli_refresh() is deprecated')] const MYSQLI_REFRESH_GRANT = UNKNOWN; /** * @var int * @cvalue REFRESH_LOG - * @deprecated */ +#[\Deprecated(since: '8.4', message: 'as mysqli_refresh() is deprecated')] const MYSQLI_REFRESH_LOG = UNKNOWN; /** * @var int * @cvalue REFRESH_TABLES - * @deprecated */ +#[\Deprecated(since: '8.4', message: 'as mysqli_refresh() is deprecated')] const MYSQLI_REFRESH_TABLES = UNKNOWN; /** * @var int * @cvalue REFRESH_HOSTS - * @deprecated */ +#[\Deprecated(since: '8.4', message: 'as mysqli_refresh() is deprecated')] const MYSQLI_REFRESH_HOSTS = UNKNOWN; /** * @var int * @cvalue REFRESH_STATUS - * @deprecated */ +#[\Deprecated(since: '8.4', message: 'as mysqli_refresh() is deprecated')] const MYSQLI_REFRESH_STATUS = UNKNOWN; /** * @var int * @cvalue REFRESH_THREADS - * @deprecated */ +#[\Deprecated(since: '8.4', message: 'as mysqli_refresh() is deprecated')] const MYSQLI_REFRESH_THREADS = UNKNOWN; /** * @var int * @cvalue REFRESH_SLAVE - * @deprecated */ +#[\Deprecated(since: '8.4', message: 'as mysqli_refresh() is deprecated')] const MYSQLI_REFRESH_REPLICA = UNKNOWN; /** * @var int * @cvalue REFRESH_SLAVE - * @deprecated */ +#[\Deprecated(since: '8.4', message: 'as mysqli_refresh() is deprecated')] const MYSQLI_REFRESH_SLAVE = UNKNOWN; /** * @var int * @cvalue REFRESH_MASTER - * @deprecated */ +#[\Deprecated(since: '8.4', message: 'as mysqli_refresh() is deprecated')] const MYSQLI_REFRESH_MASTER = UNKNOWN; /** * @var int * @cvalue REFRESH_BACKUP_LOG - * @deprecated */ +#[\Deprecated(since: '8.4', message: 'as mysqli_refresh() is deprecated')] const MYSQLI_REFRESH_BACKUP_LOG = UNKNOWN; /** @@ -591,8 +591,8 @@ /** * @var bool - * @deprecated */ +#[\Deprecated(since: '8.2', message: 'as it is always false')] const MYSQLI_IS_MARIADB = false; final class mysqli_driver diff --git a/ext/mysqli/mysqli_arginfo.h b/ext/mysqli/mysqli_arginfo.h index 4e624d623d807..43dba58417f36 100644 --- a/ext/mysqli/mysqli_arginfo.h +++ b/ext/mysqli/mysqli_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 32baf7b0642af68dea551687bc44ae47d708d810 */ + * Stub hash: 2547f63fd024fd5f4fecc574bbcff1301d1d36e5 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_mysqli_affected_rows, 0, 1, MAY_BE_LONG|MAY_BE_STRING) ZEND_ARG_OBJ_INFO(0, mysql, mysqli, 0) @@ -1187,6 +1187,240 @@ static void register_mysqli_symbols(int module_number) ZVAL_STR(&attribute_Deprecated_func_mysqli_refresh_0_arg1, attribute_Deprecated_func_mysqli_refresh_0_arg1_str); ZVAL_COPY_VALUE(&attribute_Deprecated_func_mysqli_refresh_0->args[1].value, &attribute_Deprecated_func_mysqli_refresh_0_arg1); attribute_Deprecated_func_mysqli_refresh_0->args[1].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); + zend_constant *const_MYSQLI_STORE_RESULT_COPY_DATA = zend_hash_str_find_ptr(EG(zend_constants), "MYSQLI_STORE_RESULT_COPY_DATA", sizeof("MYSQLI_STORE_RESULT_COPY_DATA") - 1); + + zend_attribute *attribute_Deprecated_const_MYSQLI_STORE_RESULT_COPY_DATA_0 = zend_add_global_constant_attribute(const_MYSQLI_STORE_RESULT_COPY_DATA, ZSTR_KNOWN(ZEND_STR_DEPRECATED_CAPITALIZED), 2); + zval attribute_Deprecated_const_MYSQLI_STORE_RESULT_COPY_DATA_0_arg0; + zend_string *attribute_Deprecated_const_MYSQLI_STORE_RESULT_COPY_DATA_0_arg0_str = zend_string_init("8.4", strlen("8.4"), 1); + ZVAL_STR(&attribute_Deprecated_const_MYSQLI_STORE_RESULT_COPY_DATA_0_arg0, attribute_Deprecated_const_MYSQLI_STORE_RESULT_COPY_DATA_0_arg0_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_MYSQLI_STORE_RESULT_COPY_DATA_0->args[0].value, &attribute_Deprecated_const_MYSQLI_STORE_RESULT_COPY_DATA_0_arg0); + attribute_Deprecated_const_MYSQLI_STORE_RESULT_COPY_DATA_0->args[0].name = ZSTR_KNOWN(ZEND_STR_SINCE); + zval attribute_Deprecated_const_MYSQLI_STORE_RESULT_COPY_DATA_0_arg1; + zend_string *attribute_Deprecated_const_MYSQLI_STORE_RESULT_COPY_DATA_0_arg1_str = zend_string_init("as the mysqli_store_result() parameter is unused since 8.1", strlen("as the mysqli_store_result() parameter is unused since 8.1"), 1); + ZVAL_STR(&attribute_Deprecated_const_MYSQLI_STORE_RESULT_COPY_DATA_0_arg1, attribute_Deprecated_const_MYSQLI_STORE_RESULT_COPY_DATA_0_arg1_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_MYSQLI_STORE_RESULT_COPY_DATA_0->args[1].value, &attribute_Deprecated_const_MYSQLI_STORE_RESULT_COPY_DATA_0_arg1); + attribute_Deprecated_const_MYSQLI_STORE_RESULT_COPY_DATA_0->args[1].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); + zend_constant *const_MYSQLI_NO_DATA = zend_hash_str_find_ptr(EG(zend_constants), "MYSQLI_NO_DATA", sizeof("MYSQLI_NO_DATA") - 1); + + zend_attribute *attribute_Deprecated_const_MYSQLI_NO_DATA_0 = zend_add_global_constant_attribute(const_MYSQLI_NO_DATA, ZSTR_KNOWN(ZEND_STR_DEPRECATED_CAPITALIZED), 2); + zval attribute_Deprecated_const_MYSQLI_NO_DATA_0_arg0; + zend_string *attribute_Deprecated_const_MYSQLI_NO_DATA_0_arg0_str = zend_string_init("8.1", strlen("8.1"), 1); + ZVAL_STR(&attribute_Deprecated_const_MYSQLI_NO_DATA_0_arg0, attribute_Deprecated_const_MYSQLI_NO_DATA_0_arg0_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_MYSQLI_NO_DATA_0->args[0].value, &attribute_Deprecated_const_MYSQLI_NO_DATA_0_arg0); + attribute_Deprecated_const_MYSQLI_NO_DATA_0->args[0].name = ZSTR_KNOWN(ZEND_STR_SINCE); + zval attribute_Deprecated_const_MYSQLI_NO_DATA_0_arg1; + zend_string *attribute_Deprecated_const_MYSQLI_NO_DATA_0_arg1_str = zend_string_init("as it was unused", strlen("as it was unused"), 1); + ZVAL_STR(&attribute_Deprecated_const_MYSQLI_NO_DATA_0_arg1, attribute_Deprecated_const_MYSQLI_NO_DATA_0_arg1_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_MYSQLI_NO_DATA_0->args[1].value, &attribute_Deprecated_const_MYSQLI_NO_DATA_0_arg1); + attribute_Deprecated_const_MYSQLI_NO_DATA_0->args[1].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); + zend_constant *const_MYSQLI_DATA_TRUNCATED = zend_hash_str_find_ptr(EG(zend_constants), "MYSQLI_DATA_TRUNCATED", sizeof("MYSQLI_DATA_TRUNCATED") - 1); + + zend_attribute *attribute_Deprecated_const_MYSQLI_DATA_TRUNCATED_0 = zend_add_global_constant_attribute(const_MYSQLI_DATA_TRUNCATED, ZSTR_KNOWN(ZEND_STR_DEPRECATED_CAPITALIZED), 2); + zval attribute_Deprecated_const_MYSQLI_DATA_TRUNCATED_0_arg0; + zend_string *attribute_Deprecated_const_MYSQLI_DATA_TRUNCATED_0_arg0_str = zend_string_init("8.1", strlen("8.1"), 1); + ZVAL_STR(&attribute_Deprecated_const_MYSQLI_DATA_TRUNCATED_0_arg0, attribute_Deprecated_const_MYSQLI_DATA_TRUNCATED_0_arg0_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_MYSQLI_DATA_TRUNCATED_0->args[0].value, &attribute_Deprecated_const_MYSQLI_DATA_TRUNCATED_0_arg0); + attribute_Deprecated_const_MYSQLI_DATA_TRUNCATED_0->args[0].name = ZSTR_KNOWN(ZEND_STR_SINCE); + zval attribute_Deprecated_const_MYSQLI_DATA_TRUNCATED_0_arg1; + zend_string *attribute_Deprecated_const_MYSQLI_DATA_TRUNCATED_0_arg1_str = zend_string_init("as it was unused", strlen("as it was unused"), 1); + ZVAL_STR(&attribute_Deprecated_const_MYSQLI_DATA_TRUNCATED_0_arg1, attribute_Deprecated_const_MYSQLI_DATA_TRUNCATED_0_arg1_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_MYSQLI_DATA_TRUNCATED_0->args[1].value, &attribute_Deprecated_const_MYSQLI_DATA_TRUNCATED_0_arg1); + attribute_Deprecated_const_MYSQLI_DATA_TRUNCATED_0->args[1].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); + zend_constant *const_MYSQLI_SERVER_QUERY_NO_GOOD_INDEX_USED = zend_hash_str_find_ptr(EG(zend_constants), "MYSQLI_SERVER_QUERY_NO_GOOD_INDEX_USED", sizeof("MYSQLI_SERVER_QUERY_NO_GOOD_INDEX_USED") - 1); + + zend_attribute *attribute_Deprecated_const_MYSQLI_SERVER_QUERY_NO_GOOD_INDEX_USED_0 = zend_add_global_constant_attribute(const_MYSQLI_SERVER_QUERY_NO_GOOD_INDEX_USED, ZSTR_KNOWN(ZEND_STR_DEPRECATED_CAPITALIZED), 2); + zval attribute_Deprecated_const_MYSQLI_SERVER_QUERY_NO_GOOD_INDEX_USED_0_arg0; + zend_string *attribute_Deprecated_const_MYSQLI_SERVER_QUERY_NO_GOOD_INDEX_USED_0_arg0_str = zend_string_init("8.1", strlen("8.1"), 1); + ZVAL_STR(&attribute_Deprecated_const_MYSQLI_SERVER_QUERY_NO_GOOD_INDEX_USED_0_arg0, attribute_Deprecated_const_MYSQLI_SERVER_QUERY_NO_GOOD_INDEX_USED_0_arg0_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_MYSQLI_SERVER_QUERY_NO_GOOD_INDEX_USED_0->args[0].value, &attribute_Deprecated_const_MYSQLI_SERVER_QUERY_NO_GOOD_INDEX_USED_0_arg0); + attribute_Deprecated_const_MYSQLI_SERVER_QUERY_NO_GOOD_INDEX_USED_0->args[0].name = ZSTR_KNOWN(ZEND_STR_SINCE); + zval attribute_Deprecated_const_MYSQLI_SERVER_QUERY_NO_GOOD_INDEX_USED_0_arg1; + zend_string *attribute_Deprecated_const_MYSQLI_SERVER_QUERY_NO_GOOD_INDEX_USED_0_arg1_str = zend_string_init("as it was unused", strlen("as it was unused"), 1); + ZVAL_STR(&attribute_Deprecated_const_MYSQLI_SERVER_QUERY_NO_GOOD_INDEX_USED_0_arg1, attribute_Deprecated_const_MYSQLI_SERVER_QUERY_NO_GOOD_INDEX_USED_0_arg1_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_MYSQLI_SERVER_QUERY_NO_GOOD_INDEX_USED_0->args[1].value, &attribute_Deprecated_const_MYSQLI_SERVER_QUERY_NO_GOOD_INDEX_USED_0_arg1); + attribute_Deprecated_const_MYSQLI_SERVER_QUERY_NO_GOOD_INDEX_USED_0->args[1].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); + zend_constant *const_MYSQLI_SERVER_QUERY_NO_INDEX_USED = zend_hash_str_find_ptr(EG(zend_constants), "MYSQLI_SERVER_QUERY_NO_INDEX_USED", sizeof("MYSQLI_SERVER_QUERY_NO_INDEX_USED") - 1); + + zend_attribute *attribute_Deprecated_const_MYSQLI_SERVER_QUERY_NO_INDEX_USED_0 = zend_add_global_constant_attribute(const_MYSQLI_SERVER_QUERY_NO_INDEX_USED, ZSTR_KNOWN(ZEND_STR_DEPRECATED_CAPITALIZED), 2); + zval attribute_Deprecated_const_MYSQLI_SERVER_QUERY_NO_INDEX_USED_0_arg0; + zend_string *attribute_Deprecated_const_MYSQLI_SERVER_QUERY_NO_INDEX_USED_0_arg0_str = zend_string_init("8.1", strlen("8.1"), 1); + ZVAL_STR(&attribute_Deprecated_const_MYSQLI_SERVER_QUERY_NO_INDEX_USED_0_arg0, attribute_Deprecated_const_MYSQLI_SERVER_QUERY_NO_INDEX_USED_0_arg0_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_MYSQLI_SERVER_QUERY_NO_INDEX_USED_0->args[0].value, &attribute_Deprecated_const_MYSQLI_SERVER_QUERY_NO_INDEX_USED_0_arg0); + attribute_Deprecated_const_MYSQLI_SERVER_QUERY_NO_INDEX_USED_0->args[0].name = ZSTR_KNOWN(ZEND_STR_SINCE); + zval attribute_Deprecated_const_MYSQLI_SERVER_QUERY_NO_INDEX_USED_0_arg1; + zend_string *attribute_Deprecated_const_MYSQLI_SERVER_QUERY_NO_INDEX_USED_0_arg1_str = zend_string_init("as it was unused", strlen("as it was unused"), 1); + ZVAL_STR(&attribute_Deprecated_const_MYSQLI_SERVER_QUERY_NO_INDEX_USED_0_arg1, attribute_Deprecated_const_MYSQLI_SERVER_QUERY_NO_INDEX_USED_0_arg1_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_MYSQLI_SERVER_QUERY_NO_INDEX_USED_0->args[1].value, &attribute_Deprecated_const_MYSQLI_SERVER_QUERY_NO_INDEX_USED_0_arg1); + attribute_Deprecated_const_MYSQLI_SERVER_QUERY_NO_INDEX_USED_0->args[1].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); + zend_constant *const_MYSQLI_SERVER_QUERY_WAS_SLOW = zend_hash_str_find_ptr(EG(zend_constants), "MYSQLI_SERVER_QUERY_WAS_SLOW", sizeof("MYSQLI_SERVER_QUERY_WAS_SLOW") - 1); + + zend_attribute *attribute_Deprecated_const_MYSQLI_SERVER_QUERY_WAS_SLOW_0 = zend_add_global_constant_attribute(const_MYSQLI_SERVER_QUERY_WAS_SLOW, ZSTR_KNOWN(ZEND_STR_DEPRECATED_CAPITALIZED), 2); + zval attribute_Deprecated_const_MYSQLI_SERVER_QUERY_WAS_SLOW_0_arg0; + zend_string *attribute_Deprecated_const_MYSQLI_SERVER_QUERY_WAS_SLOW_0_arg0_str = zend_string_init("8.1", strlen("8.1"), 1); + ZVAL_STR(&attribute_Deprecated_const_MYSQLI_SERVER_QUERY_WAS_SLOW_0_arg0, attribute_Deprecated_const_MYSQLI_SERVER_QUERY_WAS_SLOW_0_arg0_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_MYSQLI_SERVER_QUERY_WAS_SLOW_0->args[0].value, &attribute_Deprecated_const_MYSQLI_SERVER_QUERY_WAS_SLOW_0_arg0); + attribute_Deprecated_const_MYSQLI_SERVER_QUERY_WAS_SLOW_0->args[0].name = ZSTR_KNOWN(ZEND_STR_SINCE); + zval attribute_Deprecated_const_MYSQLI_SERVER_QUERY_WAS_SLOW_0_arg1; + zend_string *attribute_Deprecated_const_MYSQLI_SERVER_QUERY_WAS_SLOW_0_arg1_str = zend_string_init("as it was unused", strlen("as it was unused"), 1); + ZVAL_STR(&attribute_Deprecated_const_MYSQLI_SERVER_QUERY_WAS_SLOW_0_arg1, attribute_Deprecated_const_MYSQLI_SERVER_QUERY_WAS_SLOW_0_arg1_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_MYSQLI_SERVER_QUERY_WAS_SLOW_0->args[1].value, &attribute_Deprecated_const_MYSQLI_SERVER_QUERY_WAS_SLOW_0_arg1); + attribute_Deprecated_const_MYSQLI_SERVER_QUERY_WAS_SLOW_0->args[1].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); + zend_constant *const_MYSQLI_SERVER_PS_OUT_PARAMS = zend_hash_str_find_ptr(EG(zend_constants), "MYSQLI_SERVER_PS_OUT_PARAMS", sizeof("MYSQLI_SERVER_PS_OUT_PARAMS") - 1); + + zend_attribute *attribute_Deprecated_const_MYSQLI_SERVER_PS_OUT_PARAMS_0 = zend_add_global_constant_attribute(const_MYSQLI_SERVER_PS_OUT_PARAMS, ZSTR_KNOWN(ZEND_STR_DEPRECATED_CAPITALIZED), 2); + zval attribute_Deprecated_const_MYSQLI_SERVER_PS_OUT_PARAMS_0_arg0; + zend_string *attribute_Deprecated_const_MYSQLI_SERVER_PS_OUT_PARAMS_0_arg0_str = zend_string_init("8.1", strlen("8.1"), 1); + ZVAL_STR(&attribute_Deprecated_const_MYSQLI_SERVER_PS_OUT_PARAMS_0_arg0, attribute_Deprecated_const_MYSQLI_SERVER_PS_OUT_PARAMS_0_arg0_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_MYSQLI_SERVER_PS_OUT_PARAMS_0->args[0].value, &attribute_Deprecated_const_MYSQLI_SERVER_PS_OUT_PARAMS_0_arg0); + attribute_Deprecated_const_MYSQLI_SERVER_PS_OUT_PARAMS_0->args[0].name = ZSTR_KNOWN(ZEND_STR_SINCE); + zval attribute_Deprecated_const_MYSQLI_SERVER_PS_OUT_PARAMS_0_arg1; + zend_string *attribute_Deprecated_const_MYSQLI_SERVER_PS_OUT_PARAMS_0_arg1_str = zend_string_init("as it was unused", strlen("as it was unused"), 1); + ZVAL_STR(&attribute_Deprecated_const_MYSQLI_SERVER_PS_OUT_PARAMS_0_arg1, attribute_Deprecated_const_MYSQLI_SERVER_PS_OUT_PARAMS_0_arg1_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_MYSQLI_SERVER_PS_OUT_PARAMS_0->args[1].value, &attribute_Deprecated_const_MYSQLI_SERVER_PS_OUT_PARAMS_0_arg1); + attribute_Deprecated_const_MYSQLI_SERVER_PS_OUT_PARAMS_0->args[1].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); + zend_constant *const_MYSQLI_REFRESH_GRANT = zend_hash_str_find_ptr(EG(zend_constants), "MYSQLI_REFRESH_GRANT", sizeof("MYSQLI_REFRESH_GRANT") - 1); + + zend_attribute *attribute_Deprecated_const_MYSQLI_REFRESH_GRANT_0 = zend_add_global_constant_attribute(const_MYSQLI_REFRESH_GRANT, ZSTR_KNOWN(ZEND_STR_DEPRECATED_CAPITALIZED), 2); + zval attribute_Deprecated_const_MYSQLI_REFRESH_GRANT_0_arg0; + zend_string *attribute_Deprecated_const_MYSQLI_REFRESH_GRANT_0_arg0_str = zend_string_init("8.4", strlen("8.4"), 1); + ZVAL_STR(&attribute_Deprecated_const_MYSQLI_REFRESH_GRANT_0_arg0, attribute_Deprecated_const_MYSQLI_REFRESH_GRANT_0_arg0_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_MYSQLI_REFRESH_GRANT_0->args[0].value, &attribute_Deprecated_const_MYSQLI_REFRESH_GRANT_0_arg0); + attribute_Deprecated_const_MYSQLI_REFRESH_GRANT_0->args[0].name = ZSTR_KNOWN(ZEND_STR_SINCE); + zval attribute_Deprecated_const_MYSQLI_REFRESH_GRANT_0_arg1; + zend_string *attribute_Deprecated_const_MYSQLI_REFRESH_GRANT_0_arg1_str = zend_string_init("as mysqli_refresh() is deprecated", strlen("as mysqli_refresh() is deprecated"), 1); + ZVAL_STR(&attribute_Deprecated_const_MYSQLI_REFRESH_GRANT_0_arg1, attribute_Deprecated_const_MYSQLI_REFRESH_GRANT_0_arg1_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_MYSQLI_REFRESH_GRANT_0->args[1].value, &attribute_Deprecated_const_MYSQLI_REFRESH_GRANT_0_arg1); + attribute_Deprecated_const_MYSQLI_REFRESH_GRANT_0->args[1].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); + zend_constant *const_MYSQLI_REFRESH_LOG = zend_hash_str_find_ptr(EG(zend_constants), "MYSQLI_REFRESH_LOG", sizeof("MYSQLI_REFRESH_LOG") - 1); + + zend_attribute *attribute_Deprecated_const_MYSQLI_REFRESH_LOG_0 = zend_add_global_constant_attribute(const_MYSQLI_REFRESH_LOG, ZSTR_KNOWN(ZEND_STR_DEPRECATED_CAPITALIZED), 2); + zval attribute_Deprecated_const_MYSQLI_REFRESH_LOG_0_arg0; + zend_string *attribute_Deprecated_const_MYSQLI_REFRESH_LOG_0_arg0_str = zend_string_init("8.4", strlen("8.4"), 1); + ZVAL_STR(&attribute_Deprecated_const_MYSQLI_REFRESH_LOG_0_arg0, attribute_Deprecated_const_MYSQLI_REFRESH_LOG_0_arg0_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_MYSQLI_REFRESH_LOG_0->args[0].value, &attribute_Deprecated_const_MYSQLI_REFRESH_LOG_0_arg0); + attribute_Deprecated_const_MYSQLI_REFRESH_LOG_0->args[0].name = ZSTR_KNOWN(ZEND_STR_SINCE); + zval attribute_Deprecated_const_MYSQLI_REFRESH_LOG_0_arg1; + zend_string *attribute_Deprecated_const_MYSQLI_REFRESH_LOG_0_arg1_str = zend_string_init("as mysqli_refresh() is deprecated", strlen("as mysqli_refresh() is deprecated"), 1); + ZVAL_STR(&attribute_Deprecated_const_MYSQLI_REFRESH_LOG_0_arg1, attribute_Deprecated_const_MYSQLI_REFRESH_LOG_0_arg1_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_MYSQLI_REFRESH_LOG_0->args[1].value, &attribute_Deprecated_const_MYSQLI_REFRESH_LOG_0_arg1); + attribute_Deprecated_const_MYSQLI_REFRESH_LOG_0->args[1].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); + zend_constant *const_MYSQLI_REFRESH_TABLES = zend_hash_str_find_ptr(EG(zend_constants), "MYSQLI_REFRESH_TABLES", sizeof("MYSQLI_REFRESH_TABLES") - 1); + + zend_attribute *attribute_Deprecated_const_MYSQLI_REFRESH_TABLES_0 = zend_add_global_constant_attribute(const_MYSQLI_REFRESH_TABLES, ZSTR_KNOWN(ZEND_STR_DEPRECATED_CAPITALIZED), 2); + zval attribute_Deprecated_const_MYSQLI_REFRESH_TABLES_0_arg0; + zend_string *attribute_Deprecated_const_MYSQLI_REFRESH_TABLES_0_arg0_str = zend_string_init("8.4", strlen("8.4"), 1); + ZVAL_STR(&attribute_Deprecated_const_MYSQLI_REFRESH_TABLES_0_arg0, attribute_Deprecated_const_MYSQLI_REFRESH_TABLES_0_arg0_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_MYSQLI_REFRESH_TABLES_0->args[0].value, &attribute_Deprecated_const_MYSQLI_REFRESH_TABLES_0_arg0); + attribute_Deprecated_const_MYSQLI_REFRESH_TABLES_0->args[0].name = ZSTR_KNOWN(ZEND_STR_SINCE); + zval attribute_Deprecated_const_MYSQLI_REFRESH_TABLES_0_arg1; + zend_string *attribute_Deprecated_const_MYSQLI_REFRESH_TABLES_0_arg1_str = zend_string_init("as mysqli_refresh() is deprecated", strlen("as mysqli_refresh() is deprecated"), 1); + ZVAL_STR(&attribute_Deprecated_const_MYSQLI_REFRESH_TABLES_0_arg1, attribute_Deprecated_const_MYSQLI_REFRESH_TABLES_0_arg1_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_MYSQLI_REFRESH_TABLES_0->args[1].value, &attribute_Deprecated_const_MYSQLI_REFRESH_TABLES_0_arg1); + attribute_Deprecated_const_MYSQLI_REFRESH_TABLES_0->args[1].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); + zend_constant *const_MYSQLI_REFRESH_HOSTS = zend_hash_str_find_ptr(EG(zend_constants), "MYSQLI_REFRESH_HOSTS", sizeof("MYSQLI_REFRESH_HOSTS") - 1); + + zend_attribute *attribute_Deprecated_const_MYSQLI_REFRESH_HOSTS_0 = zend_add_global_constant_attribute(const_MYSQLI_REFRESH_HOSTS, ZSTR_KNOWN(ZEND_STR_DEPRECATED_CAPITALIZED), 2); + zval attribute_Deprecated_const_MYSQLI_REFRESH_HOSTS_0_arg0; + zend_string *attribute_Deprecated_const_MYSQLI_REFRESH_HOSTS_0_arg0_str = zend_string_init("8.4", strlen("8.4"), 1); + ZVAL_STR(&attribute_Deprecated_const_MYSQLI_REFRESH_HOSTS_0_arg0, attribute_Deprecated_const_MYSQLI_REFRESH_HOSTS_0_arg0_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_MYSQLI_REFRESH_HOSTS_0->args[0].value, &attribute_Deprecated_const_MYSQLI_REFRESH_HOSTS_0_arg0); + attribute_Deprecated_const_MYSQLI_REFRESH_HOSTS_0->args[0].name = ZSTR_KNOWN(ZEND_STR_SINCE); + zval attribute_Deprecated_const_MYSQLI_REFRESH_HOSTS_0_arg1; + zend_string *attribute_Deprecated_const_MYSQLI_REFRESH_HOSTS_0_arg1_str = zend_string_init("as mysqli_refresh() is deprecated", strlen("as mysqli_refresh() is deprecated"), 1); + ZVAL_STR(&attribute_Deprecated_const_MYSQLI_REFRESH_HOSTS_0_arg1, attribute_Deprecated_const_MYSQLI_REFRESH_HOSTS_0_arg1_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_MYSQLI_REFRESH_HOSTS_0->args[1].value, &attribute_Deprecated_const_MYSQLI_REFRESH_HOSTS_0_arg1); + attribute_Deprecated_const_MYSQLI_REFRESH_HOSTS_0->args[1].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); + zend_constant *const_MYSQLI_REFRESH_STATUS = zend_hash_str_find_ptr(EG(zend_constants), "MYSQLI_REFRESH_STATUS", sizeof("MYSQLI_REFRESH_STATUS") - 1); + + zend_attribute *attribute_Deprecated_const_MYSQLI_REFRESH_STATUS_0 = zend_add_global_constant_attribute(const_MYSQLI_REFRESH_STATUS, ZSTR_KNOWN(ZEND_STR_DEPRECATED_CAPITALIZED), 2); + zval attribute_Deprecated_const_MYSQLI_REFRESH_STATUS_0_arg0; + zend_string *attribute_Deprecated_const_MYSQLI_REFRESH_STATUS_0_arg0_str = zend_string_init("8.4", strlen("8.4"), 1); + ZVAL_STR(&attribute_Deprecated_const_MYSQLI_REFRESH_STATUS_0_arg0, attribute_Deprecated_const_MYSQLI_REFRESH_STATUS_0_arg0_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_MYSQLI_REFRESH_STATUS_0->args[0].value, &attribute_Deprecated_const_MYSQLI_REFRESH_STATUS_0_arg0); + attribute_Deprecated_const_MYSQLI_REFRESH_STATUS_0->args[0].name = ZSTR_KNOWN(ZEND_STR_SINCE); + zval attribute_Deprecated_const_MYSQLI_REFRESH_STATUS_0_arg1; + zend_string *attribute_Deprecated_const_MYSQLI_REFRESH_STATUS_0_arg1_str = zend_string_init("as mysqli_refresh() is deprecated", strlen("as mysqli_refresh() is deprecated"), 1); + ZVAL_STR(&attribute_Deprecated_const_MYSQLI_REFRESH_STATUS_0_arg1, attribute_Deprecated_const_MYSQLI_REFRESH_STATUS_0_arg1_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_MYSQLI_REFRESH_STATUS_0->args[1].value, &attribute_Deprecated_const_MYSQLI_REFRESH_STATUS_0_arg1); + attribute_Deprecated_const_MYSQLI_REFRESH_STATUS_0->args[1].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); + zend_constant *const_MYSQLI_REFRESH_THREADS = zend_hash_str_find_ptr(EG(zend_constants), "MYSQLI_REFRESH_THREADS", sizeof("MYSQLI_REFRESH_THREADS") - 1); + + zend_attribute *attribute_Deprecated_const_MYSQLI_REFRESH_THREADS_0 = zend_add_global_constant_attribute(const_MYSQLI_REFRESH_THREADS, ZSTR_KNOWN(ZEND_STR_DEPRECATED_CAPITALIZED), 2); + zval attribute_Deprecated_const_MYSQLI_REFRESH_THREADS_0_arg0; + zend_string *attribute_Deprecated_const_MYSQLI_REFRESH_THREADS_0_arg0_str = zend_string_init("8.4", strlen("8.4"), 1); + ZVAL_STR(&attribute_Deprecated_const_MYSQLI_REFRESH_THREADS_0_arg0, attribute_Deprecated_const_MYSQLI_REFRESH_THREADS_0_arg0_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_MYSQLI_REFRESH_THREADS_0->args[0].value, &attribute_Deprecated_const_MYSQLI_REFRESH_THREADS_0_arg0); + attribute_Deprecated_const_MYSQLI_REFRESH_THREADS_0->args[0].name = ZSTR_KNOWN(ZEND_STR_SINCE); + zval attribute_Deprecated_const_MYSQLI_REFRESH_THREADS_0_arg1; + zend_string *attribute_Deprecated_const_MYSQLI_REFRESH_THREADS_0_arg1_str = zend_string_init("as mysqli_refresh() is deprecated", strlen("as mysqli_refresh() is deprecated"), 1); + ZVAL_STR(&attribute_Deprecated_const_MYSQLI_REFRESH_THREADS_0_arg1, attribute_Deprecated_const_MYSQLI_REFRESH_THREADS_0_arg1_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_MYSQLI_REFRESH_THREADS_0->args[1].value, &attribute_Deprecated_const_MYSQLI_REFRESH_THREADS_0_arg1); + attribute_Deprecated_const_MYSQLI_REFRESH_THREADS_0->args[1].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); + zend_constant *const_MYSQLI_REFRESH_REPLICA = zend_hash_str_find_ptr(EG(zend_constants), "MYSQLI_REFRESH_REPLICA", sizeof("MYSQLI_REFRESH_REPLICA") - 1); + + zend_attribute *attribute_Deprecated_const_MYSQLI_REFRESH_REPLICA_0 = zend_add_global_constant_attribute(const_MYSQLI_REFRESH_REPLICA, ZSTR_KNOWN(ZEND_STR_DEPRECATED_CAPITALIZED), 2); + zval attribute_Deprecated_const_MYSQLI_REFRESH_REPLICA_0_arg0; + zend_string *attribute_Deprecated_const_MYSQLI_REFRESH_REPLICA_0_arg0_str = zend_string_init("8.4", strlen("8.4"), 1); + ZVAL_STR(&attribute_Deprecated_const_MYSQLI_REFRESH_REPLICA_0_arg0, attribute_Deprecated_const_MYSQLI_REFRESH_REPLICA_0_arg0_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_MYSQLI_REFRESH_REPLICA_0->args[0].value, &attribute_Deprecated_const_MYSQLI_REFRESH_REPLICA_0_arg0); + attribute_Deprecated_const_MYSQLI_REFRESH_REPLICA_0->args[0].name = ZSTR_KNOWN(ZEND_STR_SINCE); + zval attribute_Deprecated_const_MYSQLI_REFRESH_REPLICA_0_arg1; + zend_string *attribute_Deprecated_const_MYSQLI_REFRESH_REPLICA_0_arg1_str = zend_string_init("as mysqli_refresh() is deprecated", strlen("as mysqli_refresh() is deprecated"), 1); + ZVAL_STR(&attribute_Deprecated_const_MYSQLI_REFRESH_REPLICA_0_arg1, attribute_Deprecated_const_MYSQLI_REFRESH_REPLICA_0_arg1_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_MYSQLI_REFRESH_REPLICA_0->args[1].value, &attribute_Deprecated_const_MYSQLI_REFRESH_REPLICA_0_arg1); + attribute_Deprecated_const_MYSQLI_REFRESH_REPLICA_0->args[1].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); + zend_constant *const_MYSQLI_REFRESH_SLAVE = zend_hash_str_find_ptr(EG(zend_constants), "MYSQLI_REFRESH_SLAVE", sizeof("MYSQLI_REFRESH_SLAVE") - 1); + + zend_attribute *attribute_Deprecated_const_MYSQLI_REFRESH_SLAVE_0 = zend_add_global_constant_attribute(const_MYSQLI_REFRESH_SLAVE, ZSTR_KNOWN(ZEND_STR_DEPRECATED_CAPITALIZED), 2); + zval attribute_Deprecated_const_MYSQLI_REFRESH_SLAVE_0_arg0; + zend_string *attribute_Deprecated_const_MYSQLI_REFRESH_SLAVE_0_arg0_str = zend_string_init("8.4", strlen("8.4"), 1); + ZVAL_STR(&attribute_Deprecated_const_MYSQLI_REFRESH_SLAVE_0_arg0, attribute_Deprecated_const_MYSQLI_REFRESH_SLAVE_0_arg0_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_MYSQLI_REFRESH_SLAVE_0->args[0].value, &attribute_Deprecated_const_MYSQLI_REFRESH_SLAVE_0_arg0); + attribute_Deprecated_const_MYSQLI_REFRESH_SLAVE_0->args[0].name = ZSTR_KNOWN(ZEND_STR_SINCE); + zval attribute_Deprecated_const_MYSQLI_REFRESH_SLAVE_0_arg1; + zend_string *attribute_Deprecated_const_MYSQLI_REFRESH_SLAVE_0_arg1_str = zend_string_init("as mysqli_refresh() is deprecated", strlen("as mysqli_refresh() is deprecated"), 1); + ZVAL_STR(&attribute_Deprecated_const_MYSQLI_REFRESH_SLAVE_0_arg1, attribute_Deprecated_const_MYSQLI_REFRESH_SLAVE_0_arg1_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_MYSQLI_REFRESH_SLAVE_0->args[1].value, &attribute_Deprecated_const_MYSQLI_REFRESH_SLAVE_0_arg1); + attribute_Deprecated_const_MYSQLI_REFRESH_SLAVE_0->args[1].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); + zend_constant *const_MYSQLI_REFRESH_MASTER = zend_hash_str_find_ptr(EG(zend_constants), "MYSQLI_REFRESH_MASTER", sizeof("MYSQLI_REFRESH_MASTER") - 1); + + zend_attribute *attribute_Deprecated_const_MYSQLI_REFRESH_MASTER_0 = zend_add_global_constant_attribute(const_MYSQLI_REFRESH_MASTER, ZSTR_KNOWN(ZEND_STR_DEPRECATED_CAPITALIZED), 2); + zval attribute_Deprecated_const_MYSQLI_REFRESH_MASTER_0_arg0; + zend_string *attribute_Deprecated_const_MYSQLI_REFRESH_MASTER_0_arg0_str = zend_string_init("8.4", strlen("8.4"), 1); + ZVAL_STR(&attribute_Deprecated_const_MYSQLI_REFRESH_MASTER_0_arg0, attribute_Deprecated_const_MYSQLI_REFRESH_MASTER_0_arg0_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_MYSQLI_REFRESH_MASTER_0->args[0].value, &attribute_Deprecated_const_MYSQLI_REFRESH_MASTER_0_arg0); + attribute_Deprecated_const_MYSQLI_REFRESH_MASTER_0->args[0].name = ZSTR_KNOWN(ZEND_STR_SINCE); + zval attribute_Deprecated_const_MYSQLI_REFRESH_MASTER_0_arg1; + zend_string *attribute_Deprecated_const_MYSQLI_REFRESH_MASTER_0_arg1_str = zend_string_init("as mysqli_refresh() is deprecated", strlen("as mysqli_refresh() is deprecated"), 1); + ZVAL_STR(&attribute_Deprecated_const_MYSQLI_REFRESH_MASTER_0_arg1, attribute_Deprecated_const_MYSQLI_REFRESH_MASTER_0_arg1_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_MYSQLI_REFRESH_MASTER_0->args[1].value, &attribute_Deprecated_const_MYSQLI_REFRESH_MASTER_0_arg1); + attribute_Deprecated_const_MYSQLI_REFRESH_MASTER_0->args[1].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); + zend_constant *const_MYSQLI_REFRESH_BACKUP_LOG = zend_hash_str_find_ptr(EG(zend_constants), "MYSQLI_REFRESH_BACKUP_LOG", sizeof("MYSQLI_REFRESH_BACKUP_LOG") - 1); + + zend_attribute *attribute_Deprecated_const_MYSQLI_REFRESH_BACKUP_LOG_0 = zend_add_global_constant_attribute(const_MYSQLI_REFRESH_BACKUP_LOG, ZSTR_KNOWN(ZEND_STR_DEPRECATED_CAPITALIZED), 2); + zval attribute_Deprecated_const_MYSQLI_REFRESH_BACKUP_LOG_0_arg0; + zend_string *attribute_Deprecated_const_MYSQLI_REFRESH_BACKUP_LOG_0_arg0_str = zend_string_init("8.4", strlen("8.4"), 1); + ZVAL_STR(&attribute_Deprecated_const_MYSQLI_REFRESH_BACKUP_LOG_0_arg0, attribute_Deprecated_const_MYSQLI_REFRESH_BACKUP_LOG_0_arg0_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_MYSQLI_REFRESH_BACKUP_LOG_0->args[0].value, &attribute_Deprecated_const_MYSQLI_REFRESH_BACKUP_LOG_0_arg0); + attribute_Deprecated_const_MYSQLI_REFRESH_BACKUP_LOG_0->args[0].name = ZSTR_KNOWN(ZEND_STR_SINCE); + zval attribute_Deprecated_const_MYSQLI_REFRESH_BACKUP_LOG_0_arg1; + zend_string *attribute_Deprecated_const_MYSQLI_REFRESH_BACKUP_LOG_0_arg1_str = zend_string_init("as mysqli_refresh() is deprecated", strlen("as mysqli_refresh() is deprecated"), 1); + ZVAL_STR(&attribute_Deprecated_const_MYSQLI_REFRESH_BACKUP_LOG_0_arg1, attribute_Deprecated_const_MYSQLI_REFRESH_BACKUP_LOG_0_arg1_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_MYSQLI_REFRESH_BACKUP_LOG_0->args[1].value, &attribute_Deprecated_const_MYSQLI_REFRESH_BACKUP_LOG_0_arg1); + attribute_Deprecated_const_MYSQLI_REFRESH_BACKUP_LOG_0->args[1].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); + zend_constant *const_MYSQLI_IS_MARIADB = zend_hash_str_find_ptr(EG(zend_constants), "MYSQLI_IS_MARIADB", sizeof("MYSQLI_IS_MARIADB") - 1); + + zend_attribute *attribute_Deprecated_const_MYSQLI_IS_MARIADB_0 = zend_add_global_constant_attribute(const_MYSQLI_IS_MARIADB, ZSTR_KNOWN(ZEND_STR_DEPRECATED_CAPITALIZED), 2); + zval attribute_Deprecated_const_MYSQLI_IS_MARIADB_0_arg0; + zend_string *attribute_Deprecated_const_MYSQLI_IS_MARIADB_0_arg0_str = zend_string_init("8.2", strlen("8.2"), 1); + ZVAL_STR(&attribute_Deprecated_const_MYSQLI_IS_MARIADB_0_arg0, attribute_Deprecated_const_MYSQLI_IS_MARIADB_0_arg0_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_MYSQLI_IS_MARIADB_0->args[0].value, &attribute_Deprecated_const_MYSQLI_IS_MARIADB_0_arg0); + attribute_Deprecated_const_MYSQLI_IS_MARIADB_0->args[0].name = ZSTR_KNOWN(ZEND_STR_SINCE); + zval attribute_Deprecated_const_MYSQLI_IS_MARIADB_0_arg1; + zend_string *attribute_Deprecated_const_MYSQLI_IS_MARIADB_0_arg1_str = zend_string_init("as it is always false", strlen("as it is always false"), 1); + ZVAL_STR(&attribute_Deprecated_const_MYSQLI_IS_MARIADB_0_arg1, attribute_Deprecated_const_MYSQLI_IS_MARIADB_0_arg1_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_MYSQLI_IS_MARIADB_0->args[1].value, &attribute_Deprecated_const_MYSQLI_IS_MARIADB_0_arg1); + attribute_Deprecated_const_MYSQLI_IS_MARIADB_0->args[1].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); } static zend_class_entry *register_class_mysqli_driver(void) diff --git a/ext/mysqli/tests/deprecated_constants.phpt b/ext/mysqli/tests/deprecated_constants.phpt index 9c8e06e50fe1c..753863a3bf131 100644 --- a/ext/mysqli/tests/deprecated_constants.phpt +++ b/ext/mysqli/tests/deprecated_constants.phpt @@ -16,22 +16,22 @@ echo constant('MYSQLI_IS_MARIADB')."\n"; ?> --EXPECTF-- -Deprecated: Constant MYSQLI_NO_DATA is deprecated in %s +Deprecated: Constant MYSQLI_NO_DATA is deprecated since 8.1, as it was unused in %s %i -Deprecated: Constant MYSQLI_DATA_TRUNCATED is deprecated in %s +Deprecated: Constant MYSQLI_DATA_TRUNCATED is deprecated since 8.1, as it was unused in %s %i -Deprecated: Constant MYSQLI_SERVER_QUERY_NO_GOOD_INDEX_USED is deprecated in %s +Deprecated: Constant MYSQLI_SERVER_QUERY_NO_GOOD_INDEX_USED is deprecated since 8.1, as it was unused in %s %i -Deprecated: Constant MYSQLI_SERVER_QUERY_NO_INDEX_USED is deprecated in %s +Deprecated: Constant MYSQLI_SERVER_QUERY_NO_INDEX_USED is deprecated since 8.1, as it was unused in %s %i -Deprecated: Constant MYSQLI_SERVER_QUERY_WAS_SLOW is deprecated in %s +Deprecated: Constant MYSQLI_SERVER_QUERY_WAS_SLOW is deprecated since 8.1, as it was unused in %s %i -Deprecated: Constant MYSQLI_SERVER_PS_OUT_PARAMS is deprecated in %s +Deprecated: Constant MYSQLI_SERVER_PS_OUT_PARAMS is deprecated since 8.1, as it was unused in %s %i -Deprecated: Constant MYSQLI_IS_MARIADB is deprecated in %s +Deprecated: Constant MYSQLI_IS_MARIADB is deprecated since 8.2, as it is always false in %s diff --git a/ext/pgsql/pgsql.stub.php b/ext/pgsql/pgsql.stub.php index 04e648eff8d50..f379d115f6d45 100644 --- a/ext/pgsql/pgsql.stub.php +++ b/ext/pgsql/pgsql.stub.php @@ -13,8 +13,8 @@ /** * @var string * @cvalue pgsql_libpq_version - * @deprecated */ + #[\Deprecated(since: '8.0', message: 'as it is the same as PGSQL_LIBPQ_VERSION')] const PGSQL_LIBPQ_VERSION_STR = UNKNOWN; /* For connection option */ diff --git a/ext/pgsql/pgsql_arginfo.h b/ext/pgsql/pgsql_arginfo.h index cb58645e5e9f5..e42723eef6dd9 100644 --- a/ext/pgsql/pgsql_arginfo.h +++ b/ext/pgsql/pgsql_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 3cf44ca06d11cad086829d3d04900ade3cacb88b */ + * Stub hash: 7c5c32d94c0ac05313d8b19915c6318b0678b75b */ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_pg_connect, 0, 1, PgSql\\Connection, MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, connection_string, IS_STRING, 0) @@ -1153,6 +1153,19 @@ static void register_pgsql_symbols(int module_number) attribute_Deprecated_func_pg_clientencoding_0->args[1].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); zend_add_parameter_attribute(zend_hash_str_find_ptr(CG(function_table), "pg_change_password", sizeof("pg_change_password") - 1), 2, ZSTR_KNOWN(ZEND_STR_SENSITIVEPARAMETER), 0); + zend_constant *const_PGSQL_LIBPQ_VERSION_STR = zend_hash_str_find_ptr(EG(zend_constants), "PGSQL_LIBPQ_VERSION_STR", sizeof("PGSQL_LIBPQ_VERSION_STR") - 1); + + zend_attribute *attribute_Deprecated_const_PGSQL_LIBPQ_VERSION_STR_0 = zend_add_global_constant_attribute(const_PGSQL_LIBPQ_VERSION_STR, ZSTR_KNOWN(ZEND_STR_DEPRECATED_CAPITALIZED), 2); + zval attribute_Deprecated_const_PGSQL_LIBPQ_VERSION_STR_0_arg0; + zend_string *attribute_Deprecated_const_PGSQL_LIBPQ_VERSION_STR_0_arg0_str = zend_string_init("8.0", strlen("8.0"), 1); + ZVAL_STR(&attribute_Deprecated_const_PGSQL_LIBPQ_VERSION_STR_0_arg0, attribute_Deprecated_const_PGSQL_LIBPQ_VERSION_STR_0_arg0_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_PGSQL_LIBPQ_VERSION_STR_0->args[0].value, &attribute_Deprecated_const_PGSQL_LIBPQ_VERSION_STR_0_arg0); + attribute_Deprecated_const_PGSQL_LIBPQ_VERSION_STR_0->args[0].name = ZSTR_KNOWN(ZEND_STR_SINCE); + zval attribute_Deprecated_const_PGSQL_LIBPQ_VERSION_STR_0_arg1; + zend_string *attribute_Deprecated_const_PGSQL_LIBPQ_VERSION_STR_0_arg1_str = zend_string_init("as it is the same as PGSQL_LIBPQ_VERSION", strlen("as it is the same as PGSQL_LIBPQ_VERSION"), 1); + ZVAL_STR(&attribute_Deprecated_const_PGSQL_LIBPQ_VERSION_STR_0_arg1, attribute_Deprecated_const_PGSQL_LIBPQ_VERSION_STR_0_arg1_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_PGSQL_LIBPQ_VERSION_STR_0->args[1].value, &attribute_Deprecated_const_PGSQL_LIBPQ_VERSION_STR_0_arg1); + attribute_Deprecated_const_PGSQL_LIBPQ_VERSION_STR_0->args[1].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); } static zend_class_entry *register_class_PgSql_Connection(void) diff --git a/ext/random/random.stub.php b/ext/random/random.stub.php index b59221bf9180f..2854bdd2c2548 100644 --- a/ext/random/random.stub.php +++ b/ext/random/random.stub.php @@ -10,9 +10,9 @@ const MT_RAND_MT19937 = UNKNOWN; /** * @var int - * @deprecated * @cvalue MT_RAND_PHP */ + #[\Deprecated(since: '8.3', message: 'as it uses a biased non-standard variant of Mt19937')] const MT_RAND_PHP = UNKNOWN; #[\Deprecated(since: '8.4', message: "use \\Random\\Randomizer::getFloat() instead")] diff --git a/ext/random/random_arginfo.h b/ext/random/random_arginfo.h index c1cfb8eb34132..0d7ba3c96524b 100644 --- a/ext/random/random_arginfo.h +++ b/ext/random/random_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 8b30f08404f2912d40f4cb61b76ec283af19b79c */ + * Stub hash: 416be19494555016195600e488d79f0dd35f2620 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_lcg_value, 0, 0, IS_DOUBLE, 0) ZEND_END_ARG_INFO() @@ -240,6 +240,19 @@ static void register_random_symbols(int module_number) ZVAL_STR(&attribute_Deprecated_func_lcg_value_0_arg1, attribute_Deprecated_func_lcg_value_0_arg1_str); ZVAL_COPY_VALUE(&attribute_Deprecated_func_lcg_value_0->args[1].value, &attribute_Deprecated_func_lcg_value_0_arg1); attribute_Deprecated_func_lcg_value_0->args[1].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); + zend_constant *const_MT_RAND_PHP = zend_hash_str_find_ptr(EG(zend_constants), "MT_RAND_PHP", sizeof("MT_RAND_PHP") - 1); + + zend_attribute *attribute_Deprecated_const_MT_RAND_PHP_0 = zend_add_global_constant_attribute(const_MT_RAND_PHP, ZSTR_KNOWN(ZEND_STR_DEPRECATED_CAPITALIZED), 2); + zval attribute_Deprecated_const_MT_RAND_PHP_0_arg0; + zend_string *attribute_Deprecated_const_MT_RAND_PHP_0_arg0_str = zend_string_init("8.3", strlen("8.3"), 1); + ZVAL_STR(&attribute_Deprecated_const_MT_RAND_PHP_0_arg0, attribute_Deprecated_const_MT_RAND_PHP_0_arg0_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_MT_RAND_PHP_0->args[0].value, &attribute_Deprecated_const_MT_RAND_PHP_0_arg0); + attribute_Deprecated_const_MT_RAND_PHP_0->args[0].name = ZSTR_KNOWN(ZEND_STR_SINCE); + zval attribute_Deprecated_const_MT_RAND_PHP_0_arg1; + zend_string *attribute_Deprecated_const_MT_RAND_PHP_0_arg1_str = zend_string_init("as it uses a biased non-standard variant of Mt19937", strlen("as it uses a biased non-standard variant of Mt19937"), 1); + ZVAL_STR(&attribute_Deprecated_const_MT_RAND_PHP_0_arg1, attribute_Deprecated_const_MT_RAND_PHP_0_arg1_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_MT_RAND_PHP_0->args[1].value, &attribute_Deprecated_const_MT_RAND_PHP_0_arg1); + attribute_Deprecated_const_MT_RAND_PHP_0->args[1].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); } static zend_class_entry *register_class_Random_Engine_Mt19937(zend_class_entry *class_entry_Random_Engine) diff --git a/ext/random/tests/01_functions/array_rand_mt_rand_php.phpt b/ext/random/tests/01_functions/array_rand_mt_rand_php.phpt index 703460a890f05..ee3e155484dde 100644 --- a/ext/random/tests/01_functions/array_rand_mt_rand_php.phpt +++ b/ext/random/tests/01_functions/array_rand_mt_rand_php.phpt @@ -24,7 +24,7 @@ var_dump( ); ?> --EXPECTF-- -Deprecated: Constant MT_RAND_PHP is deprecated in %s on line %d +Deprecated: Constant MT_RAND_PHP is deprecated since 8.3, as it uses a biased non-standard variant of Mt19937 in %s on line %d Deprecated: The MT_RAND_PHP variant of Mt19937 is deprecated in %s on line %d string(11) "found key 0" diff --git a/ext/random/tests/01_functions/bug75514.phpt b/ext/random/tests/01_functions/bug75514.phpt index 583ac9a209ef3..8f90064966988 100644 --- a/ext/random/tests/01_functions/bug75514.phpt +++ b/ext/random/tests/01_functions/bug75514.phpt @@ -6,7 +6,7 @@ mt_srand(0, MT_RAND_PHP); var_dump(mt_rand(0,999999999), mt_rand(0,999)); ?> --EXPECTF-- -Deprecated: Constant MT_RAND_PHP is deprecated in %s on line %d +Deprecated: Constant MT_RAND_PHP is deprecated since 8.3, as it uses a biased non-standard variant of Mt19937 in %s on line %d Deprecated: The MT_RAND_PHP variant of Mt19937 is deprecated in %s on line %d int(448865905) diff --git a/ext/random/tests/01_functions/mt_rand_value.phpt b/ext/random/tests/01_functions/mt_rand_value.phpt index ede620648e16b..bf5fdfc2a4e88 100644 --- a/ext/random/tests/01_functions/mt_rand_value.phpt +++ b/ext/random/tests/01_functions/mt_rand_value.phpt @@ -38,7 +38,7 @@ echo $x.PHP_EOL; ?> --EXPECTF-- -Deprecated: Constant MT_RAND_PHP is deprecated in %s on line %d +Deprecated: Constant MT_RAND_PHP is deprecated since 8.3, as it uses a biased non-standard variant of Mt19937 in %s on line %d Deprecated: The MT_RAND_PHP variant of Mt19937 is deprecated in %s on line %d 1614640687 diff --git a/ext/random/tests/03_randomizer/compatibility_mt_rand.phpt b/ext/random/tests/03_randomizer/compatibility_mt_rand.phpt index 4abb1276f3665..50aa7b3efb0d8 100644 --- a/ext/random/tests/03_randomizer/compatibility_mt_rand.phpt +++ b/ext/random/tests/03_randomizer/compatibility_mt_rand.phpt @@ -46,11 +46,11 @@ die('success'); --EXPECTF-- MT_RAND_PHP -Deprecated: Constant MT_RAND_PHP is deprecated in %s on line %d +Deprecated: Constant MT_RAND_PHP is deprecated since 8.3, as it uses a biased non-standard variant of Mt19937 in %s on line %d Deprecated: The MT_RAND_PHP variant of Mt19937 is deprecated in %s on line %d -Deprecated: Constant MT_RAND_PHP is deprecated in %s on line %d +Deprecated: Constant MT_RAND_PHP is deprecated since 8.3, as it uses a biased non-standard variant of Mt19937 in %s on line %d Deprecated: The MT_RAND_PHP variant of Mt19937 is deprecated in %s on line %d MT_RAND_MT19937 diff --git a/ext/random/tests/03_randomizer/methods/getBytes.phpt b/ext/random/tests/03_randomizer/methods/getBytes.phpt index 70aca6e22f8b6..2a163040ac770 100644 --- a/ext/random/tests/03_randomizer/methods/getBytes.phpt +++ b/ext/random/tests/03_randomizer/methods/getBytes.phpt @@ -39,7 +39,7 @@ die('success'); ?> --EXPECTF-- -Deprecated: Constant MT_RAND_PHP is deprecated in %s on line %d +Deprecated: Constant MT_RAND_PHP is deprecated since 8.3, as it uses a biased non-standard variant of Mt19937 in %s on line %d Deprecated: The MT_RAND_PHP variant of Mt19937 is deprecated in %s on line %d Random\Engine\Mt19937 diff --git a/ext/random/tests/03_randomizer/methods/getBytesFromString.phpt b/ext/random/tests/03_randomizer/methods/getBytesFromString.phpt index 06d24cc82bd23..8189d061d9701 100644 --- a/ext/random/tests/03_randomizer/methods/getBytesFromString.phpt +++ b/ext/random/tests/03_randomizer/methods/getBytesFromString.phpt @@ -43,7 +43,7 @@ die('success'); ?> --EXPECTF-- -Deprecated: Constant MT_RAND_PHP is deprecated in %s on line %d +Deprecated: Constant MT_RAND_PHP is deprecated since 8.3, as it uses a biased non-standard variant of Mt19937 in %s on line %d Deprecated: The MT_RAND_PHP variant of Mt19937 is deprecated in %s on line %d Random\Engine\Mt19937 diff --git a/ext/random/tests/03_randomizer/methods/getFloat.phpt b/ext/random/tests/03_randomizer/methods/getFloat.phpt index 8655777f89c3a..c07fedcc210e4 100644 --- a/ext/random/tests/03_randomizer/methods/getFloat.phpt +++ b/ext/random/tests/03_randomizer/methods/getFloat.phpt @@ -42,7 +42,7 @@ die('success'); ?> --EXPECTF-- -Deprecated: Constant MT_RAND_PHP is deprecated in %s on line %d +Deprecated: Constant MT_RAND_PHP is deprecated since 8.3, as it uses a biased non-standard variant of Mt19937 in %s on line %d Deprecated: The MT_RAND_PHP variant of Mt19937 is deprecated in %s on line %d Random\Engine\Mt19937 diff --git a/ext/random/tests/03_randomizer/methods/getInt.phpt b/ext/random/tests/03_randomizer/methods/getInt.phpt index ba936dcf3bf0f..5f1656c615a7f 100644 --- a/ext/random/tests/03_randomizer/methods/getInt.phpt +++ b/ext/random/tests/03_randomizer/methods/getInt.phpt @@ -46,7 +46,7 @@ die('success'); ?> --EXPECTF-- -Deprecated: Constant MT_RAND_PHP is deprecated in %s on line %d +Deprecated: Constant MT_RAND_PHP is deprecated since 8.3, as it uses a biased non-standard variant of Mt19937 in %s on line %d Deprecated: The MT_RAND_PHP variant of Mt19937 is deprecated in %s on line %d Random\Engine\Mt19937 diff --git a/ext/random/tests/03_randomizer/methods/nextFloat.phpt b/ext/random/tests/03_randomizer/methods/nextFloat.phpt index b515242137506..62243faf434a7 100644 --- a/ext/random/tests/03_randomizer/methods/nextFloat.phpt +++ b/ext/random/tests/03_randomizer/methods/nextFloat.phpt @@ -41,7 +41,7 @@ die('success'); ?> --EXPECTF-- -Deprecated: Constant MT_RAND_PHP is deprecated in %s on line %d +Deprecated: Constant MT_RAND_PHP is deprecated since 8.3, as it uses a biased non-standard variant of Mt19937 in %s on line %d Deprecated: The MT_RAND_PHP variant of Mt19937 is deprecated in %s on line %d Random\Engine\Mt19937 diff --git a/ext/random/tests/03_randomizer/methods/nextInt.phpt b/ext/random/tests/03_randomizer/methods/nextInt.phpt index 008e5acb259e8..6739ae9f1cf55 100644 --- a/ext/random/tests/03_randomizer/methods/nextInt.phpt +++ b/ext/random/tests/03_randomizer/methods/nextInt.phpt @@ -41,7 +41,7 @@ die('success'); ?> --EXPECTF-- -Deprecated: Constant MT_RAND_PHP is deprecated in %s on line %d +Deprecated: Constant MT_RAND_PHP is deprecated since 8.3, as it uses a biased non-standard variant of Mt19937 in %s on line %d Deprecated: The MT_RAND_PHP variant of Mt19937 is deprecated in %s on line %d Random\Engine\Mt19937 diff --git a/ext/random/tests/03_randomizer/methods/pickArrayKeys.phpt b/ext/random/tests/03_randomizer/methods/pickArrayKeys.phpt index d5b159f2af136..8c799909c8749 100644 --- a/ext/random/tests/03_randomizer/methods/pickArrayKeys.phpt +++ b/ext/random/tests/03_randomizer/methods/pickArrayKeys.phpt @@ -76,7 +76,7 @@ die('success'); ?> --EXPECTF-- -Deprecated: Constant MT_RAND_PHP is deprecated in %s on line %d +Deprecated: Constant MT_RAND_PHP is deprecated since 8.3, as it uses a biased non-standard variant of Mt19937 in %s on line %d Deprecated: The MT_RAND_PHP variant of Mt19937 is deprecated in %s on line %d Random\Engine\Mt19937 diff --git a/ext/random/tests/03_randomizer/methods/shuffleArray.phpt b/ext/random/tests/03_randomizer/methods/shuffleArray.phpt index af4ce88b3821d..302e683edbf20 100644 --- a/ext/random/tests/03_randomizer/methods/shuffleArray.phpt +++ b/ext/random/tests/03_randomizer/methods/shuffleArray.phpt @@ -45,7 +45,7 @@ die('success'); ?> --EXPECTF-- -Deprecated: Constant MT_RAND_PHP is deprecated in %s on line %d +Deprecated: Constant MT_RAND_PHP is deprecated since 8.3, as it uses a biased non-standard variant of Mt19937 in %s on line %d Deprecated: The MT_RAND_PHP variant of Mt19937 is deprecated in %s on line %d Random\Engine\Mt19937 diff --git a/ext/random/tests/03_randomizer/methods/shuffleBytes.phpt b/ext/random/tests/03_randomizer/methods/shuffleBytes.phpt index 404b670e2863a..cacf3688bb310 100644 --- a/ext/random/tests/03_randomizer/methods/shuffleBytes.phpt +++ b/ext/random/tests/03_randomizer/methods/shuffleBytes.phpt @@ -52,7 +52,7 @@ die('success'); ?> --EXPECTF-- -Deprecated: Constant MT_RAND_PHP is deprecated in %s on line %d +Deprecated: Constant MT_RAND_PHP is deprecated since 8.3, as it uses a biased non-standard variant of Mt19937 in %s on line %d Deprecated: The MT_RAND_PHP variant of Mt19937 is deprecated in %s on line %d Random\Engine\Mt19937 diff --git a/ext/random/tests/03_randomizer/serialize.phpt b/ext/random/tests/03_randomizer/serialize.phpt index 1f56228d33220..4ac8bfe6e682a 100644 --- a/ext/random/tests/03_randomizer/serialize.phpt +++ b/ext/random/tests/03_randomizer/serialize.phpt @@ -43,7 +43,7 @@ foreach ($engines as $engine) { die('success'); ?> --EXPECTF-- -Deprecated: Constant MT_RAND_PHP is deprecated in %s on line %d +Deprecated: Constant MT_RAND_PHP is deprecated since 8.3, as it uses a biased non-standard variant of Mt19937 in %s on line %d Deprecated: The MT_RAND_PHP variant of Mt19937 is deprecated in %s on line %d Random\Engine\Mt19937 diff --git a/ext/reflection/tests/internal_parameter_default_value/check_all.phpt b/ext/reflection/tests/internal_parameter_default_value/check_all.phpt index 534f781c4fc1a..241dea449b615 100644 --- a/ext/reflection/tests/internal_parameter_default_value/check_all.phpt +++ b/ext/reflection/tests/internal_parameter_default_value/check_all.phpt @@ -30,7 +30,7 @@ foreach (get_declared_classes() as $class) { ?> ===DONE=== --EXPECTF-- -Deprecated: Constant SUNFUNCS_RET_STRING is deprecated in %s on line %d +Deprecated: Constant SUNFUNCS_RET_STRING is deprecated since 8.4, as date_sunrise() and date_sunset() were deprecated in 8.1 in %s on line %d -Deprecated: Constant SUNFUNCS_RET_STRING is deprecated in %s on line %d +Deprecated: Constant SUNFUNCS_RET_STRING is deprecated since 8.4, as date_sunrise() and date_sunset() were deprecated in 8.1 in %s on line %d ===DONE=== diff --git a/ext/soap/soap.c b/ext/soap/soap.c index cdb0216ebcf83..23607dd600683 100644 --- a/ext/soap/soap.c +++ b/ext/soap/soap.c @@ -21,6 +21,7 @@ #endif #include "php_soap.h" #include "ext/session/php_session.h" +#include "zend_attributes.h" #include "soap_arginfo.h" #include "zend_exceptions.h" #include "zend_interfaces.h" diff --git a/ext/soap/soap.stub.php b/ext/soap/soap.stub.php index 2520bd1346963..15d4ef1e6bd3e 100644 --- a/ext/soap/soap.stub.php +++ b/ext/soap/soap.stub.php @@ -44,8 +44,8 @@ final class Sdl /** * @var int * @cvalue SOAP_FUNCTIONS_ALL - * @deprecated since 8.4 */ + #[\Deprecated(since: '8.4', message: 'as enabling all functions is a possible security concern')] const SOAP_FUNCTIONS_ALL = UNKNOWN; /** diff --git a/ext/soap/soap_arginfo.h b/ext/soap/soap_arginfo.h index e932f2af71093..c4ce5bf1e58b7 100644 --- a/ext/soap/soap_arginfo.h +++ b/ext/soap/soap_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 78a27b18c6b4007494a6aed9acc5f6e99c6f0350 */ + * Stub hash: 4277993645a3f560c7a9971466fabf2d451bc92d */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_use_soap_error_handler, 0, 0, _IS_BOOL, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, enable, _IS_BOOL, 0, "true") @@ -315,6 +315,20 @@ static void register_soap_symbols(int module_number) REGISTER_LONG_CONSTANT("SOAP_SSL_METHOD_SSLv2", SOAP_SSL_METHOD_SSLv2, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("SOAP_SSL_METHOD_SSLv3", SOAP_SSL_METHOD_SSLv3, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("SOAP_SSL_METHOD_SSLv23", SOAP_SSL_METHOD_SSLv23, CONST_PERSISTENT); + + zend_constant *const_SOAP_FUNCTIONS_ALL = zend_hash_str_find_ptr(EG(zend_constants), "SOAP_FUNCTIONS_ALL", sizeof("SOAP_FUNCTIONS_ALL") - 1); + + zend_attribute *attribute_Deprecated_const_SOAP_FUNCTIONS_ALL_0 = zend_add_global_constant_attribute(const_SOAP_FUNCTIONS_ALL, ZSTR_KNOWN(ZEND_STR_DEPRECATED_CAPITALIZED), 2); + zval attribute_Deprecated_const_SOAP_FUNCTIONS_ALL_0_arg0; + zend_string *attribute_Deprecated_const_SOAP_FUNCTIONS_ALL_0_arg0_str = zend_string_init("8.4", strlen("8.4"), 1); + ZVAL_STR(&attribute_Deprecated_const_SOAP_FUNCTIONS_ALL_0_arg0, attribute_Deprecated_const_SOAP_FUNCTIONS_ALL_0_arg0_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_SOAP_FUNCTIONS_ALL_0->args[0].value, &attribute_Deprecated_const_SOAP_FUNCTIONS_ALL_0_arg0); + attribute_Deprecated_const_SOAP_FUNCTIONS_ALL_0->args[0].name = ZSTR_KNOWN(ZEND_STR_SINCE); + zval attribute_Deprecated_const_SOAP_FUNCTIONS_ALL_0_arg1; + zend_string *attribute_Deprecated_const_SOAP_FUNCTIONS_ALL_0_arg1_str = zend_string_init("as enabling all functions is a possible security concern", strlen("as enabling all functions is a possible security concern"), 1); + ZVAL_STR(&attribute_Deprecated_const_SOAP_FUNCTIONS_ALL_0_arg1, attribute_Deprecated_const_SOAP_FUNCTIONS_ALL_0_arg1_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_SOAP_FUNCTIONS_ALL_0->args[1].value, &attribute_Deprecated_const_SOAP_FUNCTIONS_ALL_0_arg1); + attribute_Deprecated_const_SOAP_FUNCTIONS_ALL_0->args[1].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); } static zend_class_entry *register_class_Soap_Url(void) diff --git a/ext/soap/tests/server003.phpt b/ext/soap/tests/server003.phpt index 1425daf819404..c278ca23a1959 100644 --- a/ext/soap/tests/server003.phpt +++ b/ext/soap/tests/server003.phpt @@ -27,7 +27,7 @@ $server->handle($HTTP_RAW_POST_DATA); echo "ok\n"; ?> --EXPECTF-- -Deprecated: Constant SOAP_FUNCTIONS_ALL is deprecated in %s on line %d +Deprecated: Constant SOAP_FUNCTIONS_ALL is deprecated since 8.4, as enabling all functions is a possible security concern in %s on line %d Deprecated: SoapServer::addFunction(): Enabling all functions via SOAP_FUNCTIONS_ALL is deprecated since 8.4, due to possible security concerns. If all PHP functions should be enabled, the flattened return value of get_defined_functions() can be used in %s on line %d diff --git a/ext/standard/basic_functions.stub.php b/ext/standard/basic_functions.stub.php index 18dc66dcc7648..740d3014f26a7 100644 --- a/ext/standard/basic_functions.stub.php +++ b/ext/standard/basic_functions.stub.php @@ -124,33 +124,33 @@ /** * @var int - * @deprecated * @cvalue PHP_ASSERT_ACTIVE */ +#[\Deprecated(since: '8.3', message: 'as assert_options() is deprecated')] const ASSERT_ACTIVE = UNKNOWN; /** * @var int - * @deprecated * @cvalue PHP_ASSERT_CALLBACK */ +#[\Deprecated(since: '8.3', message: 'as assert_options() is deprecated')] const ASSERT_CALLBACK = UNKNOWN; /** * @var int - * @deprecated * @cvalue PHP_ASSERT_BAIL */ +#[\Deprecated(since: '8.3', message: 'as assert_options() is deprecated')] const ASSERT_BAIL = UNKNOWN; /** * @var int - * @deprecated * @cvalue PHP_ASSERT_WARNING */ +#[\Deprecated(since: '8.3', message: 'as assert_options() is deprecated')] const ASSERT_WARNING = UNKNOWN; /** * @var int - * @deprecated * @cvalue PHP_ASSERT_EXCEPTION */ +#[\Deprecated(since: '8.3', message: 'as assert_options() is deprecated')] const ASSERT_EXCEPTION = UNKNOWN; /* basic_functions.h */ diff --git a/ext/standard/basic_functions_arginfo.h b/ext/standard/basic_functions_arginfo.h index f831e5bdc9b72..a3d965ad99667 100644 --- a/ext/standard/basic_functions_arginfo.h +++ b/ext/standard/basic_functions_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 0ed546c180da5fe4af917086bee80259def15a78 */ + * Stub hash: f8c3745d39ed21f29f46b47f15b6fd1178e55dbb */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_set_time_limit, 0, 1, _IS_BOOL, 0) ZEND_ARG_TYPE_INFO(0, seconds, IS_LONG, 0) @@ -3940,6 +3940,71 @@ static void register_basic_functions_symbols(int module_number) zend_add_parameter_attribute(zend_hash_str_find_ptr(CG(function_table), "password_hash", sizeof("password_hash") - 1), 0, ZSTR_KNOWN(ZEND_STR_SENSITIVEPARAMETER), 0); zend_add_parameter_attribute(zend_hash_str_find_ptr(CG(function_table), "password_verify", sizeof("password_verify") - 1), 0, ZSTR_KNOWN(ZEND_STR_SENSITIVEPARAMETER), 0); + zend_constant *const_ASSERT_ACTIVE = zend_hash_str_find_ptr(EG(zend_constants), "ASSERT_ACTIVE", sizeof("ASSERT_ACTIVE") - 1); + + zend_attribute *attribute_Deprecated_const_ASSERT_ACTIVE_0 = zend_add_global_constant_attribute(const_ASSERT_ACTIVE, ZSTR_KNOWN(ZEND_STR_DEPRECATED_CAPITALIZED), 2); + zval attribute_Deprecated_const_ASSERT_ACTIVE_0_arg0; + zend_string *attribute_Deprecated_const_ASSERT_ACTIVE_0_arg0_str = zend_string_init("8.3", strlen("8.3"), 1); + ZVAL_STR(&attribute_Deprecated_const_ASSERT_ACTIVE_0_arg0, attribute_Deprecated_const_ASSERT_ACTIVE_0_arg0_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_ASSERT_ACTIVE_0->args[0].value, &attribute_Deprecated_const_ASSERT_ACTIVE_0_arg0); + attribute_Deprecated_const_ASSERT_ACTIVE_0->args[0].name = ZSTR_KNOWN(ZEND_STR_SINCE); + zval attribute_Deprecated_const_ASSERT_ACTIVE_0_arg1; + zend_string *attribute_Deprecated_const_ASSERT_ACTIVE_0_arg1_str = zend_string_init("as assert_options() is deprecated", strlen("as assert_options() is deprecated"), 1); + ZVAL_STR(&attribute_Deprecated_const_ASSERT_ACTIVE_0_arg1, attribute_Deprecated_const_ASSERT_ACTIVE_0_arg1_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_ASSERT_ACTIVE_0->args[1].value, &attribute_Deprecated_const_ASSERT_ACTIVE_0_arg1); + attribute_Deprecated_const_ASSERT_ACTIVE_0->args[1].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); + zend_constant *const_ASSERT_CALLBACK = zend_hash_str_find_ptr(EG(zend_constants), "ASSERT_CALLBACK", sizeof("ASSERT_CALLBACK") - 1); + + zend_attribute *attribute_Deprecated_const_ASSERT_CALLBACK_0 = zend_add_global_constant_attribute(const_ASSERT_CALLBACK, ZSTR_KNOWN(ZEND_STR_DEPRECATED_CAPITALIZED), 2); + zval attribute_Deprecated_const_ASSERT_CALLBACK_0_arg0; + zend_string *attribute_Deprecated_const_ASSERT_CALLBACK_0_arg0_str = zend_string_init("8.3", strlen("8.3"), 1); + ZVAL_STR(&attribute_Deprecated_const_ASSERT_CALLBACK_0_arg0, attribute_Deprecated_const_ASSERT_CALLBACK_0_arg0_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_ASSERT_CALLBACK_0->args[0].value, &attribute_Deprecated_const_ASSERT_CALLBACK_0_arg0); + attribute_Deprecated_const_ASSERT_CALLBACK_0->args[0].name = ZSTR_KNOWN(ZEND_STR_SINCE); + zval attribute_Deprecated_const_ASSERT_CALLBACK_0_arg1; + zend_string *attribute_Deprecated_const_ASSERT_CALLBACK_0_arg1_str = zend_string_init("as assert_options() is deprecated", strlen("as assert_options() is deprecated"), 1); + ZVAL_STR(&attribute_Deprecated_const_ASSERT_CALLBACK_0_arg1, attribute_Deprecated_const_ASSERT_CALLBACK_0_arg1_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_ASSERT_CALLBACK_0->args[1].value, &attribute_Deprecated_const_ASSERT_CALLBACK_0_arg1); + attribute_Deprecated_const_ASSERT_CALLBACK_0->args[1].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); + zend_constant *const_ASSERT_BAIL = zend_hash_str_find_ptr(EG(zend_constants), "ASSERT_BAIL", sizeof("ASSERT_BAIL") - 1); + + zend_attribute *attribute_Deprecated_const_ASSERT_BAIL_0 = zend_add_global_constant_attribute(const_ASSERT_BAIL, ZSTR_KNOWN(ZEND_STR_DEPRECATED_CAPITALIZED), 2); + zval attribute_Deprecated_const_ASSERT_BAIL_0_arg0; + zend_string *attribute_Deprecated_const_ASSERT_BAIL_0_arg0_str = zend_string_init("8.3", strlen("8.3"), 1); + ZVAL_STR(&attribute_Deprecated_const_ASSERT_BAIL_0_arg0, attribute_Deprecated_const_ASSERT_BAIL_0_arg0_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_ASSERT_BAIL_0->args[0].value, &attribute_Deprecated_const_ASSERT_BAIL_0_arg0); + attribute_Deprecated_const_ASSERT_BAIL_0->args[0].name = ZSTR_KNOWN(ZEND_STR_SINCE); + zval attribute_Deprecated_const_ASSERT_BAIL_0_arg1; + zend_string *attribute_Deprecated_const_ASSERT_BAIL_0_arg1_str = zend_string_init("as assert_options() is deprecated", strlen("as assert_options() is deprecated"), 1); + ZVAL_STR(&attribute_Deprecated_const_ASSERT_BAIL_0_arg1, attribute_Deprecated_const_ASSERT_BAIL_0_arg1_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_ASSERT_BAIL_0->args[1].value, &attribute_Deprecated_const_ASSERT_BAIL_0_arg1); + attribute_Deprecated_const_ASSERT_BAIL_0->args[1].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); + zend_constant *const_ASSERT_WARNING = zend_hash_str_find_ptr(EG(zend_constants), "ASSERT_WARNING", sizeof("ASSERT_WARNING") - 1); + + zend_attribute *attribute_Deprecated_const_ASSERT_WARNING_0 = zend_add_global_constant_attribute(const_ASSERT_WARNING, ZSTR_KNOWN(ZEND_STR_DEPRECATED_CAPITALIZED), 2); + zval attribute_Deprecated_const_ASSERT_WARNING_0_arg0; + zend_string *attribute_Deprecated_const_ASSERT_WARNING_0_arg0_str = zend_string_init("8.3", strlen("8.3"), 1); + ZVAL_STR(&attribute_Deprecated_const_ASSERT_WARNING_0_arg0, attribute_Deprecated_const_ASSERT_WARNING_0_arg0_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_ASSERT_WARNING_0->args[0].value, &attribute_Deprecated_const_ASSERT_WARNING_0_arg0); + attribute_Deprecated_const_ASSERT_WARNING_0->args[0].name = ZSTR_KNOWN(ZEND_STR_SINCE); + zval attribute_Deprecated_const_ASSERT_WARNING_0_arg1; + zend_string *attribute_Deprecated_const_ASSERT_WARNING_0_arg1_str = zend_string_init("as assert_options() is deprecated", strlen("as assert_options() is deprecated"), 1); + ZVAL_STR(&attribute_Deprecated_const_ASSERT_WARNING_0_arg1, attribute_Deprecated_const_ASSERT_WARNING_0_arg1_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_ASSERT_WARNING_0->args[1].value, &attribute_Deprecated_const_ASSERT_WARNING_0_arg1); + attribute_Deprecated_const_ASSERT_WARNING_0->args[1].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); + zend_constant *const_ASSERT_EXCEPTION = zend_hash_str_find_ptr(EG(zend_constants), "ASSERT_EXCEPTION", sizeof("ASSERT_EXCEPTION") - 1); + + zend_attribute *attribute_Deprecated_const_ASSERT_EXCEPTION_0 = zend_add_global_constant_attribute(const_ASSERT_EXCEPTION, ZSTR_KNOWN(ZEND_STR_DEPRECATED_CAPITALIZED), 2); + zval attribute_Deprecated_const_ASSERT_EXCEPTION_0_arg0; + zend_string *attribute_Deprecated_const_ASSERT_EXCEPTION_0_arg0_str = zend_string_init("8.3", strlen("8.3"), 1); + ZVAL_STR(&attribute_Deprecated_const_ASSERT_EXCEPTION_0_arg0, attribute_Deprecated_const_ASSERT_EXCEPTION_0_arg0_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_ASSERT_EXCEPTION_0->args[0].value, &attribute_Deprecated_const_ASSERT_EXCEPTION_0_arg0); + attribute_Deprecated_const_ASSERT_EXCEPTION_0->args[0].name = ZSTR_KNOWN(ZEND_STR_SINCE); + zval attribute_Deprecated_const_ASSERT_EXCEPTION_0_arg1; + zend_string *attribute_Deprecated_const_ASSERT_EXCEPTION_0_arg1_str = zend_string_init("as assert_options() is deprecated", strlen("as assert_options() is deprecated"), 1); + ZVAL_STR(&attribute_Deprecated_const_ASSERT_EXCEPTION_0_arg1, attribute_Deprecated_const_ASSERT_EXCEPTION_0_arg1_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_ASSERT_EXCEPTION_0->args[1].value, &attribute_Deprecated_const_ASSERT_EXCEPTION_0_arg1); + attribute_Deprecated_const_ASSERT_EXCEPTION_0->args[1].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); } static zend_class_entry *register_class___PHP_Incomplete_Class(void) diff --git a/ext/standard/file.c b/ext/standard/file.c index 4e136bedf5fab..ab6ed4fbadd2d 100644 --- a/ext/standard/file.c +++ b/ext/standard/file.c @@ -98,6 +98,7 @@ php_file_globals file_globals; # include #endif +#include "zend_attributes.h" #include "file_arginfo.h" /* }}} */ diff --git a/ext/standard/file.stub.php b/ext/standard/file.stub.php index 591169a37e001..91d2ea08708ec 100644 --- a/ext/standard/file.stub.php +++ b/ext/standard/file.stub.php @@ -446,13 +446,13 @@ /** * @var int - * @deprecated */ +#[\Deprecated(since: '8.1', message: 'as the constant has no effect')] const FILE_TEXT = 0; /** * @var int - * @deprecated */ +#[\Deprecated(since: '8.1', message: 'as the constant has no effect')] const FILE_BINARY = 0; #ifdef HAVE_FNMATCH diff --git a/ext/standard/file_arginfo.h b/ext/standard/file_arginfo.h index 7dc8fcf80aabe..276419b9abaa7 100644 --- a/ext/standard/file_arginfo.h +++ b/ext/standard/file_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: e9a566d5ef96f781074027b1b3ff1824d0208b47 */ + * Stub hash: dde0b40909dbadb565d898338834de7fa689c5e9 */ static void register_file_symbols(int module_number) { @@ -118,4 +118,31 @@ static void register_file_symbols(int module_number) #if defined(HAVE_FNMATCH) && defined(FNM_CASEFOLD) REGISTER_LONG_CONSTANT("FNM_CASEFOLD", FNM_CASEFOLD, CONST_PERSISTENT); #endif + + zend_constant *const_FILE_TEXT = zend_hash_str_find_ptr(EG(zend_constants), "FILE_TEXT", sizeof("FILE_TEXT") - 1); + + zend_attribute *attribute_Deprecated_const_FILE_TEXT_0 = zend_add_global_constant_attribute(const_FILE_TEXT, ZSTR_KNOWN(ZEND_STR_DEPRECATED_CAPITALIZED), 2); + zval attribute_Deprecated_const_FILE_TEXT_0_arg0; + zend_string *attribute_Deprecated_const_FILE_TEXT_0_arg0_str = zend_string_init("8.1", strlen("8.1"), 1); + ZVAL_STR(&attribute_Deprecated_const_FILE_TEXT_0_arg0, attribute_Deprecated_const_FILE_TEXT_0_arg0_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_FILE_TEXT_0->args[0].value, &attribute_Deprecated_const_FILE_TEXT_0_arg0); + attribute_Deprecated_const_FILE_TEXT_0->args[0].name = ZSTR_KNOWN(ZEND_STR_SINCE); + zval attribute_Deprecated_const_FILE_TEXT_0_arg1; + zend_string *attribute_Deprecated_const_FILE_TEXT_0_arg1_str = zend_string_init("as the constant has no effect", strlen("as the constant has no effect"), 1); + ZVAL_STR(&attribute_Deprecated_const_FILE_TEXT_0_arg1, attribute_Deprecated_const_FILE_TEXT_0_arg1_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_FILE_TEXT_0->args[1].value, &attribute_Deprecated_const_FILE_TEXT_0_arg1); + attribute_Deprecated_const_FILE_TEXT_0->args[1].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); + zend_constant *const_FILE_BINARY = zend_hash_str_find_ptr(EG(zend_constants), "FILE_BINARY", sizeof("FILE_BINARY") - 1); + + zend_attribute *attribute_Deprecated_const_FILE_BINARY_0 = zend_add_global_constant_attribute(const_FILE_BINARY, ZSTR_KNOWN(ZEND_STR_DEPRECATED_CAPITALIZED), 2); + zval attribute_Deprecated_const_FILE_BINARY_0_arg0; + zend_string *attribute_Deprecated_const_FILE_BINARY_0_arg0_str = zend_string_init("8.1", strlen("8.1"), 1); + ZVAL_STR(&attribute_Deprecated_const_FILE_BINARY_0_arg0, attribute_Deprecated_const_FILE_BINARY_0_arg0_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_FILE_BINARY_0->args[0].value, &attribute_Deprecated_const_FILE_BINARY_0_arg0); + attribute_Deprecated_const_FILE_BINARY_0->args[0].name = ZSTR_KNOWN(ZEND_STR_SINCE); + zval attribute_Deprecated_const_FILE_BINARY_0_arg1; + zend_string *attribute_Deprecated_const_FILE_BINARY_0_arg1_str = zend_string_init("as the constant has no effect", strlen("as the constant has no effect"), 1); + ZVAL_STR(&attribute_Deprecated_const_FILE_BINARY_0_arg1, attribute_Deprecated_const_FILE_BINARY_0_arg1_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_FILE_BINARY_0->args[1].value, &attribute_Deprecated_const_FILE_BINARY_0_arg1); + attribute_Deprecated_const_FILE_BINARY_0->args[1].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); } diff --git a/ext/standard/tests/assert/assert.phpt b/ext/standard/tests/assert/assert.phpt index 4649e9a59be94..a275b9949cbe6 100644 --- a/ext/standard/tests/assert/assert.phpt +++ b/ext/standard/tests/assert/assert.phpt @@ -41,25 +41,25 @@ Deprecated: PHP Startup: assert.active INI setting is deprecated in Unknown on l Deprecated: PHP Startup: assert.exception INI setting is deprecated in Unknown on line 0 -Deprecated: Constant ASSERT_ACTIVE is deprecated in %s on line %d +Deprecated: Constant ASSERT_ACTIVE is deprecated since 8.3, as assert_options() is deprecated in %s on line %d Deprecated: Function assert_options() is deprecated since 8.3 in %s on line %d -Deprecated: Constant ASSERT_WARNING is deprecated in %s on line %d +Deprecated: Constant ASSERT_WARNING is deprecated since 8.3, as assert_options() is deprecated in %s on line %d Deprecated: Function assert_options() is deprecated since 8.3 in %s on line %d -Deprecated: Constant ASSERT_CALLBACK is deprecated in %s on line %d +Deprecated: Constant ASSERT_CALLBACK is deprecated since 8.3, as assert_options() is deprecated in %s on line %d Deprecated: Function assert_options() is deprecated since 8.3 in %s on line %d assertion failed 21,"assert($a != 0)" -Deprecated: Constant ASSERT_CALLBACK is deprecated in %s on line %d +Deprecated: Constant ASSERT_CALLBACK is deprecated since 8.3, as assert_options() is deprecated in %s on line %d Deprecated: Function assert_options() is deprecated since 8.3 in %s on line %d class assertion failed 24,"assert($a != 0)" -Deprecated: Constant ASSERT_CALLBACK is deprecated in %s on line %d +Deprecated: Constant ASSERT_CALLBACK is deprecated since 8.3, as assert_options() is deprecated in %s on line %d Deprecated: Function assert_options() is deprecated since 8.3 in %s on line %d class assertion failed 28,"assert($a != 0)" diff --git a/ext/standard/tests/assert/assert03.phpt b/ext/standard/tests/assert/assert03.phpt index 2a62665893a88..968af83065036 100644 --- a/ext/standard/tests/assert/assert03.phpt +++ b/ext/standard/tests/assert/assert03.phpt @@ -36,15 +36,15 @@ Deprecated: PHP Startup: assert.warning INI setting is deprecated in Unknown on Deprecated: PHP Startup: assert.exception INI setting is deprecated in Unknown on line 0 -Deprecated: Constant ASSERT_ACTIVE is deprecated in %s on line %d +Deprecated: Constant ASSERT_ACTIVE is deprecated since 8.3, as assert_options() is deprecated in %s on line %d Deprecated: Function assert_options() is deprecated since 8.3 in %s on line %d -Deprecated: Constant ASSERT_WARNING is deprecated in %s on line %d +Deprecated: Constant ASSERT_WARNING is deprecated since 8.3, as assert_options() is deprecated in %s on line %d Deprecated: Function assert_options() is deprecated since 8.3 in %s on line %d -Deprecated: Constant ASSERT_CALLBACK is deprecated in %s on line %d +Deprecated: Constant ASSERT_CALLBACK is deprecated since 8.3, as assert_options() is deprecated in %s on line %d Deprecated: Function assert_options() is deprecated since 8.3 in %s on line %d assertion failed - a - 18,"assert($a != 0)" diff --git a/ext/standard/tests/assert/assert04.phpt b/ext/standard/tests/assert/assert04.phpt index 9935d4c6a123c..744633c197be2 100644 --- a/ext/standard/tests/assert/assert04.phpt +++ b/ext/standard/tests/assert/assert04.phpt @@ -29,17 +29,17 @@ echo "not reached\n"; --EXPECTF-- Deprecated: PHP Startup: assert.exception INI setting is deprecated in Unknown on line 0 -Deprecated: Constant ASSERT_ACTIVE is deprecated in %s on line %d +Deprecated: Constant ASSERT_ACTIVE is deprecated since 8.3, as assert_options() is deprecated in %s on line %d Deprecated: Function assert_options() is deprecated since 8.3 in %s on line %d -Deprecated: Constant ASSERT_ACTIVE is deprecated in %s on line %d +Deprecated: Constant ASSERT_ACTIVE is deprecated since 8.3, as assert_options() is deprecated in %s on line %d Deprecated: Function assert_options() is deprecated since 8.3 in %s on line %d Warning: assert(): assert(0) failed in %s on line %d -Deprecated: Constant ASSERT_BAIL is deprecated in %s on line %d +Deprecated: Constant ASSERT_BAIL is deprecated since 8.3, as assert_options() is deprecated in %s on line %d Deprecated: Function assert_options() is deprecated since 8.3 in %s on line %d diff --git a/ext/standard/tests/assert/assert_basic2.phpt b/ext/standard/tests/assert/assert_basic2.phpt index fdd1c53b93dde..6bca82a91f536 100644 --- a/ext/standard/tests/assert/assert_basic2.phpt +++ b/ext/standard/tests/assert/assert_basic2.phpt @@ -30,7 +30,7 @@ Deprecated: PHP Startup: assert.callback INI setting is deprecated in Unknown on Deprecated: PHP Startup: assert.exception INI setting is deprecated in Unknown on line 0 -Deprecated: Constant ASSERT_CALLBACK is deprecated in %s on line %d +Deprecated: Constant ASSERT_CALLBACK is deprecated since 8.3, as assert_options() is deprecated in %s on line %d Deprecated: Function assert_options() is deprecated since 8.3 in %s on line %d string(2) "f1" @@ -38,12 +38,12 @@ f1 called Warning: assert(): assert(0) failed in %s on line %d -Deprecated: Constant ASSERT_CALLBACK is deprecated in %s on line %d +Deprecated: Constant ASSERT_CALLBACK is deprecated since 8.3, as assert_options() is deprecated in %s on line %d Deprecated: Function assert_options() is deprecated since 8.3 in %s on line %d string(2) "f1" -Deprecated: Constant ASSERT_CALLBACK is deprecated in %s on line %d +Deprecated: Constant ASSERT_CALLBACK is deprecated since 8.3, as assert_options() is deprecated in %s on line %d Deprecated: Function assert_options() is deprecated since 8.3 in %s on line %d string(2) "f2" diff --git a/ext/standard/tests/assert/assert_basic3.phpt b/ext/standard/tests/assert/assert_basic3.phpt index 0849f16e6f208..4e281ef504245 100644 --- a/ext/standard/tests/assert/assert_basic3.phpt +++ b/ext/standard/tests/assert/assert_basic3.phpt @@ -23,7 +23,7 @@ Deprecated: PHP Startup: assert.callback INI setting is deprecated in Unknown on Deprecated: PHP Startup: assert.exception INI setting is deprecated in Unknown on line 0 -Deprecated: Constant ASSERT_BAIL is deprecated in %s on line %d +Deprecated: Constant ASSERT_BAIL is deprecated since 8.3, as assert_options() is deprecated in %s on line %d Deprecated: Function assert_options() is deprecated since 8.3 in %s on line %d int(0) diff --git a/ext/standard/tests/assert/assert_basic4.phpt b/ext/standard/tests/assert/assert_basic4.phpt index 76ade8d00f403..cc9ec64328866 100644 --- a/ext/standard/tests/assert/assert_basic4.phpt +++ b/ext/standard/tests/assert/assert_basic4.phpt @@ -28,22 +28,22 @@ Deprecated: PHP Startup: assert.warning INI setting is deprecated in Unknown on Deprecated: PHP Startup: assert.callback INI setting is deprecated in Unknown on line 0 -Deprecated: Constant ASSERT_ACTIVE is deprecated in %s on line %d +Deprecated: Constant ASSERT_ACTIVE is deprecated since 8.3, as assert_options() is deprecated in %s on line %d Deprecated: Function assert_options() is deprecated since 8.3 in %s on line %d Initial values: assert_options(ASSERT_ACTIVE) => [0] -Deprecated: Constant ASSERT_WARNING is deprecated in %s on line %d +Deprecated: Constant ASSERT_WARNING is deprecated since 8.3, as assert_options() is deprecated in %s on line %d Deprecated: Function assert_options() is deprecated since 8.3 in %s on line %d Initial values: assert_options(ASSERT_WARNING) => [0] -Deprecated: Constant ASSERT_BAIL is deprecated in %s on line %d +Deprecated: Constant ASSERT_BAIL is deprecated since 8.3, as assert_options() is deprecated in %s on line %d Deprecated: Function assert_options() is deprecated since 8.3 in %s on line %d Initial values: assert_options(ASSERT_BAIL) => [0] -Deprecated: Constant ASSERT_CALLBACK is deprecated in %s on line %d +Deprecated: Constant ASSERT_CALLBACK is deprecated since 8.3, as assert_options() is deprecated in %s on line %d Deprecated: Function assert_options() is deprecated since 8.3 in %s on line %d Initial values: assert_options(ASSERT_CALLBACK) => [f1] diff --git a/ext/standard/tests/assert/assert_basic5.phpt b/ext/standard/tests/assert/assert_basic5.phpt index 6ba70d94cdf36..66858eed5cb81 100644 --- a/ext/standard/tests/assert/assert_basic5.phpt +++ b/ext/standard/tests/assert/assert_basic5.phpt @@ -28,7 +28,7 @@ Deprecated: PHP Startup: assert.callback INI setting is deprecated in Unknown on Deprecated: PHP Startup: assert.exception INI setting is deprecated in Unknown on line 0 -Deprecated: Constant ASSERT_WARNING is deprecated in %s on line %d +Deprecated: Constant ASSERT_WARNING is deprecated since 8.3, as assert_options() is deprecated in %s on line %d Deprecated: Function assert_options() is deprecated since 8.3 in %s on line %d int(0) @@ -38,7 +38,7 @@ Warning: assert(): assert(0 != 0) failed in %s on line %d bool(false) bool(true) -Deprecated: Constant ASSERT_WARNING is deprecated in %s on line %d +Deprecated: Constant ASSERT_WARNING is deprecated since 8.3, as assert_options() is deprecated in %s on line %d Deprecated: Function assert_options() is deprecated since 8.3 in %s on line %d int(1) diff --git a/ext/standard/tests/assert/assert_basic6.phpt b/ext/standard/tests/assert/assert_basic6.phpt index 4e95fdb01dc4c..b531425912c21 100644 --- a/ext/standard/tests/assert/assert_basic6.phpt +++ b/ext/standard/tests/assert/assert_basic6.phpt @@ -32,11 +32,11 @@ try { ?> --EXPECTF-- -Deprecated: Constant ASSERT_CALLBACK is deprecated in %s on line %d +Deprecated: Constant ASSERT_CALLBACK is deprecated since 8.3, as assert_options() is deprecated in %s on line %d Deprecated: Function assert_options() is deprecated since 8.3 in %s on line %d -Deprecated: Constant ASSERT_CALLBACK is deprecated in %s on line %d +Deprecated: Constant ASSERT_CALLBACK is deprecated since 8.3, as assert_options() is deprecated in %s on line %d Deprecated: Function assert_options() is deprecated since 8.3 in %s on line %d string(2) "f1" @@ -44,11 +44,11 @@ foo assert(false) -Deprecated: Constant ASSERT_CALLBACK is deprecated in %s on line %d +Deprecated: Constant ASSERT_CALLBACK is deprecated since 8.3, as assert_options() is deprecated in %s on line %d Deprecated: Function assert_options() is deprecated since 8.3 in %s on line %d -Deprecated: Constant ASSERT_CALLBACK is deprecated in %s on line %d +Deprecated: Constant ASSERT_CALLBACK is deprecated since 8.3, as assert_options() is deprecated in %s on line %d Deprecated: Function assert_options() is deprecated since 8.3 in %s on line %d NULL diff --git a/ext/standard/tests/assert/assert_closures.phpt b/ext/standard/tests/assert/assert_closures.phpt index aa7246ee21bee..ecce049860236 100644 --- a/ext/standard/tests/assert/assert_closures.phpt +++ b/ext/standard/tests/assert/assert_closures.phpt @@ -13,7 +13,7 @@ assert(0); --EXPECTF-- Deprecated: PHP Startup: assert.exception INI setting is deprecated in Unknown on line 0 -Deprecated: Constant ASSERT_CALLBACK is deprecated in %s on line %d +Deprecated: Constant ASSERT_CALLBACK is deprecated since 8.3, as assert_options() is deprecated in %s on line %d Deprecated: Function assert_options() is deprecated since 8.3 in %s on line %d Hello World! diff --git a/ext/standard/tests/assert/assert_closures_multiple.phpt b/ext/standard/tests/assert/assert_closures_multiple.phpt index 0786740155571..8f0cb9551ab93 100644 --- a/ext/standard/tests/assert/assert_closures_multiple.phpt +++ b/ext/standard/tests/assert/assert_closures_multiple.phpt @@ -32,7 +32,7 @@ try { ?> DONE --EXPECTF-- -Deprecated: Constant ASSERT_CALLBACK is deprecated in %s on line %d +Deprecated: Constant ASSERT_CALLBACK is deprecated since 8.3, as assert_options() is deprecated in %s on line %d Deprecated: Function assert_options() is deprecated since 8.3 in %s on line %d DONE diff --git a/ext/standard/tests/assert/assert_error2.phpt b/ext/standard/tests/assert/assert_error2.phpt index eaa62523845d4..6cfa5a6ce4447 100644 --- a/ext/standard/tests/assert/assert_error2.phpt +++ b/ext/standard/tests/assert/assert_error2.phpt @@ -25,7 +25,7 @@ Deprecated: PHP Startup: assert.callback INI setting is deprecated in Unknown on Deprecated: PHP Startup: assert.exception INI setting is deprecated in Unknown on line 0 -Deprecated: Constant ASSERT_BAIL is deprecated in %s on line %d +Deprecated: Constant ASSERT_BAIL is deprecated since 8.3, as assert_options() is deprecated in %s on line %d Deprecated: Function assert_options() is deprecated since 8.3 in %s on line %d int(0) diff --git a/ext/standard/tests/assert/assert_variation.phpt b/ext/standard/tests/assert/assert_variation.phpt index 494f6cd579384..6824f7246d5b6 100644 --- a/ext/standard/tests/assert/assert_variation.phpt +++ b/ext/standard/tests/assert/assert_variation.phpt @@ -85,7 +85,7 @@ Deprecated: PHP Startup: assert.callback INI setting is deprecated in Unknown on Deprecated: PHP Startup: assert.exception INI setting is deprecated in Unknown on line 0 -Deprecated: Constant ASSERT_CALLBACK is deprecated in %s on line %d +Deprecated: Constant ASSERT_CALLBACK is deprecated since 8.3, as assert_options() is deprecated in %s on line %d Deprecated: Function assert_options() is deprecated since 8.3 in %s on line %d Initial values: assert_options(ASSERT_CALLBACK) => [f1] @@ -98,7 +98,7 @@ Change callback function using ini.set and test return value Deprecated: ini_set(): assert.callback INI setting is deprecated in %s on line %d string(2) "f1" -Deprecated: Constant ASSERT_CALLBACK is deprecated in %s on line %d +Deprecated: Constant ASSERT_CALLBACK is deprecated since 8.3, as assert_options() is deprecated in %s on line %d Deprecated: Function assert_options() is deprecated since 8.3 in %s on line %d assert_options(ASSERT_CALLBACK) => [f2] @@ -108,12 +108,12 @@ bool(false) Change callback function using assert_options and test return value -Deprecated: Constant ASSERT_CALLBACK is deprecated in %s on line %d +Deprecated: Constant ASSERT_CALLBACK is deprecated since 8.3, as assert_options() is deprecated in %s on line %d Deprecated: Function assert_options() is deprecated since 8.3 in %s on line %d string(2) "f2" -Deprecated: Constant ASSERT_CALLBACK is deprecated in %s on line %d +Deprecated: Constant ASSERT_CALLBACK is deprecated since 8.3, as assert_options() is deprecated in %s on line %d Deprecated: Function assert_options() is deprecated since 8.3 in %s on line %d assert_options(ASSERT_CALLBACK) => [f3] @@ -123,12 +123,12 @@ bool(false) Reset the name of the callback routine to a class method -Deprecated: Constant ASSERT_CALLBACK is deprecated in %s on line %d +Deprecated: Constant ASSERT_CALLBACK is deprecated since 8.3, as assert_options() is deprecated in %s on line %d Deprecated: Function assert_options() is deprecated since 8.3 in %s on line %d string(2) "f3" -Deprecated: Constant ASSERT_CALLBACK is deprecated in %s on line %d +Deprecated: Constant ASSERT_CALLBACK is deprecated since 8.3, as assert_options() is deprecated in %s on line %d Deprecated: Function assert_options() is deprecated since 8.3 in %s on line %d assert_options(ASSERT_CALLBACK) => [c1] @@ -137,12 +137,12 @@ Invalid callback c1, function "c1" not found or invalid function name Reset callback options to use a class method -Deprecated: Constant ASSERT_CALLBACK is deprecated in %s on line %d +Deprecated: Constant ASSERT_CALLBACK is deprecated since 8.3, as assert_options() is deprecated in %s on line %d Deprecated: Function assert_options() is deprecated since 8.3 in %s on line %d string(2) "c1" -Deprecated: Constant ASSERT_CALLBACK is deprecated in %s on line %d +Deprecated: Constant ASSERT_CALLBACK is deprecated since 8.3, as assert_options() is deprecated in %s on line %d Deprecated: Function assert_options() is deprecated since 8.3 in %s on line %d array(2) { @@ -158,7 +158,7 @@ bool(false) Reset callback options to use an object method -Deprecated: Constant ASSERT_CALLBACK is deprecated in %s on line %d +Deprecated: Constant ASSERT_CALLBACK is deprecated since 8.3, as assert_options() is deprecated in %s on line %d Deprecated: Function assert_options() is deprecated since 8.3 in %s on line %d array(2) { @@ -168,7 +168,7 @@ array(2) { string(6) "assert" } -Deprecated: Constant ASSERT_CALLBACK is deprecated in %s on line %d +Deprecated: Constant ASSERT_CALLBACK is deprecated since 8.3, as assert_options() is deprecated in %s on line %d Deprecated: Function assert_options() is deprecated since 8.3 in %s on line %d array(2) { @@ -185,11 +185,11 @@ bool(false) Set callback to something silly -Deprecated: Constant ASSERT_CALLBACK is deprecated in %s on line %d +Deprecated: Constant ASSERT_CALLBACK is deprecated since 8.3, as assert_options() is deprecated in %s on line %d Deprecated: Function assert_options() is deprecated since 8.3 in %s on line %d -Deprecated: Constant ASSERT_CALLBACK is deprecated in %s on line %d +Deprecated: Constant ASSERT_CALLBACK is deprecated since 8.3, as assert_options() is deprecated in %s on line %d Deprecated: Function assert_options() is deprecated since 8.3 in %s on line %d float(3.141) diff --git a/ext/standard/tests/assert/bug80290.phpt b/ext/standard/tests/assert/bug80290.phpt index 8b737f33043f9..112dbde1a4e47 100644 --- a/ext/standard/tests/assert/bug80290.phpt +++ b/ext/standard/tests/assert/bug80290.phpt @@ -12,7 +12,7 @@ assert(false, 'Dynamic message: ' . $x); ?> --EXPECTF-- -Deprecated: Constant ASSERT_CALLBACK is deprecated in %s on line %d +Deprecated: Constant ASSERT_CALLBACK is deprecated since 8.3, as assert_options() is deprecated in %s on line %d Deprecated: Function assert_options() is deprecated since 8.3 in %s on line %d string(18) "Dynamic message: x" diff --git a/ext/standard/tests/file/file_binary_text_deprecated.phpt b/ext/standard/tests/file/file_binary_text_deprecated.phpt index cbc57809c6175..b6967c5967103 100644 --- a/ext/standard/tests/file/file_binary_text_deprecated.phpt +++ b/ext/standard/tests/file/file_binary_text_deprecated.phpt @@ -8,8 +8,8 @@ var_dump(FILE_TEXT); ?> --EXPECTF-- -Deprecated: Constant FILE_BINARY is deprecated in %s on line %d +Deprecated: Constant FILE_BINARY is deprecated since 8.1, as the constant has no effect in %s on line %d int(0) -Deprecated: Constant FILE_TEXT is deprecated in %s on line %d +Deprecated: Constant FILE_TEXT is deprecated since 8.1, as the constant has no effect in %s on line %d int(0) From 1b7f4567cba06caa89f1a2677ab2c1c6c5c77857 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Thu, 26 Jun 2025 21:38:08 +0100 Subject: [PATCH 179/682] ext/date: Fix tests (#18891) --- ext/date/tests/bug33536.phpt | 4 -- ext/date/tests/bug44780.phpt | 4 +- ext/date/tests/date_sun_info_003.phpt | 57 +++++++++++++++------------ 3 files changed, 33 insertions(+), 32 deletions(-) diff --git a/ext/date/tests/bug33536.phpt b/ext/date/tests/bug33536.phpt index aa5f5ddb38bfe..e41fc7f1e2490 100644 --- a/ext/date/tests/bug33536.phpt +++ b/ext/date/tests/bug33536.phpt @@ -4,10 +4,6 @@ Bug #33456 (strtotime defaults to now even on non time string) --EXPECT-- bool(false) -1970-01-01 -1970-01-01 diff --git a/ext/date/tests/bug44780.phpt b/ext/date/tests/bug44780.phpt index 5c822d48e6add..964c619f9b73f 100644 --- a/ext/date/tests/bug44780.phpt +++ b/ext/date/tests/bug44780.phpt @@ -2,8 +2,8 @@ Bug #44780 (some time zone offsets not recognized by timezone_name_from_abbr) --FILE-- --EXPECT-- string(12) "Asia/Kolkata" diff --git a/ext/date/tests/date_sun_info_003.phpt b/ext/date/tests/date_sun_info_003.phpt index 7e74bab621e41..e3cdc93823f2f 100644 --- a/ext/date/tests/date_sun_info_003.phpt +++ b/ext/date/tests/date_sun_info_003.phpt @@ -5,38 +5,43 @@ edgarsandi - --FILE-- $elem ) { - echo "$key: " . date("H:i:s", $elem) . "\n"; + +function print_sun_info(string $date) { + echo $date, "\n"; + $sun_info = date_sun_info(strtotime($date), 89.00, 1.00); + foreach ($sun_info as $key => $elem ) { + echo "$key: " . match ($elem) { + true => 'always', + false => 'never', + default => date("H:i:s", $elem), + } . "\n"; + } } +print_sun_info("2015-01-12 00:00:00 UTC"); echo "\n"; +print_sun_info("2015-09-12 00:00:00 UTC"); -$sun_info = date_sun_info(strtotime("2015-09-12 00:00:00 UTC"), 89.00, 1.00); -foreach ($sun_info as $key => $elem ) { - echo "$key: " . date("H:i:s", $elem) . "\n"; -} - -echo "Done\n"; ?> --EXPECT-- -sunrise: 21:00:00 -sunset: 21:00:00 +2015-01-12 00:00:00 UTC +sunrise: never +sunset: never transit: 10:03:48 -civil_twilight_begin: 21:00:00 -civil_twilight_end: 21:00:00 -nautical_twilight_begin: 21:00:00 -nautical_twilight_end: 21:00:00 -astronomical_twilight_begin: 21:00:00 -astronomical_twilight_end: 21:00:00 +civil_twilight_begin: never +civil_twilight_end: never +nautical_twilight_begin: never +nautical_twilight_end: never +astronomical_twilight_begin: never +astronomical_twilight_end: never -sunrise: 21:00:01 -sunset: 21:00:01 +2015-09-12 00:00:00 UTC +sunrise: always +sunset: always transit: 08:52:44 -civil_twilight_begin: 21:00:01 -civil_twilight_end: 21:00:01 -nautical_twilight_begin: 21:00:01 -nautical_twilight_end: 21:00:01 -astronomical_twilight_begin: 21:00:01 -astronomical_twilight_end: 21:00:01 -Done +civil_twilight_begin: always +civil_twilight_end: always +nautical_twilight_begin: always +nautical_twilight_end: always +astronomical_twilight_begin: always +astronomical_twilight_end: always From c7c6a79bd0a46a8600cf60b7fd3338d3f58fe825 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Fri, 27 Jun 2025 09:03:50 +0200 Subject: [PATCH 180/682] Add support for ParentNode::$children (#18908) ParentNode::$children returns a HTMLCollection of all directly descendant child elements of a container. I had to move around some properties such that the ParentNode property offsets are always at a fixed offset, to simplify the code. This also adds the necessary code to deal with GC cycles in HTMLCollections. Furthermore, we also disable cloning a HTMLCollection as that never worked and furthermore it also conflicts with the [[SameObject]] WebIDL requirement of $children. --- NEWS | 1 + UPGRADING | 1 + ext/dom/dom_properties.h | 1 + ext/dom/element.c | 19 +-- ext/dom/html_collection.c | 40 ++++++- ext/dom/html_collection.h | 1 + ext/dom/html_document.c | 11 +- ext/dom/obj_map.c | 60 +++++++++- ext/dom/obj_map.h | 1 + ext/dom/parentnode/tree.c | 25 ++++ ext/dom/php_dom.c | 40 ++++++- ext/dom/php_dom.h | 4 + ext/dom/php_dom.stub.php | 96 ++++++++------- ext/dom/php_dom_arginfo.h | 113 +++++++++++------- ext/dom/tests/gh15192.phpt | 4 +- ext/dom/tests/gh16356.phpt | 8 +- .../modern/common/ParentNode_children.phpt | 48 ++++++++ ...should_retain_properties_and_owner_01.phpt | 4 +- ...should_retain_properties_and_owner_02.phpt | 4 +- ...ocument_implementation_createDocument.phpt | 4 +- ...rentNode_append_exception_consistency.phpt | 4 +- .../tests/modern/xml/XMLDocument_debug.phpt | 4 +- .../xml/XMLDocument_fromEmptyDocument_02.phpt | 4 +- ...MLDocument_node_ownerDocument_for_XML.phpt | 4 +- 24 files changed, 372 insertions(+), 129 deletions(-) create mode 100644 ext/dom/tests/modern/common/ParentNode_children.phpt diff --git a/NEWS b/NEWS index fe91a1f30ef74..f3c92852e4db5 100644 --- a/NEWS +++ b/NEWS @@ -74,6 +74,7 @@ PHP NEWS - DOM: . Added Dom\Element::$outerHTML. (nielsdos) . Added Dom\Element::insertAdjacentHTML(). (nielsdos) + . Added $children property to ParentNode implementations. (nielsdos) - Enchant: . Added enchant_dict_remove_from_session(). (nielsdos) diff --git a/UPGRADING b/UPGRADING index 4d968ef4daf80..5491d85f529d8 100644 --- a/UPGRADING +++ b/UPGRADING @@ -185,6 +185,7 @@ PHP 8.5 UPGRADE NOTES - DOM: . Added Dom\Element::$outerHTML. + . Added $children property to Dom\ParentNode implementations. - EXIF: . Add OffsetTime* Exif tags. diff --git a/ext/dom/dom_properties.h b/ext/dom/dom_properties.h index f9402c929d937..100c6c3d3e78b 100644 --- a/ext/dom/dom_properties.h +++ b/ext/dom/dom_properties.h @@ -109,6 +109,7 @@ zend_result dom_entity_reference_child_nodes_read(dom_object *obj, zval *retval) zend_result dom_namednodemap_length_read(dom_object *obj, zval *retval); /* parent node properties */ +zend_result dom_parent_node_children_read(dom_object *obj, zval *retval); zend_result dom_parent_node_first_element_child_read(dom_object *obj, zval *retval); zend_result dom_parent_node_last_element_child_read(dom_object *obj, zval *retval); zend_result dom_parent_node_child_element_count(dom_object *obj, zval *retval); diff --git a/ext/dom/element.c b/ext/dom/element.c index 27e2845c9f0cb..ea18d0a817ede 100644 --- a/ext/dom/element.c +++ b/ext/dom/element.c @@ -177,18 +177,21 @@ zend_result dom_element_class_name_write(dom_object *obj, zval *newval) } /* }}} */ -zval *dom_element_class_list_zval(dom_object *obj) +zval *dom_get_prop_checked_offset(dom_object *obj, uint32_t offset, const char *name) { - const uint32_t PROP_INDEX = 0; - #if ZEND_DEBUG - zend_string *class_list_str = ZSTR_INIT_LITERAL("classList", false); - const zend_property_info *prop_info = zend_get_property_info(dom_modern_element_class_entry, class_list_str, 0); - zend_string_release_ex(class_list_str, false); - ZEND_ASSERT(OBJ_PROP_TO_NUM(prop_info->offset) == PROP_INDEX); + zend_string *name_zstr = ZSTR_INIT_LITERAL(name, false); + const zend_property_info *prop_info = zend_get_property_info(obj->std.ce, name_zstr, 0); + zend_string_release_ex(name_zstr, false); + ZEND_ASSERT(OBJ_PROP_TO_NUM(prop_info->offset) == offset); #endif - return OBJ_PROP_NUM(&obj->std, PROP_INDEX); + return OBJ_PROP_NUM(&obj->std, offset); +} + +zval *dom_element_class_list_zval(dom_object *obj) +{ + return dom_get_prop_checked_offset(obj, 1, "classList"); } /* {{{ classList TokenList diff --git a/ext/dom/html_collection.c b/ext/dom/html_collection.c index e5dca84de75ff..ce56b77ecd958 100644 --- a/ext/dom/html_collection.c +++ b/ext/dom/html_collection.c @@ -50,12 +50,23 @@ static dom_named_item dom_html_collection_named_item(zend_string *key, zend_obje zend_long cur = 0; zend_long next = cur; /* not +1, otherwise we skip the first candidate */ xmlNodePtr candidate = basep->children; + bool iterate_tag_name = objmap->handler == &php_dom_obj_map_by_tag_name; while (candidate != NULL) { - candidate = dom_get_elements_by_tag_name_ns_raw(basep, candidate, objmap->ns, objmap->local, objmap->local_lower, &cur, next); - if (candidate == NULL) { - break; + if (iterate_tag_name) { + candidate = dom_get_elements_by_tag_name_ns_raw(basep, candidate, objmap->ns, objmap->local, objmap->local_lower, &cur, next); + if (candidate == NULL) { + break; + } + next = cur + 1; + } else { + if (candidate->type != XML_ELEMENT_NODE) { + candidate = candidate->next; + continue; + } } + ZEND_ASSERT(candidate->type == XML_ELEMENT_NODE); + xmlAttrPtr attr; /* it has an ID which is key; */ @@ -73,7 +84,9 @@ static dom_named_item dom_html_collection_named_item(zend_string *key, zend_obje } } - next = cur + 1; + if (!iterate_tag_name) { + candidate = candidate->next; + } } } @@ -141,4 +154,23 @@ int dom_html_collection_has_dimension(zend_object *object, zval *member, int che } } +HashTable *dom_html_collection_get_gc(zend_object *object, zval **table, int *n) +{ + dom_nnodemap_object *objmap = php_dom_obj_from_obj(object)->ptr; + + if (objmap->baseobj) { + zend_get_gc_buffer *gc_buffer = zend_get_gc_buffer_create(); + zend_get_gc_buffer_add_obj(gc_buffer, &objmap->baseobj->std); + zend_get_gc_buffer_use(gc_buffer, table, n); + + if (object->properties == NULL && object->ce->default_properties_count == 0) { + return NULL; + } else { + return zend_std_get_properties(object); + } + } else { + return zend_std_get_gc(object, table, n); + } +} + #endif diff --git a/ext/dom/html_collection.h b/ext/dom/html_collection.h index a94daa1aae805..59ab50abc1b05 100644 --- a/ext/dom/html_collection.h +++ b/ext/dom/html_collection.h @@ -19,5 +19,6 @@ zval *dom_html_collection_read_dimension(zend_object *object, zval *offset, int type, zval *rv); int dom_html_collection_has_dimension(zend_object *object, zval *member, int check_empty); +HashTable *dom_html_collection_get_gc(zend_object *object, zval **table, int *n); #endif diff --git a/ext/dom/html_document.c b/ext/dom/html_document.c index 19d7faef0dbb2..954403ca1c5df 100644 --- a/ext/dom/html_document.c +++ b/ext/dom/html_document.c @@ -84,16 +84,7 @@ typedef struct dom_decoding_encoding_ctx { /* https://dom.spec.whatwg.org/#dom-document-implementation */ zend_result dom_modern_document_implementation_read(dom_object *obj, zval *retval) { - const uint32_t PROP_INDEX = 0; - -#if ZEND_DEBUG - zend_string *implementation_str = ZSTR_INIT_LITERAL("implementation", false); - const zend_property_info *prop_info = zend_get_property_info(dom_abstract_base_document_class_entry, implementation_str, 0); - zend_string_release_ex(implementation_str, false); - ZEND_ASSERT(OBJ_PROP_TO_NUM(prop_info->offset) == PROP_INDEX); -#endif - - zval *cached_implementation = OBJ_PROP_NUM(&obj->std, PROP_INDEX); + zval *cached_implementation = dom_get_prop_checked_offset(obj, 1, "implementation"); if (Z_ISUNDEF_P(cached_implementation)) { php_dom_create_implementation(cached_implementation, true); } diff --git a/ext/dom/obj_map.c b/ext/dom/obj_map.c index fe71a6f47a38a..367171c43007e 100644 --- a/ext/dom/obj_map.c +++ b/ext/dom/obj_map.c @@ -28,10 +28,7 @@ static zend_always_inline void objmap_cache_release_cached_obj(dom_nnodemap_object *objmap) { if (objmap->cached_obj) { - /* Since the DOM is a tree there can be no cycles. */ - if (GC_DELREF(&objmap->cached_obj->std) == 0) { - zend_objects_store_del(&objmap->cached_obj->std); - } + OBJ_RELEASE(&objmap->cached_obj->std); objmap->cached_obj = NULL; objmap->cached_obj_index = 0; } @@ -82,6 +79,20 @@ static zend_long dom_map_get_nodes_length(dom_nnodemap_object *map) return count; } +static zend_long dom_map_get_elements_length(dom_nnodemap_object *map) +{ + zend_long count = 0; + xmlNodePtr nodep = dom_object_get_node(map->baseobj); + if (nodep) { + for (xmlNodePtr curnode = dom_nodelist_iter_start_first_child(nodep); curnode; curnode = curnode->next) { + if (curnode->type == XML_ELEMENT_NODE) { + count++; + } + } + } + return count; +} + static zend_long dom_map_get_by_tag_name_length(dom_nnodemap_object *map) { xmlNodePtr nodep = dom_object_get_node(map->baseobj); @@ -223,6 +234,38 @@ static void dom_map_get_nodes_item(dom_nnodemap_object *map, zend_long index, zv } } +static void dom_map_get_elements_item(dom_nnodemap_object *map, zend_long index, zval *return_value) +{ + xmlNodePtr nodep = dom_object_get_node(map->baseobj); + xmlNodePtr itemnode = NULL; + if (nodep && index >= 0) { + dom_node_idx_pair start_point = dom_obj_map_get_start_point(map, nodep, index); + if (start_point.node) { + /* Guaranteed to be an element */ + itemnode = start_point.node; + } else { + /* Fetch first element child */ + itemnode = nodep->children; + while (itemnode && itemnode->type != XML_ELEMENT_NODE) { + itemnode = itemnode->next; + } + } + + for (; start_point.index > 0 && itemnode; --start_point.index) { + do { + itemnode = itemnode->next; + } while (itemnode && itemnode->type != XML_ELEMENT_NODE); + } + if (itemnode && itemnode->type != XML_ELEMENT_NODE) { + itemnode = NULL; + } + } + dom_ret_node_to_zobj(map, itemnode, return_value); + if (itemnode) { + dom_map_cache_obj(map, itemnode, index, return_value); + } +} + static void dom_map_get_by_tag_name_item(dom_nnodemap_object *map, zend_long index, zval *return_value) { xmlNodePtr nodep = dom_object_get_node(map->baseobj); @@ -456,6 +499,15 @@ const php_dom_obj_map_handler php_dom_obj_map_notations = { .nameless = false, }; +const php_dom_obj_map_handler php_dom_obj_map_child_elements = { + .length = dom_map_get_elements_length, + .get_item = dom_map_get_elements_item, + .get_named_item = dom_map_get_named_item_null, + .has_named_item = dom_map_has_named_item_null, + .use_cache = true, + .nameless = true, +}; + const php_dom_obj_map_handler php_dom_obj_map_noop = { .length = dom_map_get_zero_length, .get_item = dom_map_get_null_item, diff --git a/ext/dom/obj_map.h b/ext/dom/obj_map.h index e1de9addd98f2..dc2b33bd24fec 100644 --- a/ext/dom/obj_map.h +++ b/ext/dom/obj_map.h @@ -57,6 +57,7 @@ zend_long php_dom_get_nodelist_length(dom_object *obj); extern const php_dom_obj_map_handler php_dom_obj_map_attributes; extern const php_dom_obj_map_handler php_dom_obj_map_by_tag_name; +extern const php_dom_obj_map_handler php_dom_obj_map_child_elements; extern const php_dom_obj_map_handler php_dom_obj_map_child_nodes; extern const php_dom_obj_map_handler php_dom_obj_map_nodeset; extern const php_dom_obj_map_handler php_dom_obj_map_entities; diff --git a/ext/dom/parentnode/tree.c b/ext/dom/parentnode/tree.c index f57fd1cc7335b..c51bd2753cd8d 100644 --- a/ext/dom/parentnode/tree.c +++ b/ext/dom/parentnode/tree.c @@ -22,9 +22,34 @@ #include "php.h" #if defined(HAVE_LIBXML) && defined(HAVE_DOM) #include "../php_dom.h" +#include "../obj_map.h" #include "../internal_helpers.h" #include "../dom_properties.h" +zval *dom_parent_node_children(dom_object *obj) +{ + return dom_get_prop_checked_offset(obj, 0, "children"); +} + +zend_result dom_parent_node_children_read(dom_object *obj, zval *retval) +{ + zval *cached_children = dom_parent_node_children(obj); + if (Z_ISUNDEF_P(cached_children)) { + object_init_ex(cached_children, dom_html_collection_class_entry); + php_dom_create_obj_map(obj, Z_DOMOBJ_P(cached_children), NULL, NULL, NULL, &php_dom_obj_map_child_elements); + + /* Handle cycles for potential TMPVARs (could also be CV but we can't differentiate). + * RC == 2 because of 1 TMPVAR and 1 in HTMLCollection. */ + if (GC_REFCOUNT(&obj->std) == 2) { + gc_possible_root(Z_COUNTED_P(cached_children)); + } + } + + ZVAL_OBJ_COPY(retval, Z_OBJ_P(cached_children)); + + return SUCCESS; +} + /* {{{ firstElementChild DomParentNode readonly=yes URL: https://www.w3.org/TR/dom/#dom-parentnode-firstelementchild diff --git a/ext/dom/php_dom.c b/ext/dom/php_dom.c index f1571ca78c4f2..5df8a1cb1e1ce 100644 --- a/ext/dom/php_dom.c +++ b/ext/dom/php_dom.c @@ -96,6 +96,7 @@ PHP_DOM_EXPORT zend_class_entry *dom_namespace_info_class_entry; static zend_object_handlers dom_object_handlers; static zend_object_handlers dom_nnodemap_object_handlers; static zend_object_handlers dom_nodelist_object_handlers; +static zend_object_handlers dom_unset_children_property_object_handlers; static zend_object_handlers dom_modern_nnodemap_object_handlers; static zend_object_handlers dom_modern_nodelist_object_handlers; static zend_object_handlers dom_html_collection_object_handlers; @@ -668,14 +669,35 @@ static zend_object *dom_objects_store_clone_obj(zend_object *zobject) /* {{{ */ static zend_object *dom_modern_element_clone_obj(zend_object *zobject) { zend_object *clone = dom_objects_store_clone_obj(zobject); + dom_object *intern = php_dom_obj_from_obj(clone); /* The $classList property is unique per element, and cached due to its [[SameObject]] requirement. * Remove it from the clone so the clone will get a fresh instance upon demand. */ - zval *class_list = dom_element_class_list_zval(php_dom_obj_from_obj(clone)); + zval *class_list = dom_element_class_list_zval(intern); if (!Z_ISUNDEF_P(class_list)) { zval_ptr_dtor(class_list); ZVAL_UNDEF(class_list); } + /* Likewise for $children */ + zval *children = dom_parent_node_children(intern); + if (!Z_ISUNDEF_P(children)) { + zval_ptr_dtor(children); + ZVAL_UNDEF(children); + } + + return clone; +} + +static zend_object *dom_clone_obj_unset_children_property(zend_object *zobject) +{ + zend_object *clone = dom_objects_store_clone_obj(zobject); + dom_object *intern = php_dom_obj_from_obj(clone); + + zval *children = dom_parent_node_children(intern); + if (!Z_ISUNDEF_P(children)) { + zval_ptr_dtor(children); + ZVAL_UNDEF(children); + } return clone; } @@ -777,6 +799,9 @@ PHP_MINIT_FUNCTION(dom) memcpy(&dom_modern_element_object_handlers, &dom_object_handlers, sizeof(zend_object_handlers)); dom_modern_element_object_handlers.clone_obj = dom_modern_element_clone_obj; + memcpy(&dom_unset_children_property_object_handlers, &dom_object_handlers, sizeof(zend_object_handlers)); + dom_unset_children_property_object_handlers.clone_obj = dom_clone_obj_unset_children_property; + memcpy(&dom_nnodemap_object_handlers, &dom_object_handlers, sizeof(zend_object_handlers)); dom_nnodemap_object_handlers.free_obj = dom_nnodemap_objects_free_storage; dom_nnodemap_object_handlers.read_dimension = dom_nodemap_read_dimension; @@ -797,6 +822,8 @@ PHP_MINIT_FUNCTION(dom) memcpy(&dom_html_collection_object_handlers, &dom_modern_nodelist_object_handlers, sizeof(zend_object_handlers)); dom_html_collection_object_handlers.read_dimension = dom_html_collection_read_dimension; dom_html_collection_object_handlers.has_dimension = dom_html_collection_has_dimension; + dom_html_collection_object_handlers.get_gc = dom_html_collection_get_gc; + dom_html_collection_object_handlers.clone_obj = NULL; memcpy(&dom_object_namespace_node_handlers, &dom_object_handlers, sizeof(zend_object_handlers)); dom_object_namespace_node_handlers.offset = XtOffsetOf(dom_object_namespace_node, dom.std); @@ -911,9 +938,10 @@ PHP_MINIT_FUNCTION(dom) dom_modern_documentfragment_class_entry = register_class_Dom_DocumentFragment(dom_modern_node_class_entry, dom_modern_parentnode_class_entry); dom_modern_documentfragment_class_entry->create_object = dom_objects_new; - dom_modern_documentfragment_class_entry->default_object_handlers = &dom_object_handlers; + dom_modern_documentfragment_class_entry->default_object_handlers = &dom_unset_children_property_object_handlers; zend_hash_init(&dom_modern_documentfragment_prop_handlers, 0, NULL, NULL, true); + DOM_REGISTER_PROP_HANDLER(&dom_modern_documentfragment_prop_handlers, "children", dom_parent_node_children_read, NULL); DOM_REGISTER_PROP_HANDLER(&dom_modern_documentfragment_prop_handlers, "firstElementChild", dom_parent_node_first_element_child_read, NULL); DOM_REGISTER_PROP_HANDLER(&dom_modern_documentfragment_prop_handlers, "lastElementChild", dom_parent_node_last_element_child_read, NULL); DOM_REGISTER_PROP_HANDLER(&dom_modern_documentfragment_prop_handlers, "childElementCount", dom_parent_node_child_element_count, NULL); @@ -922,7 +950,7 @@ PHP_MINIT_FUNCTION(dom) zend_hash_add_new_ptr(&classes, dom_modern_documentfragment_class_entry->name, &dom_modern_documentfragment_prop_handlers); dom_abstract_base_document_class_entry = register_class_Dom_Document(dom_modern_node_class_entry, dom_modern_parentnode_class_entry); - dom_abstract_base_document_class_entry->default_object_handlers = &dom_object_handlers; + dom_abstract_base_document_class_entry->default_object_handlers = &dom_unset_children_property_object_handlers; zend_hash_init(&dom_abstract_base_document_prop_handlers, 0, NULL, NULL, true); DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "implementation", dom_modern_document_implementation_read, NULL); DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "URL", dom_document_document_uri_read, dom_document_document_uri_write); @@ -932,6 +960,7 @@ PHP_MINIT_FUNCTION(dom) DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "inputEncoding", dom_document_encoding_read, dom_html_document_encoding_write); DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "doctype", dom_document_doctype_read, NULL); DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "documentElement", dom_document_document_element_read, NULL); + DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "children", dom_parent_node_children_read, NULL); DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "firstElementChild", dom_parent_node_first_element_child_read, NULL); DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "lastElementChild", dom_parent_node_last_element_child_read, NULL); DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "childElementCount", dom_parent_node_child_element_count, NULL); @@ -1118,6 +1147,7 @@ PHP_MINIT_FUNCTION(dom) DOM_REGISTER_PROP_HANDLER(&dom_modern_element_prop_handlers, "className", dom_element_class_name_read, dom_element_class_name_write); DOM_REGISTER_PROP_HANDLER(&dom_modern_element_prop_handlers, "classList", dom_element_class_list_read, NULL); DOM_REGISTER_PROP_HANDLER(&dom_modern_element_prop_handlers, "attributes", dom_node_attributes_read, NULL); + DOM_REGISTER_PROP_HANDLER(&dom_modern_element_prop_handlers, "children", dom_parent_node_children_read, NULL); DOM_REGISTER_PROP_HANDLER(&dom_modern_element_prop_handlers, "firstElementChild", dom_parent_node_first_element_child_read, NULL); DOM_REGISTER_PROP_HANDLER(&dom_modern_element_prop_handlers, "lastElementChild", dom_parent_node_last_element_child_read, NULL); DOM_REGISTER_PROP_HANDLER(&dom_modern_element_prop_handlers, "childElementCount", dom_parent_node_child_element_count, NULL); @@ -1534,8 +1564,8 @@ void dom_nnodemap_objects_free_storage(zend_object *object) /* {{{ */ dom_nnodemap_object *objmap = (dom_nnodemap_object *)intern->ptr; if (objmap) { - if (objmap->cached_obj && GC_DELREF(&objmap->cached_obj->std) == 0) { - zend_objects_store_del(&objmap->cached_obj->std); + if (objmap->cached_obj) { + OBJ_RELEASE(&objmap->cached_obj->std); } if (objmap->release_local) { dom_zend_string_release_from_char_pointer(objmap->local); diff --git a/ext/dom/php_dom.h b/ext/dom/php_dom.h index 0ff8692c4cc74..34cc4af85c568 100644 --- a/ext/dom/php_dom.h +++ b/ext/dom/php_dom.h @@ -160,7 +160,11 @@ bool php_dom_create_nullable_object(xmlNodePtr obj, zval *return_value, dom_obje xmlNodePtr dom_clone_node(php_dom_libxml_ns_mapper *ns_mapper, xmlNodePtr node, xmlDocPtr doc, bool recursive); void dom_set_document_ref_pointers(xmlNodePtr node, php_libxml_ref_obj *document); void dom_set_document_ref_pointers_attr(xmlAttrPtr attr, php_libxml_ref_obj *document); + +/* Prop getters by offset */ +zval *dom_get_prop_checked_offset(dom_object *obj, uint32_t offset, const char *name); zval *dom_element_class_list_zval(dom_object *obj); +zval *dom_parent_node_children(dom_object *obj); typedef enum { DOM_LOAD_STRING = 0, diff --git a/ext/dom/php_dom.stub.php b/ext/dom/php_dom.stub.php index 686ebe87b71ca..fd0927fc0ea12 100644 --- a/ext/dom/php_dom.stub.php +++ b/ext/dom/php_dom.stub.php @@ -1584,6 +1584,36 @@ class Element extends Node implements ParentNode, ChildNode */ public string $tagName; + /** + * @readonly + */ + public HTMLCollection $children; + /** + * @readonly + * @virtual + */ + public ?Element $firstElementChild; + /** + * @readonly + * @virtual + */ + public ?Element $lastElementChild; + /** + * @readonly + * @virtual + */ + public int $childElementCount; + /** + * @readonly + * @virtual + */ + public ?Element $previousElementSibling; + /** + * @readonly + * @virtual + */ + public ?Element $nextElementSibling; + /** @virtual */ public string $id; /** @virtual */ @@ -1634,32 +1664,6 @@ public function insertAdjacentElement(AdjacentPosition $where, Element $element) public function insertAdjacentText(AdjacentPosition $where, string $data): void {} public function insertAdjacentHTML(AdjacentPosition $where, string $string): void {} - /** - * @readonly - * @virtual - */ - public ?Element $firstElementChild; - /** - * @readonly - * @virtual - */ - public ?Element $lastElementChild; - /** - * @readonly - * @virtual - */ - public int $childElementCount; - /** - * @readonly - * @virtual - */ - public ?Element $previousElementSibling; - /** - * @readonly - * @virtual - */ - public ?Element $nextElementSibling; - /** @implementation-alias DOMElement::setIdAttribute */ public function setIdAttribute(string $qualifiedName, bool $isId): void {} /** @implementation-alias DOMElement::setIdAttributeNS */ @@ -1863,6 +1867,10 @@ public function replaceWith(Node|string ...$nodes): void {} class DocumentFragment extends Node implements ParentNode { + /** + * @readonly + */ + public HTMLCollection $children; /** * @readonly * @virtual @@ -1931,6 +1939,26 @@ class Notation extends Node abstract class Document extends Node implements ParentNode { + /** + * @readonly + */ + public HTMLCollection $children; + /** + * @readonly + * @virtual + */ + public ?Element $firstElementChild; + /** + * @readonly + * @virtual + */ + public ?Element $lastElementChild; + /** + * @readonly + * @virtual + */ + public int $childElementCount; + /** @readonly */ public Implementation $implementation; /** @virtual */ @@ -1979,22 +2007,6 @@ public function createAttribute(string $localName): Attr {} /** @implementation-alias DOMDocument::createAttributeNS */ public function createAttributeNS(?string $namespace, string $qualifiedName): Attr {} - /** - * @readonly - * @virtual - */ - public ?Element $firstElementChild; - /** - * @readonly - * @virtual - */ - public ?Element $lastElementChild; - /** - * @readonly - * @virtual - */ - public int $childElementCount; - /** @implementation-alias DOMDocument::getElementById */ public function getElementById(string $elementId): ?Element {} diff --git a/ext/dom/php_dom_arginfo.h b/ext/dom/php_dom_arginfo.h index cf73a85226e4f..922f03240e0f8 100644 --- a/ext/dom/php_dom_arginfo.h +++ b/ext/dom/php_dom_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 1c8b81daeaf360b0ecab9ebbdf4f8865f521f43d */ + * Stub hash: 2119512797f6d51d9835660cd0eccd3ba83417a9 */ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_dom_import_simplexml, 0, 1, DOMAttr|DOMElement, 0) ZEND_ARG_TYPE_INFO(0, node, IS_OBJECT, 0) @@ -3025,31 +3025,12 @@ static zend_class_entry *register_class_Dom_Element(zend_class_entry *class_entr zend_declare_typed_property(class_entry, property_tagName_name, &property_tagName_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_VIRTUAL, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); zend_string_release(property_tagName_name); - zval property_id_default_value; - ZVAL_UNDEF(&property_id_default_value); - zend_string *property_id_name = zend_string_init("id", sizeof("id") - 1, 1); - zend_declare_typed_property(class_entry, property_id_name, &property_id_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_VIRTUAL, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); - zend_string_release(property_id_name); - - zval property_className_default_value; - ZVAL_UNDEF(&property_className_default_value); - zend_string *property_className_name = zend_string_init("className", sizeof("className") - 1, 1); - zend_declare_typed_property(class_entry, property_className_name, &property_className_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_VIRTUAL, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); - zend_string_release(property_className_name); - - zval property_classList_default_value; - ZVAL_UNDEF(&property_classList_default_value); - zend_string *property_classList_name = zend_string_init("classList", sizeof("classList") - 1, 1); - zend_string *property_classList_class_Dom_TokenList = zend_string_init("Dom\\TokenList", sizeof("Dom\\TokenList")-1, 1); - zend_declare_typed_property(class_entry, property_classList_name, &property_classList_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_classList_class_Dom_TokenList, 0, 0)); - zend_string_release(property_classList_name); - - zval property_attributes_default_value; - ZVAL_UNDEF(&property_attributes_default_value); - zend_string *property_attributes_name = zend_string_init("attributes", sizeof("attributes") - 1, 1); - zend_string *property_attributes_class_Dom_NamedNodeMap = zend_string_init("Dom\\\116amedNodeMap", sizeof("Dom\\\116amedNodeMap")-1, 1); - zend_declare_typed_property(class_entry, property_attributes_name, &property_attributes_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_VIRTUAL, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_attributes_class_Dom_NamedNodeMap, 0, 0)); - zend_string_release(property_attributes_name); + zval property_children_default_value; + ZVAL_UNDEF(&property_children_default_value); + zend_string *property_children_name = zend_string_init("children", sizeof("children") - 1, 1); + zend_string *property_children_class_Dom_HTMLCollection = zend_string_init("Dom\\HTMLCollection", sizeof("Dom\\HTMLCollection")-1, 1); + zend_declare_typed_property(class_entry, property_children_name, &property_children_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_children_class_Dom_HTMLCollection, 0, 0)); + zend_string_release(property_children_name); zval property_firstElementChild_default_value; ZVAL_UNDEF(&property_firstElementChild_default_value); @@ -3085,6 +3066,32 @@ static zend_class_entry *register_class_Dom_Element(zend_class_entry *class_entr zend_declare_typed_property(class_entry, property_nextElementSibling_name, &property_nextElementSibling_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_VIRTUAL, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_nextElementSibling_class_Dom_Element, 0, MAY_BE_NULL)); zend_string_release(property_nextElementSibling_name); + zval property_id_default_value; + ZVAL_UNDEF(&property_id_default_value); + zend_string *property_id_name = zend_string_init("id", sizeof("id") - 1, 1); + zend_declare_typed_property(class_entry, property_id_name, &property_id_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_VIRTUAL, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); + zend_string_release(property_id_name); + + zval property_className_default_value; + ZVAL_UNDEF(&property_className_default_value); + zend_string *property_className_name = zend_string_init("className", sizeof("className") - 1, 1); + zend_declare_typed_property(class_entry, property_className_name, &property_className_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_VIRTUAL, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); + zend_string_release(property_className_name); + + zval property_classList_default_value; + ZVAL_UNDEF(&property_classList_default_value); + zend_string *property_classList_name = zend_string_init("classList", sizeof("classList") - 1, 1); + zend_string *property_classList_class_Dom_TokenList = zend_string_init("Dom\\TokenList", sizeof("Dom\\TokenList")-1, 1); + zend_declare_typed_property(class_entry, property_classList_name, &property_classList_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_classList_class_Dom_TokenList, 0, 0)); + zend_string_release(property_classList_name); + + zval property_attributes_default_value; + ZVAL_UNDEF(&property_attributes_default_value); + zend_string *property_attributes_name = zend_string_init("attributes", sizeof("attributes") - 1, 1); + zend_string *property_attributes_class_Dom_NamedNodeMap = zend_string_init("Dom\\\116amedNodeMap", sizeof("Dom\\\116amedNodeMap")-1, 1); + zend_declare_typed_property(class_entry, property_attributes_name, &property_attributes_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_VIRTUAL, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_attributes_class_Dom_NamedNodeMap, 0, 0)); + zend_string_release(property_attributes_name); + zval property_innerHTML_default_value; ZVAL_UNDEF(&property_innerHTML_default_value); zend_string *property_innerHTML_name = zend_string_init("innerHTML", sizeof("innerHTML") - 1, 1); @@ -3309,6 +3316,13 @@ static zend_class_entry *register_class_Dom_DocumentFragment(zend_class_entry *c class_entry = zend_register_internal_class_with_flags(&ce, class_entry_Dom_Node, 0); zend_class_implements(class_entry, 1, class_entry_Dom_ParentNode); + zval property_children_default_value; + ZVAL_UNDEF(&property_children_default_value); + zend_string *property_children_name = zend_string_init("children", sizeof("children") - 1, 1); + zend_string *property_children_class_Dom_HTMLCollection = zend_string_init("Dom\\HTMLCollection", sizeof("Dom\\HTMLCollection")-1, 1); + zend_declare_typed_property(class_entry, property_children_name, &property_children_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_children_class_Dom_HTMLCollection, 0, 0)); + zend_string_release(property_children_name); + zval property_firstElementChild_default_value; ZVAL_UNDEF(&property_firstElementChild_default_value); zend_string *property_firstElementChild_name = zend_string_init("firstElementChild", sizeof("firstElementChild") - 1, 1); @@ -3400,6 +3414,33 @@ static zend_class_entry *register_class_Dom_Document(zend_class_entry *class_ent class_entry = zend_register_internal_class_with_flags(&ce, class_entry_Dom_Node, ZEND_ACC_ABSTRACT); zend_class_implements(class_entry, 1, class_entry_Dom_ParentNode); + zval property_children_default_value; + ZVAL_UNDEF(&property_children_default_value); + zend_string *property_children_name = zend_string_init("children", sizeof("children") - 1, 1); + zend_string *property_children_class_Dom_HTMLCollection = zend_string_init("Dom\\HTMLCollection", sizeof("Dom\\HTMLCollection")-1, 1); + zend_declare_typed_property(class_entry, property_children_name, &property_children_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_children_class_Dom_HTMLCollection, 0, 0)); + zend_string_release(property_children_name); + + zval property_firstElementChild_default_value; + ZVAL_UNDEF(&property_firstElementChild_default_value); + zend_string *property_firstElementChild_name = zend_string_init("firstElementChild", sizeof("firstElementChild") - 1, 1); + zend_string *property_firstElementChild_class_Dom_Element = zend_string_init("Dom\\Element", sizeof("Dom\\Element")-1, 1); + zend_declare_typed_property(class_entry, property_firstElementChild_name, &property_firstElementChild_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_VIRTUAL, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_firstElementChild_class_Dom_Element, 0, MAY_BE_NULL)); + zend_string_release(property_firstElementChild_name); + + zval property_lastElementChild_default_value; + ZVAL_UNDEF(&property_lastElementChild_default_value); + zend_string *property_lastElementChild_name = zend_string_init("lastElementChild", sizeof("lastElementChild") - 1, 1); + zend_string *property_lastElementChild_class_Dom_Element = zend_string_init("Dom\\Element", sizeof("Dom\\Element")-1, 1); + zend_declare_typed_property(class_entry, property_lastElementChild_name, &property_lastElementChild_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_VIRTUAL, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_lastElementChild_class_Dom_Element, 0, MAY_BE_NULL)); + zend_string_release(property_lastElementChild_name); + + zval property_childElementCount_default_value; + ZVAL_UNDEF(&property_childElementCount_default_value); + zend_string *property_childElementCount_name = zend_string_init("childElementCount", sizeof("childElementCount") - 1, 1); + zend_declare_typed_property(class_entry, property_childElementCount_name, &property_childElementCount_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_VIRTUAL, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(property_childElementCount_name); + zval property_implementation_default_value; ZVAL_UNDEF(&property_implementation_default_value); zend_string *property_implementation_name = zend_string_init("implementation", sizeof("implementation") - 1, 1); @@ -3451,26 +3492,6 @@ static zend_class_entry *register_class_Dom_Document(zend_class_entry *class_ent zend_declare_typed_property(class_entry, property_documentElement_name, &property_documentElement_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_VIRTUAL, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_documentElement_class_Dom_Element, 0, MAY_BE_NULL)); zend_string_release(property_documentElement_name); - zval property_firstElementChild_default_value; - ZVAL_UNDEF(&property_firstElementChild_default_value); - zend_string *property_firstElementChild_name = zend_string_init("firstElementChild", sizeof("firstElementChild") - 1, 1); - zend_string *property_firstElementChild_class_Dom_Element = zend_string_init("Dom\\Element", sizeof("Dom\\Element")-1, 1); - zend_declare_typed_property(class_entry, property_firstElementChild_name, &property_firstElementChild_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_VIRTUAL, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_firstElementChild_class_Dom_Element, 0, MAY_BE_NULL)); - zend_string_release(property_firstElementChild_name); - - zval property_lastElementChild_default_value; - ZVAL_UNDEF(&property_lastElementChild_default_value); - zend_string *property_lastElementChild_name = zend_string_init("lastElementChild", sizeof("lastElementChild") - 1, 1); - zend_string *property_lastElementChild_class_Dom_Element = zend_string_init("Dom\\Element", sizeof("Dom\\Element")-1, 1); - zend_declare_typed_property(class_entry, property_lastElementChild_name, &property_lastElementChild_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_VIRTUAL, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_lastElementChild_class_Dom_Element, 0, MAY_BE_NULL)); - zend_string_release(property_lastElementChild_name); - - zval property_childElementCount_default_value; - ZVAL_UNDEF(&property_childElementCount_default_value); - zend_string *property_childElementCount_name = zend_string_init("childElementCount", sizeof("childElementCount") - 1, 1); - zend_declare_typed_property(class_entry, property_childElementCount_name, &property_childElementCount_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_VIRTUAL, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); - zend_string_release(property_childElementCount_name); - zval property_body_default_value; ZVAL_UNDEF(&property_body_default_value); zend_string *property_body_name = zend_string_init("body", sizeof("body") - 1, 1); diff --git a/ext/dom/tests/gh15192.phpt b/ext/dom/tests/gh15192.phpt index c7bf0a543bb93..f6031c2a40b02 100644 --- a/ext/dom/tests/gh15192.phpt +++ b/ext/dom/tests/gh15192.phpt @@ -11,7 +11,7 @@ $dom = new DomDocument(); var_dump($element); ?> --EXPECT-- -object(Dom\HTMLElement)#3 (30) { +object(Dom\HTMLElement)#3 (31) { ["namespaceURI"]=> string(28) "http://www.w3.org/1999/xhtml" ["prefix"]=> @@ -28,6 +28,8 @@ object(Dom\HTMLElement)#3 (30) { string(22) "(object value omitted)" ["attributes"]=> string(22) "(object value omitted)" + ["children"]=> + string(22) "(object value omitted)" ["firstElementChild"]=> string(22) "(object value omitted)" ["lastElementChild"]=> diff --git a/ext/dom/tests/gh16356.phpt b/ext/dom/tests/gh16356.phpt index ad09c2681806e..53d90d8490e29 100644 --- a/ext/dom/tests/gh16356.phpt +++ b/ext/dom/tests/gh16356.phpt @@ -13,7 +13,7 @@ var_dump($e1, $e2); ?> --EXPECT-- -object(Dom\Element)#3 (30) { +object(Dom\Element)#3 (31) { ["namespaceURI"]=> string(12) "urn:example1" ["prefix"]=> @@ -30,6 +30,8 @@ object(Dom\Element)#3 (30) { string(22) "(object value omitted)" ["attributes"]=> string(22) "(object value omitted)" + ["children"]=> + string(22) "(object value omitted)" ["firstElementChild"]=> NULL ["lastElementChild"]=> @@ -75,7 +77,7 @@ object(Dom\Element)#3 (30) { ["textContent"]=> string(0) "" } -object(Dom\Element)#4 (30) { +object(Dom\Element)#4 (31) { ["namespaceURI"]=> string(12) "urn:example2" ["prefix"]=> @@ -92,6 +94,8 @@ object(Dom\Element)#4 (30) { string(22) "(object value omitted)" ["attributes"]=> string(22) "(object value omitted)" + ["children"]=> + string(22) "(object value omitted)" ["firstElementChild"]=> NULL ["lastElementChild"]=> diff --git a/ext/dom/tests/modern/common/ParentNode_children.phpt b/ext/dom/tests/modern/common/ParentNode_children.phpt new file mode 100644 index 0000000000000..54db132f1b161 --- /dev/null +++ b/ext/dom/tests/modern/common/ParentNode_children.phpt @@ -0,0 +1,48 @@ +--TEST-- +ParentNode::$children +--EXTENSIONS-- +dom +--FILE-- + + + + + + + +XML); +$children = $dom->documentElement->children; +var_dump($children === $dom->documentElement->children); // Tests caching behaviour +var_dump($children !== (clone $dom->documentElement)->children); // Tests caching behaviour does not persist across clones +var_dump(count($children)); +var_dump($children->length); + +foreach ($children as $key => $child) { + var_dump($key, $child->nodeName); +} + +foreach ($children->namedItem('foo')->children as $key => $child) { + var_dump($key, $child->nodeName); +} + +?> +--EXPECT-- +bool(true) +bool(true) +int(4) +int(4) +int(0) +string(1) "a" +int(1) +string(1) "b" +int(2) +string(1) "c" +int(3) +string(1) "e" +int(0) +string(2) "c1" +int(1) +string(2) "c2" diff --git a/ext/dom/tests/modern/html/interactions/HTMLDocument_should_retain_properties_and_owner_01.phpt b/ext/dom/tests/modern/html/interactions/HTMLDocument_should_retain_properties_and_owner_01.phpt index 5051c3f9aabf6..c6347ae485894 100644 --- a/ext/dom/tests/modern/html/interactions/HTMLDocument_should_retain_properties_and_owner_01.phpt +++ b/ext/dom/tests/modern/html/interactions/HTMLDocument_should_retain_properties_and_owner_01.phpt @@ -23,7 +23,7 @@ var_dump(get_class($dom->getElementsByTagName("p")->item(0))); ?> --EXPECT-- -object(Dom\HTMLDocument)#1 (28) { +object(Dom\HTMLDocument)#1 (29) { ["implementation"]=> string(22) "(object value omitted)" ["URL"]=> @@ -40,6 +40,8 @@ object(Dom\HTMLDocument)#1 (28) { NULL ["documentElement"]=> string(22) "(object value omitted)" + ["children"]=> + string(22) "(object value omitted)" ["firstElementChild"]=> string(22) "(object value omitted)" ["lastElementChild"]=> diff --git a/ext/dom/tests/modern/html/interactions/HTMLDocument_should_retain_properties_and_owner_02.phpt b/ext/dom/tests/modern/html/interactions/HTMLDocument_should_retain_properties_and_owner_02.phpt index b160c72f0a54f..d7dea308b5d2a 100644 --- a/ext/dom/tests/modern/html/interactions/HTMLDocument_should_retain_properties_and_owner_02.phpt +++ b/ext/dom/tests/modern/html/interactions/HTMLDocument_should_retain_properties_and_owner_02.phpt @@ -23,7 +23,7 @@ var_dump(get_class($dom->getElementsByTagName("p")->item(0))); ?> --EXPECT-- -object(Dom\HTMLDocument)#1 (28) { +object(Dom\HTMLDocument)#1 (29) { ["implementation"]=> string(22) "(object value omitted)" ["URL"]=> @@ -40,6 +40,8 @@ object(Dom\HTMLDocument)#1 (28) { NULL ["documentElement"]=> string(22) "(object value omitted)" + ["children"]=> + string(22) "(object value omitted)" ["firstElementChild"]=> string(22) "(object value omitted)" ["lastElementChild"]=> diff --git a/ext/dom/tests/modern/spec/Document_implementation_createDocument.phpt b/ext/dom/tests/modern/spec/Document_implementation_createDocument.phpt index ae49a6a494c9a..4cf1b3888feed 100644 --- a/ext/dom/tests/modern/spec/Document_implementation_createDocument.phpt +++ b/ext/dom/tests/modern/spec/Document_implementation_createDocument.phpt @@ -37,7 +37,7 @@ echo $dom->implementation->createDocument(null, "", $dtd)->saveXml(), "\n"; ?> --EXPECT-- --- (null, "") --- -object(Dom\XMLDocument)#3 (32) { +object(Dom\XMLDocument)#3 (33) { ["xmlEncoding"]=> string(5) "UTF-8" ["xmlStandalone"]=> @@ -62,6 +62,8 @@ object(Dom\XMLDocument)#3 (32) { NULL ["documentElement"]=> NULL + ["children"]=> + string(22) "(object value omitted)" ["firstElementChild"]=> NULL ["lastElementChild"]=> diff --git a/ext/dom/tests/modern/spec/ParentNode_append_exception_consistency.phpt b/ext/dom/tests/modern/spec/ParentNode_append_exception_consistency.phpt index 67a964a7ec515..a88b4fc808b08 100644 --- a/ext/dom/tests/modern/spec/ParentNode_append_exception_consistency.phpt +++ b/ext/dom/tests/modern/spec/ParentNode_append_exception_consistency.phpt @@ -18,7 +18,9 @@ var_dump($element->parentNode); ?> --EXPECT-- Exception: Cannot have more than one element child in a document -object(Dom\DocumentFragment)#2 (17) { +object(Dom\DocumentFragment)#2 (18) { + ["children"]=> + string(22) "(object value omitted)" ["firstElementChild"]=> string(22) "(object value omitted)" ["lastElementChild"]=> diff --git a/ext/dom/tests/modern/xml/XMLDocument_debug.phpt b/ext/dom/tests/modern/xml/XMLDocument_debug.phpt index e2d6ebffe89cd..7067e5607bdb6 100644 --- a/ext/dom/tests/modern/xml/XMLDocument_debug.phpt +++ b/ext/dom/tests/modern/xml/XMLDocument_debug.phpt @@ -10,7 +10,7 @@ var_dump($dom); ?> --EXPECT-- -object(Dom\XMLDocument)#1 (32) { +object(Dom\XMLDocument)#1 (33) { ["xmlEncoding"]=> string(5) "UTF-8" ["xmlStandalone"]=> @@ -35,6 +35,8 @@ object(Dom\XMLDocument)#1 (32) { NULL ["documentElement"]=> NULL + ["children"]=> + string(22) "(object value omitted)" ["firstElementChild"]=> NULL ["lastElementChild"]=> diff --git a/ext/dom/tests/modern/xml/XMLDocument_fromEmptyDocument_02.phpt b/ext/dom/tests/modern/xml/XMLDocument_fromEmptyDocument_02.phpt index 62d64a05f9b2a..6276d1e9a98cb 100644 --- a/ext/dom/tests/modern/xml/XMLDocument_fromEmptyDocument_02.phpt +++ b/ext/dom/tests/modern/xml/XMLDocument_fromEmptyDocument_02.phpt @@ -10,7 +10,7 @@ var_dump($dom); ?> --EXPECT-- -object(Dom\XMLDocument)#1 (32) { +object(Dom\XMLDocument)#1 (33) { ["xmlEncoding"]=> string(5) "UTF-8" ["xmlStandalone"]=> @@ -35,6 +35,8 @@ object(Dom\XMLDocument)#1 (32) { NULL ["documentElement"]=> NULL + ["children"]=> + string(22) "(object value omitted)" ["firstElementChild"]=> NULL ["lastElementChild"]=> diff --git a/ext/dom/tests/modern/xml/XMLDocument_node_ownerDocument_for_XML.phpt b/ext/dom/tests/modern/xml/XMLDocument_node_ownerDocument_for_XML.phpt index e18c43f05ae82..89cfe83de710f 100644 --- a/ext/dom/tests/modern/xml/XMLDocument_node_ownerDocument_for_XML.phpt +++ b/ext/dom/tests/modern/xml/XMLDocument_node_ownerDocument_for_XML.phpt @@ -13,7 +13,7 @@ var_dump($element->ownerDocument); ?> --EXPECTF-- -object(Dom\XMLDocument)#1 (32) { +object(Dom\XMLDocument)#1 (33) { ["xmlEncoding"]=> string(5) "UTF-8" ["xmlStandalone"]=> @@ -38,6 +38,8 @@ object(Dom\XMLDocument)#1 (32) { NULL ["documentElement"]=> string(22) "(object value omitted)" + ["children"]=> + string(22) "(object value omitted)" ["firstElementChild"]=> string(22) "(object value omitted)" ["lastElementChild"]=> From aee1d7fb964aadfbcf9d421e416e48b3cb17190a Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Fri, 27 Jun 2025 14:02:36 +0200 Subject: [PATCH 181/682] Fix pcntl_rfork / pcntl_forkx with zend-max-execution-timers --- NEWS | 4 ++++ ext/pcntl/pcntl.c | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/NEWS b/NEWS index 624bcda9bdf44..61e1697b62dd9 100644 --- a/NEWS +++ b/NEWS @@ -26,6 +26,10 @@ PHP NEWS . Fixed bug GH-14082 (Segmentation fault on unknown address 0x600000000018 in ext/opcache/jit/zend_jit.c). (nielsdos) +- PCNTL: + . Fixed bug GH-18958 (Fatal error during shutdown after pcntl_rfork() or + pcntl_forkx() with zend-max-execution-timers). (Arnaud) + - Standard: . Fix misleading errors in printf(). (nielsdos) . Fix RCN violations in array functions. (nielsdos) diff --git a/ext/pcntl/pcntl.c b/ext/pcntl/pcntl.c index adb94af2fb463..16f29d419e3da 100644 --- a/ext/pcntl/pcntl.c +++ b/ext/pcntl/pcntl.c @@ -1277,6 +1277,8 @@ PHP_FUNCTION(pcntl_rfork) default: php_error_docref(NULL, E_WARNING, "Error %d", errno); } + } else if (pid == 0) { + zend_max_execution_timer_init(); } RETURN_LONG((zend_long) pid); @@ -1320,6 +1322,8 @@ PHP_FUNCTION(pcntl_forkx) default: php_error_docref(NULL, E_WARNING, "Error %d", errno); } + } else if (pid == 0) { + zend_max_execution_timer_init(); } RETURN_LONG((zend_long) pid); From 25e1674fbe0f065ed4debe94e5a4fe9f91676891 Mon Sep 17 00:00:00 2001 From: Dmitrii Derepko Date: Fri, 27 Jun 2025 22:59:46 +0400 Subject: [PATCH 182/682] [skip ci] Add editor config dirs to gitignore (GH-18669) --- .gitignore | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.gitignore b/.gitignore index 3c4566ed663c3..1e92e88fb77fa 100644 --- a/.gitignore +++ b/.gitignore @@ -294,6 +294,13 @@ tmp-php.ini /junit.out.xml /.ccache/ +# ------------------------------------------------------------------------------ +# Editor configuration directories +# ------------------------------------------------------------------------------ +/.idea/ +/.vscode/ +/.zed/ + # ------------------------------------------------------------------------------ # Additional test build files # ------------------------------------------------------------------------------ From 22e444c5c7d8b2890ff60802ba8f65aa28dab960 Mon Sep 17 00:00:00 2001 From: Dmitrii Derepko Date: Fri, 27 Jun 2025 23:33:49 +0400 Subject: [PATCH 183/682] [skip ci] README: Add macos instruction + other tweaks (GH-18670) --- README.md | 60 ++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 46 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index c5376f67725cd..587cc9bd2dc54 100644 --- a/README.md +++ b/README.md @@ -42,28 +42,50 @@ a default build, you will additionally need libxml2 and libsqlite3. On Ubuntu, you can install these using: - sudo apt install -y pkg-config build-essential autoconf bison re2c \ - libxml2-dev libsqlite3-dev +```shell +sudo apt install -y pkg-config build-essential autoconf bison re2c libxml2-dev libsqlite3-dev +``` On Fedora, you can install these using: - sudo dnf install re2c bison autoconf make libtool ccache libxml2-devel sqlite-devel +```shell +sudo dnf install re2c bison autoconf make libtool ccache libxml2-devel sqlite-devel +``` + +On MacOS, you can install these using `brew`: + +```shell +brew install autoconf bison re2c iconv libxml2 sqlite +``` + +or with `MacPorts`: + +```shell +sudo port install autoconf bison re2c libiconv libxml2 sqlite3 +``` Generate configure: - ./buildconf +```shell +./buildconf +``` Configure your build. `--enable-debug` is recommended for development, see `./configure --help` for a full list of options. - # For development - ./configure --enable-debug - # For production - ./configure +```shell +# For development +./configure --enable-debug +# For production +./configure +``` -Build PHP. To speed up the build, specify the maximum number of jobs using `-j`: +Build PHP. To speed up the build, specify the maximum number of jobs using the +`-j` argument: - make -j4 +```shell +make -j4 +``` The number of jobs should usually match the number of available cores, which can be determined using `nproc`. @@ -74,13 +96,21 @@ PHP ships with an extensive test suite, the command `make test` is used after successful compilation of the sources to run this test suite. It is possible to run tests using multiple cores by setting `-jN` in -`TEST_PHP_ARGS`: +`TEST_PHP_ARGS` or `TESTS`: - make TEST_PHP_ARGS=-j4 test +```shell +make TEST_PHP_ARGS=-j4 test +``` Shall run `make test` with a maximum of 4 concurrent jobs: Generally the maximum number of jobs should not exceed the number of cores available. +Use the `TEST_PHP_ARGS` or `TESTS` variable to test only specific directories: + +```shell +make TESTS=tests/lang/ test +``` + The [qa.php.net](https://qa.php.net) site provides more detailed info about testing and quality assurance. @@ -88,9 +118,11 @@ testing and quality assurance. After a successful build (and test), PHP may be installed with: - make install +```shell +make install +``` -Depending on your permissions and prefix, `make install` may need super user +Depending on your permissions and prefix, `make install` may need superuser permissions. ## PHP extensions From 44955943f9319493b7e744a1ae913977fef80db3 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sat, 28 Jun 2025 10:12:20 +0200 Subject: [PATCH 184/682] ext/intl: Use zval_get_tmp_string where possible (#18966) --- ext/intl/listformatter/listformatter_class.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ext/intl/listformatter/listformatter_class.c b/ext/intl/listformatter/listformatter_class.c index 1fe8da554a1ca..f1a5039079b8a 100644 --- a/ext/intl/listformatter/listformatter_class.c +++ b/ext/intl/listformatter/listformatter_class.c @@ -135,9 +135,9 @@ PHP_METHOD(IntlListFormatter, format) zval *val; ZEND_HASH_FOREACH_VAL(ht, val) { - zend_string *str_val; + zend_string *str_val, *tmp_str; - str_val = zval_get_string(val); + str_val = zval_get_tmp_string(val, &tmp_str); // Convert PHP string to UTF-16 UChar *ustr = NULL; @@ -145,7 +145,7 @@ PHP_METHOD(IntlListFormatter, format) UErrorCode status = U_ZERO_ERROR; intl_convert_utf8_to_utf16(&ustr, &ustr_len, ZSTR_VAL(str_val), ZSTR_LEN(str_val), &status); - zend_string_release(str_val); + zend_tmp_string_release(tmp_str); if (U_FAILURE(status)) { // We can't use goto cleanup because items and itemLengths are incompletely allocated From 737db4a7dced9a047407b5ab889a86c7528943dd Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sat, 28 Jun 2025 10:12:43 +0200 Subject: [PATCH 185/682] Use hasThis() where appropriate (#18967) This macro was introduced to solve false compilers warning about the getThis() condition not making sense for the address-taken part of the ternary. --- ext/intl/formatter/formatter_format.c | 2 +- ext/intl/formatter/formatter_parse.c | 2 +- ext/mysqli/mysqli_api.c | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ext/intl/formatter/formatter_format.c b/ext/intl/formatter/formatter_format.c index 0323757ed8620..54c5d92fe18a1 100644 --- a/ext/intl/formatter/formatter_format.c +++ b/ext/intl/formatter/formatter_format.c @@ -104,7 +104,7 @@ PHP_FUNCTION( numfmt_format ) INTL_METHOD_CHECK_STATUS( nfo, "Number formatting failed" ); break; case FORMAT_TYPE_CURRENCY: - if (getThis()) { + if (hasThis()) { const char *space; const char *class_name = get_active_class_name(&space); zend_argument_value_error(2, "cannot be NumberFormatter::TYPE_CURRENCY constant, " diff --git a/ext/intl/formatter/formatter_parse.c b/ext/intl/formatter/formatter_parse.c index 9939900650408..ba8307419b4cf 100644 --- a/ext/intl/formatter/formatter_parse.c +++ b/ext/intl/formatter/formatter_parse.c @@ -86,7 +86,7 @@ PHP_FUNCTION( numfmt_parse ) RETVAL_DOUBLE(val_double); break; case FORMAT_TYPE_CURRENCY: - if (getThis()) { + if (hasThis()) { const char *space; const char *class_name = get_active_class_name(&space); zend_argument_value_error(2, "cannot be NumberFormatter::TYPE_CURRENCY constant, " diff --git a/ext/mysqli/mysqli_api.c b/ext/mysqli/mysqli_api.c index 5e2645740b26e..2a20919eee45e 100644 --- a/ext/mysqli/mysqli_api.c +++ b/ext/mysqli/mysqli_api.c @@ -327,7 +327,7 @@ PHP_FUNCTION(mysqli_data_seek) MYSQLI_FETCH_RESOURCE(result, MYSQL_RES *, mysql_result, "mysqli_result", MYSQLI_STATUS_VALID); if (mysqli_result_is_unbuffered(result)) { - if (getThis()) { + if (hasThis()) { zend_throw_error(NULL, "mysqli_result::data_seek() cannot be used in MYSQLI_USE_RESULT mode"); } else { zend_throw_error(NULL, "mysqli_data_seek() cannot be used in MYSQLI_USE_RESULT mode"); @@ -855,7 +855,7 @@ PHP_FUNCTION(mysqli_free_result) /* {{{ Get MySQL client info */ PHP_FUNCTION(mysqli_get_client_info) { - if (getThis()) { + if (hasThis()) { if (zend_parse_parameters_none() == FAILURE) { RETURN_THROWS(); } From 984bcb5d69e7ad79621f19d22e8817b7acabec19 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sat, 28 Jun 2025 14:04:39 +0200 Subject: [PATCH 186/682] ext/mysqli: Get rid of calls to strcpy (#18970) strcpy is a dangerous API that should be avoided. --- ext/mysqli/mysqli_report.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ext/mysqli/mysqli_report.c b/ext/mysqli/mysqli_report.c index 68a7a57584750..06611310b0b0c 100644 --- a/ext/mysqli/mysqli_report.c +++ b/ext/mysqli/mysqli_report.c @@ -48,12 +48,12 @@ void php_mysqli_report_error(const char *sqlstate, int errorno, const char *erro /* {{{ void php_mysqli_report_index() */ void php_mysqli_report_index(const char *query, unsigned int status) { - char index[15]; + const char *index; if (status & SERVER_QUERY_NO_GOOD_INDEX_USED) { - strcpy(index, "Bad index"); + index = "Bad index"; } else if (status & SERVER_QUERY_NO_INDEX_USED) { - strcpy(index, "No index"); + index = "No index"; } else { return; } From fffe642d67bc5f11f353562cc4032b7eac185a8e Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Sat, 28 Jun 2025 20:16:12 +0200 Subject: [PATCH 187/682] Remove HAVE_PTRDIFF_T and SIZEOF_PTRDIFF_T (#18968) The ptrdiff_t is a C89 standard type defined in `` and widely available on current platforms. Using it conditionally as in these occurrences is not needed anymore. --- UPGRADING.INTERNALS | 4 ++++ configure.ac | 1 - ext/intl/collator/collator_sort.c | 4 ---- main/snprintf.c | 12 ------------ main/snprintf.h | 2 -- main/spprintf.c | 12 ------------ win32/build/config.w32.h.in | 2 -- 7 files changed, 4 insertions(+), 33 deletions(-) diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS index acc9612b72e30..47cae00b0955e 100644 --- a/UPGRADING.INTERNALS +++ b/UPGRADING.INTERNALS @@ -50,6 +50,9 @@ PHP 8.5 INTERNALS UPGRADE NOTES 2. Build system changes ======================== +- Abstract + . Preprocessor macro SIZEOF_PTRDIFF_T has been removed. + - Windows build system changes . SAPI() and ADD_SOURCES() now suport the optional `duplicate_sources` parameter. If truthy, no rules to build the object files are generated. @@ -69,6 +72,7 @@ PHP 8.5 INTERNALS UPGRADE NOTES . Autoconf macro PHP_DEF_HAVE has been removed (use AC_DEFINE). . Autoconf macro PHP_OUTPUT has been removed (use AC_CONFIG_FILES). . Autoconf macro PHP_TEST_BUILD has been removed (use AC_* macros). + . Preprocessor macro HAVE_PTRDIFF_T has been removed. ======================== 3. Module changes diff --git a/configure.ac b/configure.ac index e4bd8162a2ebc..f6f305ba053a8 100644 --- a/configure.ac +++ b/configure.ac @@ -454,7 +454,6 @@ AC_CHECK_TYPES([socklen_t], [], [], [ dnl These are defined elsewhere than stdio.h. PHP_CHECK_SIZEOF([intmax_t], [0]) PHP_CHECK_SIZEOF([ssize_t], [8]) -PHP_CHECK_SIZEOF([ptrdiff_t], [8]) dnl Check stdint types (must be after header check). PHP_CHECK_STDINT_TYPES diff --git a/ext/intl/collator/collator_sort.c b/ext/intl/collator/collator_sort.c index 75466aacb07af..ee68a21c98960 100644 --- a/ext/intl/collator/collator_sort.c +++ b/ext/intl/collator/collator_sort.c @@ -24,10 +24,6 @@ #include "collator_convert.h" #include "intl_convert.h" -#if !defined(HAVE_PTRDIFF_T) && !defined(_PTRDIFF_T_DEFINED) -typedef zend_long ptrdiff_t; -#endif - /** * Declare 'index' which will point to sort key in sort key * buffer. diff --git a/main/snprintf.c b/main/snprintf.c index de69200304a43..2bc354fec284d 100644 --- a/main/snprintf.c +++ b/main/snprintf.c @@ -621,11 +621,7 @@ static size_t format_converter(buffy * odp, const char *fmt, va_list ap) /* {{{ break; case 't': fmt++; -#if SIZEOF_PTRDIFF_T modifier = LM_PTRDIFF_T; -#else - modifier = LM_SIZE_T; -#endif break; case 'p': { @@ -694,11 +690,9 @@ static size_t format_converter(buffy * odp, const char *fmt, va_list ap) /* {{{ i_num = (int64_t) va_arg(ap, uintmax_t); break; #endif -#if SIZEOF_PTRDIFF_T case LM_PTRDIFF_T: i_num = (int64_t) va_arg(ap, ptrdiff_t); break; -#endif } /* * The rest also applies to other integer formats, so fall @@ -737,11 +731,9 @@ static size_t format_converter(buffy * odp, const char *fmt, va_list ap) /* {{{ i_num = (int64_t) va_arg(ap, intmax_t); break; #endif -#if SIZEOF_PTRDIFF_T case LM_PTRDIFF_T: i_num = (int64_t) va_arg(ap, ptrdiff_t); break; -#endif } } s = ap_php_conv_10(i_num, (*fmt) == 'u', &is_negative, @@ -783,11 +775,9 @@ static size_t format_converter(buffy * odp, const char *fmt, va_list ap) /* {{{ ui_num = (uint64_t) va_arg(ap, uintmax_t); break; #endif -#if SIZEOF_PTRDIFF_T case LM_PTRDIFF_T: ui_num = (uint64_t) va_arg(ap, ptrdiff_t); break; -#endif } s = ap_php_conv_p2(ui_num, 3, *fmt, &num_buf[NUM_BUF_SIZE], &s_len); FIX_PRECISION(adjust_precision, precision, s, s_len); @@ -822,11 +812,9 @@ static size_t format_converter(buffy * odp, const char *fmt, va_list ap) /* {{{ ui_num = (uint64_t) va_arg(ap, uintmax_t); break; #endif -#if SIZEOF_PTRDIFF_T case LM_PTRDIFF_T: ui_num = (uint64_t) va_arg(ap, ptrdiff_t); break; -#endif } s = ap_php_conv_p2(ui_num, 4, *fmt, &num_buf[NUM_BUF_SIZE], &s_len); FIX_PRECISION(adjust_precision, precision, s, s_len); diff --git a/main/snprintf.h b/main/snprintf.h index 2ff7116c3fbb4..daaea80685b99 100644 --- a/main/snprintf.h +++ b/main/snprintf.h @@ -116,9 +116,7 @@ typedef enum { #if SIZEOF_INTMAX_T LM_INTMAX_T, #endif -#if SIZEOF_PTRDIFF_T LM_PTRDIFF_T, -#endif #if SIZEOF_LONG_LONG LM_LONG_LONG, #endif diff --git a/main/spprintf.c b/main/spprintf.c index f180ad31b3da9..157e1ea4fea69 100644 --- a/main/spprintf.c +++ b/main/spprintf.c @@ -321,11 +321,7 @@ static void xbuf_format_converter(void *xbuf, bool is_char, const char *fmt, va_ break; case 't': fmt++; -#if SIZEOF_PTRDIFF_T modifier = LM_PTRDIFF_T; -#else - modifier = LM_SIZE_T; -#endif break; case 'p': { @@ -403,11 +399,9 @@ static void xbuf_format_converter(void *xbuf, bool is_char, const char *fmt, va_ i_num = (int64_t) va_arg(ap, uintmax_t); break; #endif -#if SIZEOF_PTRDIFF_T case LM_PTRDIFF_T: i_num = (int64_t) va_arg(ap, ptrdiff_t); break; -#endif } /* * The rest also applies to other integer formats, so fall @@ -446,11 +440,9 @@ static void xbuf_format_converter(void *xbuf, bool is_char, const char *fmt, va_ i_num = (int64_t) va_arg(ap, intmax_t); break; #endif -#if SIZEOF_PTRDIFF_T case LM_PTRDIFF_T: i_num = (int64_t) va_arg(ap, ptrdiff_t); break; -#endif } } s = ap_php_conv_10(i_num, (*fmt) == 'u', &is_negative, @@ -491,11 +483,9 @@ static void xbuf_format_converter(void *xbuf, bool is_char, const char *fmt, va_ ui_num = (uint64_t) va_arg(ap, uintmax_t); break; #endif -#if SIZEOF_PTRDIFF_T case LM_PTRDIFF_T: ui_num = (uint64_t) va_arg(ap, ptrdiff_t); break; -#endif } s = ap_php_conv_p2(ui_num, 3, *fmt, &num_buf[NUM_BUF_SIZE], &s_len); @@ -531,11 +521,9 @@ static void xbuf_format_converter(void *xbuf, bool is_char, const char *fmt, va_ ui_num = (uint64_t) va_arg(ap, uintmax_t); break; #endif -#if SIZEOF_PTRDIFF_T case LM_PTRDIFF_T: ui_num = (uint64_t) va_arg(ap, ptrdiff_t); break; -#endif } s = ap_php_conv_p2(ui_num, 4, *fmt, &num_buf[NUM_BUF_SIZE], &s_len); diff --git a/win32/build/config.w32.h.in b/win32/build/config.w32.h.in index df69e4622f90d..73a22f04b2ea9 100644 --- a/win32/build/config.w32.h.in +++ b/win32/build/config.w32.h.in @@ -84,10 +84,8 @@ #define ssize_t SSIZE_T #ifdef _WIN64 # define SIZEOF_SIZE_T 8 -# define SIZEOF_PTRDIFF_T 8 #else # define SIZEOF_SIZE_T 4 -# define SIZEOF_PTRDIFF_T 4 #endif #define SIZEOF_OFF_T 4 #define HAVE_FNMATCH From 761478a032541b27f52c96c0d6427436a6011a15 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sun, 29 Jun 2025 01:00:15 +0200 Subject: [PATCH 188/682] [ci skip] Update OSS-Fuzz link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 587cc9bd2dc54..39c6b89fbbd74 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ blog to the most popular websites in the world. PHP is distributed under the [PHP License v3.01](LICENSE). [![Push](https://github.com/php/php-src/actions/workflows/push.yml/badge.svg)](https://github.com/php/php-src/actions/workflows/push.yml) -[![Fuzzing Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/php.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:php) +[![Fuzzing Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/php.svg)](https://issues.oss-fuzz.com/issues?q=project:php) ## Documentation From 8d116a4ba10703c54d947d95e152d25d75d45aa0 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sun, 29 Jun 2025 14:29:28 +0200 Subject: [PATCH 189/682] Implement GH-15483: Use C23 memset_explicit() for ZEND_SECURE_ZERO() if available (#18713) --- Zend/zend_portability.h | 2 ++ configure.ac | 1 + main/explicit_bzero.c | 6 ++++-- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/Zend/zend_portability.h b/Zend/zend_portability.h index 97bd038ecf3d8..7a41a496a0ed7 100644 --- a/Zend/zend_portability.h +++ b/Zend/zend_portability.h @@ -496,6 +496,8 @@ extern "C++" { #ifdef ZEND_WIN32 #define ZEND_SECURE_ZERO(var, size) RtlSecureZeroMemory((var), (size)) +#elif defined(HAVE_MEMSET_EXPLICIT) +#define ZEND_SECURE_ZERO(var, size) memset_explicit((var), 0, (size)) #else #define ZEND_SECURE_ZERO(var, size) explicit_bzero((var), (size)) #endif diff --git a/configure.ac b/configure.ac index f6f305ba053a8..e4a8c31de474f 100644 --- a/configure.ac +++ b/configure.ac @@ -564,6 +564,7 @@ AC_CHECK_FUNCS(m4_normalize([ memmem mempcpy memrchr + memset_explicit mkstemp mmap nice diff --git a/main/explicit_bzero.c b/main/explicit_bzero.c index 75cd126ee9a56..c49bdede66247 100644 --- a/main/explicit_bzero.c +++ b/main/explicit_bzero.c @@ -28,8 +28,10 @@ PHPAPI void php_explicit_bzero(void *dst, size_t siz) { -#ifdef HAVE_EXPLICIT_MEMSET - explicit_memset(dst, 0, siz); +#ifdef HAVE_MEMSET_EXPLICIT /* C23 */ + memset_explicit(dst, 0, siz); +#elif defined(HAVE_EXPLICIT_MEMSET) /* NetBSD-specific */ + explicit_memset(dst, 0, siz); #elif defined(PHP_WIN32) RtlSecureZeroMemory(dst, siz); #elif defined(__GNUC__) From 1d5da8660b2a1a905b7695d83dc25a7501e0aeed Mon Sep 17 00:00:00 2001 From: David CARLIER Date: Sun, 29 Jun 2025 13:58:58 +0100 Subject: [PATCH 190/682] ext/sqlite3: relax sqlite3 explain test conditions (#18949) --- ext/sqlite3/tests/sqlite3_explain.phpt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ext/sqlite3/tests/sqlite3_explain.phpt b/ext/sqlite3/tests/sqlite3_explain.phpt index f580783ca1d14..40648588733c6 100644 --- a/ext/sqlite3/tests/sqlite3_explain.phpt +++ b/ext/sqlite3/tests/sqlite3_explain.phpt @@ -79,7 +79,7 @@ array(%d) { ["addr"]=> int(1) ["opcode"]=> - string(13) "InitCoroutine" + string(%d) "%s" ["p1"]=> int(3) ["p2"]=> @@ -87,7 +87,7 @@ array(%d) { ["p3"]=> int(2) ["p4"]=> - NULL + %s ["p5"]=> int(0) ["comment"]=> @@ -368,7 +368,7 @@ array(1) { ["parent"]=> int(0) ["notused"]=> - int(0) + int(%d) ["detail"]=> string(17) "SCAN test_explain" } From 1a5128f51e1c51c603c3fd3c0f311209c23b8868 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sun, 29 Jun 2025 16:51:23 +0200 Subject: [PATCH 191/682] [ci skip] Fix comment typo in opcache --- ext/opcache/zend_accelerator_module.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/opcache/zend_accelerator_module.c b/ext/opcache/zend_accelerator_module.c index 203a41d93b40a..a4f632872f546 100644 --- a/ext/opcache/zend_accelerator_module.c +++ b/ext/opcache/zend_accelerator_module.c @@ -323,7 +323,7 @@ ZEND_INI_BEGIN() STD_PHP_INI_ENTRY("opcache.jit_max_root_traces" , "1024", PHP_INI_SYSTEM, OnUpdateLong, max_root_traces, zend_jit_globals, jit_globals) STD_PHP_INI_ENTRY("opcache.jit_max_side_traces" , "128", PHP_INI_SYSTEM, OnUpdateLong, max_side_traces, zend_jit_globals, jit_globals) STD_PHP_INI_ENTRY("opcache.jit_max_exit_counters" , "8192", PHP_INI_SYSTEM, OnUpdateLong, max_exit_counters, zend_jit_globals, jit_globals) - /* Defautl value should be a prime number, to reduce the chances of loop iterations being a factor of opcache.jit_hot_loop */ + /* Default value should be a prime number, to reduce the chances of loop iterations being a factor of opcache.jit_hot_loop */ STD_PHP_INI_ENTRY("opcache.jit_hot_loop" , "61", PHP_INI_SYSTEM, OnUpdateCounter, hot_loop, zend_jit_globals, jit_globals) STD_PHP_INI_ENTRY("opcache.jit_hot_func" , "127", PHP_INI_SYSTEM, OnUpdateCounter, hot_func, zend_jit_globals, jit_globals) STD_PHP_INI_ENTRY("opcache.jit_hot_return" , "8", PHP_INI_SYSTEM, OnUpdateCounter, hot_return, zend_jit_globals, jit_globals) From 865739e5b196390f2eb1c5aeb2a7551e31da87cb Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sun, 29 Jun 2025 13:03:43 +0100 Subject: [PATCH 192/682] Fix GH-18976: pack with h or H format string overflow. adding with its own remainder, INT_MAX overflows here (negative values are discarded). close GH-18977 --- NEWS | 2 ++ ext/standard/pack.c | 2 +- ext/standard/tests/strings/gh18976.phpt | 14 ++++++++++++++ 3 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 ext/standard/tests/strings/gh18976.phpt diff --git a/NEWS b/NEWS index 61e1697b62dd9..267681cfa265e 100644 --- a/NEWS +++ b/NEWS @@ -33,6 +33,8 @@ PHP NEWS - Standard: . Fix misleading errors in printf(). (nielsdos) . Fix RCN violations in array functions. (nielsdos) + . Fixed GH-18976 pack() overflow with h/H format and INT_MAX repeater value. + (David Carlier) - Streams: . Fixed GH-13264 (fgets() and stream_get_line() do not return false on filter diff --git a/ext/standard/pack.c b/ext/standard/pack.c index 8f72164a26956..46798e7403daf 100644 --- a/ext/standard/pack.c +++ b/ext/standard/pack.c @@ -388,7 +388,7 @@ PHP_FUNCTION(pack) switch ((int) code) { case 'h': case 'H': - INC_OUTPUTPOS((arg + (arg % 2)) / 2,1) /* 4 bit per arg */ + INC_OUTPUTPOS((arg / 2) + (arg % 2),1) /* 4 bit per arg */ break; case 'a': diff --git a/ext/standard/tests/strings/gh18976.phpt b/ext/standard/tests/strings/gh18976.phpt new file mode 100644 index 0000000000000..aa58167f9d45b --- /dev/null +++ b/ext/standard/tests/strings/gh18976.phpt @@ -0,0 +1,14 @@ +--TEST-- +GH-18976 (pack overflow with h/H format) +--INI-- +memory_limit=-1 +--FILE-- + +--EXPECTF-- + +Warning: pack(): Type h: not enough characters in string in %s on line %d + +Warning: pack(): Type H: not enough characters in string in %s on line %d From 93e3aca5fa235e23f0526826a30c641598b7eb7b Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Sun, 29 Jun 2025 19:50:27 +0200 Subject: [PATCH 193/682] Remove HAVE_INTMAX_T and SIZEOF_INTMAX_T (#18971) The intmax_t is a C99 standard type defined in `` and widely available on current platforms. On Windows they are available as of Visual Studio 2013. Using it conditionally as in these occurrences is not needed anymore. --- UPGRADING.INTERNALS | 2 ++ configure.ac | 1 - main/snprintf.c | 12 ------------ main/snprintf.h | 2 -- main/spprintf.c | 12 ------------ win32/build/config.w32.h.in | 3 +-- 6 files changed, 3 insertions(+), 29 deletions(-) diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS index 47cae00b0955e..3b248614c376d 100644 --- a/UPGRADING.INTERNALS +++ b/UPGRADING.INTERNALS @@ -52,6 +52,7 @@ PHP 8.5 INTERNALS UPGRADE NOTES - Abstract . Preprocessor macro SIZEOF_PTRDIFF_T has been removed. + . Preprocessor macro SIZEOF_INTMAX_T has been removed. - Windows build system changes . SAPI() and ADD_SOURCES() now suport the optional `duplicate_sources` @@ -73,6 +74,7 @@ PHP 8.5 INTERNALS UPGRADE NOTES . Autoconf macro PHP_OUTPUT has been removed (use AC_CONFIG_FILES). . Autoconf macro PHP_TEST_BUILD has been removed (use AC_* macros). . Preprocessor macro HAVE_PTRDIFF_T has been removed. + . Preprocessor macro HAVE_INTMAX_T has been removed. ======================== 3. Module changes diff --git a/configure.ac b/configure.ac index e4a8c31de474f..6c8a888a1d2be 100644 --- a/configure.ac +++ b/configure.ac @@ -452,7 +452,6 @@ AC_CHECK_TYPES([socklen_t], [], [], [ ]) dnl These are defined elsewhere than stdio.h. -PHP_CHECK_SIZEOF([intmax_t], [0]) PHP_CHECK_SIZEOF([ssize_t], [8]) dnl Check stdint types (must be after header check). diff --git a/main/snprintf.c b/main/snprintf.c index 2bc354fec284d..61d9bdd4bddff 100644 --- a/main/snprintf.c +++ b/main/snprintf.c @@ -613,11 +613,7 @@ static size_t format_converter(buffy * odp, const char *fmt, va_list ap) /* {{{ break; case 'j': fmt++; -#if SIZEOF_INTMAX_T modifier = LM_INTMAX_T; -#else - modifier = LM_SIZE_T; -#endif break; case 't': fmt++; @@ -685,11 +681,9 @@ static size_t format_converter(buffy * odp, const char *fmt, va_list ap) /* {{{ i_num = (int64_t) va_arg(ap, unsigned long long int); break; #endif -#if SIZEOF_INTMAX_T case LM_INTMAX_T: i_num = (int64_t) va_arg(ap, uintmax_t); break; -#endif case LM_PTRDIFF_T: i_num = (int64_t) va_arg(ap, ptrdiff_t); break; @@ -726,11 +720,9 @@ static size_t format_converter(buffy * odp, const char *fmt, va_list ap) /* {{{ i_num = (int64_t) va_arg(ap, long long int); break; #endif -#if SIZEOF_INTMAX_T case LM_INTMAX_T: i_num = (int64_t) va_arg(ap, intmax_t); break; -#endif case LM_PTRDIFF_T: i_num = (int64_t) va_arg(ap, ptrdiff_t); break; @@ -770,11 +762,9 @@ static size_t format_converter(buffy * odp, const char *fmt, va_list ap) /* {{{ ui_num = (uint64_t) va_arg(ap, unsigned long long int); break; #endif -#if SIZEOF_INTMAX_T case LM_INTMAX_T: ui_num = (uint64_t) va_arg(ap, uintmax_t); break; -#endif case LM_PTRDIFF_T: ui_num = (uint64_t) va_arg(ap, ptrdiff_t); break; @@ -807,11 +797,9 @@ static size_t format_converter(buffy * odp, const char *fmt, va_list ap) /* {{{ ui_num = (uint64_t) va_arg(ap, unsigned long long int); break; #endif -#if SIZEOF_INTMAX_T case LM_INTMAX_T: ui_num = (uint64_t) va_arg(ap, uintmax_t); break; -#endif case LM_PTRDIFF_T: ui_num = (uint64_t) va_arg(ap, ptrdiff_t); break; diff --git a/main/snprintf.h b/main/snprintf.h index daaea80685b99..d61ee5e39a696 100644 --- a/main/snprintf.h +++ b/main/snprintf.h @@ -113,9 +113,7 @@ END_EXTERN_C() typedef enum { LM_STD = 0, -#if SIZEOF_INTMAX_T LM_INTMAX_T, -#endif LM_PTRDIFF_T, #if SIZEOF_LONG_LONG LM_LONG_LONG, diff --git a/main/spprintf.c b/main/spprintf.c index 157e1ea4fea69..3f6005e90b071 100644 --- a/main/spprintf.c +++ b/main/spprintf.c @@ -313,11 +313,7 @@ static void xbuf_format_converter(void *xbuf, bool is_char, const char *fmt, va_ break; case 'j': fmt++; -#if SIZEOF_INTMAX_T modifier = LM_INTMAX_T; -#else - modifier = LM_SIZE_T; -#endif break; case 't': fmt++; @@ -394,11 +390,9 @@ static void xbuf_format_converter(void *xbuf, bool is_char, const char *fmt, va_ i_num = (int64_t) va_arg(ap, unsigned long long int); break; #endif -#if SIZEOF_INTMAX_T case LM_INTMAX_T: i_num = (int64_t) va_arg(ap, uintmax_t); break; -#endif case LM_PTRDIFF_T: i_num = (int64_t) va_arg(ap, ptrdiff_t); break; @@ -435,11 +429,9 @@ static void xbuf_format_converter(void *xbuf, bool is_char, const char *fmt, va_ i_num = (int64_t) va_arg(ap, long long int); break; #endif -#if SIZEOF_INTMAX_T case LM_INTMAX_T: i_num = (int64_t) va_arg(ap, intmax_t); break; -#endif case LM_PTRDIFF_T: i_num = (int64_t) va_arg(ap, ptrdiff_t); break; @@ -478,11 +470,9 @@ static void xbuf_format_converter(void *xbuf, bool is_char, const char *fmt, va_ ui_num = (uint64_t) va_arg(ap, unsigned long long int); break; #endif -#if SIZEOF_INTMAX_T case LM_INTMAX_T: ui_num = (uint64_t) va_arg(ap, uintmax_t); break; -#endif case LM_PTRDIFF_T: ui_num = (uint64_t) va_arg(ap, ptrdiff_t); break; @@ -516,11 +506,9 @@ static void xbuf_format_converter(void *xbuf, bool is_char, const char *fmt, va_ ui_num = (uint64_t) va_arg(ap, unsigned long long int); break; #endif -#if SIZEOF_INTMAX_T case LM_INTMAX_T: ui_num = (uint64_t) va_arg(ap, uintmax_t); break; -#endif case LM_PTRDIFF_T: ui_num = (uint64_t) va_arg(ap, ptrdiff_t); break; diff --git a/win32/build/config.w32.h.in b/win32/build/config.w32.h.in index 73a22f04b2ea9..d8ff0c9dc6a50 100644 --- a/win32/build/config.w32.h.in +++ b/win32/build/config.w32.h.in @@ -78,9 +78,8 @@ /* int and long are still 32bit in 64bit compiles */ #define SIZEOF_INT 4 #define SIZEOF_LONG 4 -/* MSVC.6/NET don't allow 'long long' or know 'intmax_t' */ +/* MSVC.6/NET don't allow 'long long' */ #define SIZEOF_LONG_LONG 8 /* defined as __int64 */ -#define SIZEOF_INTMAX_T 0 #define ssize_t SSIZE_T #ifdef _WIN64 # define SIZEOF_SIZE_T 8 From db157e3168624ded7f24bfadec974810b54e793b Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Sun, 29 Jun 2025 20:56:07 +0200 Subject: [PATCH 194/682] Remove redundant PCRE_STATIC definition (#18952) Current minimum PCRE2 library in PHP is 10.30 (with bundled 10.45) and none of these versions use PCRE_STATIC macro anymore in favor of PCRE2_STATIC, which is defined in the generated config.w32.h on Windows. --- main/php_compat.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/main/php_compat.h b/main/php_compat.h index 438ada44eda03..2f9f4c1c89e62 100644 --- a/main/php_compat.h +++ b/main/php_compat.h @@ -393,8 +393,4 @@ #define XML_NS 1 #endif -#ifdef PHP_EXPORTS -#define PCRE_STATIC -#endif - #endif From 4a18c895ca7f0511bf26b10171d9efbff5a2af8c Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Mon, 30 Jun 2025 09:09:55 +0200 Subject: [PATCH 195/682] Fix OSS-Fuzz #428053935 (#18969) Registering the constant may happen under another name due to lowercasing. This will cause the lookup to the constant to fail. Instead of looking it up, just change the Zend API to return a pointer instead. --- UPGRADING.INTERNALS | 2 ++ .../constants/oss_fuzz_428053935.phpt | 19 +++++++++++++++++++ Zend/zend_builtin_functions.c | 2 +- Zend/zend_constants.c | 7 +++---- Zend/zend_constants.h | 2 +- Zend/zend_vm_def.h | 9 ++++----- Zend/zend_vm_execute.h | 9 ++++----- 7 files changed, 34 insertions(+), 16 deletions(-) create mode 100644 Zend/tests/attributes/constants/oss_fuzz_428053935.phpt diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS index 3b248614c376d..6b5959fe030fb 100644 --- a/UPGRADING.INTERNALS +++ b/UPGRADING.INTERNALS @@ -45,6 +45,8 @@ PHP 8.5 INTERNALS UPGRADE NOTES properties. . ZEND_IS_XDIGIT() macro was removed because it was unused and its name did not match its actual behavior. + . zend_register_constant() now returns a pointer to the added constant + on success and NULL on failure instead of SUCCESS/FAILURE. ======================== 2. Build system changes diff --git a/Zend/tests/attributes/constants/oss_fuzz_428053935.phpt b/Zend/tests/attributes/constants/oss_fuzz_428053935.phpt new file mode 100644 index 0000000000000..97747fd6194fd --- /dev/null +++ b/Zend/tests/attributes/constants/oss_fuzz_428053935.phpt @@ -0,0 +1,19 @@ +--TEST-- +OSS-Fuzz #428053935 +--FILE-- +getAttributes()); +?> +--EXPECTF-- +array(1) { + [0]=> + object(ReflectionAttribute)#%d (1) { + ["name"]=> + string(8) "Foo\Attr" + } +} diff --git a/Zend/zend_builtin_functions.c b/Zend/zend_builtin_functions.c index 48e5c70897294..fc3b0f57d85e7 100644 --- a/Zend/zend_builtin_functions.c +++ b/Zend/zend_builtin_functions.c @@ -592,7 +592,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) == SUCCESS) { + if (zend_register_constant(&c) != NULL) { RETURN_TRUE; } else { RETURN_FALSE; diff --git a/Zend/zend_constants.c b/Zend/zend_constants.c index ffad8315ae40d..db37b9cf76824 100644 --- a/Zend/zend_constants.c +++ b/Zend/zend_constants.c @@ -505,11 +505,11 @@ static void* zend_hash_add_constant(HashTable *ht, zend_string *key, zend_consta return ret; } -ZEND_API zend_result zend_register_constant(zend_constant *c) +ZEND_API zend_constant *zend_register_constant(zend_constant *c) { zend_string *lowercase_name = NULL; zend_string *name; - zend_result ret = SUCCESS; + zend_constant *ret = NULL; bool persistent = (ZEND_CONSTANT_FLAGS(c) & CONST_PERSISTENT) != 0; #if 0 @@ -539,7 +539,7 @@ ZEND_API zend_result zend_register_constant(zend_constant *c) /* Check if the user is trying to define any special constant */ if (zend_string_equals_literal(name, "__COMPILER_HALT_OFFSET__") || (!persistent && zend_get_special_const(ZSTR_VAL(name), ZSTR_LEN(name))) - || zend_hash_add_constant(EG(zend_constants), name, c) == NULL + || (ret = zend_hash_add_constant(EG(zend_constants), name, c)) == NULL ) { zend_error(E_WARNING, "Constant %s already defined", ZSTR_VAL(name)); zend_string_release(c->name); @@ -550,7 +550,6 @@ ZEND_API zend_result zend_register_constant(zend_constant *c) if (!persistent) { zval_ptr_dtor_nogc(&c->value); } - ret = FAILURE; } if (lowercase_name) { zend_string_release(lowercase_name); diff --git a/Zend/zend_constants.h b/Zend/zend_constants.h index 3912215d80775..69ea1d9021a83 100644 --- a/Zend/zend_constants.h +++ b/Zend/zend_constants.h @@ -97,7 +97,7 @@ ZEND_API void zend_register_long_constant(const char *name, size_t name_len, zen ZEND_API void zend_register_double_constant(const char *name, size_t name_len, double dval, int flags, int module_number); ZEND_API void zend_register_string_constant(const char *name, size_t name_len, const char *strval, int flags, int module_number); ZEND_API void zend_register_stringl_constant(const char *name, size_t name_len, const char *strval, size_t strlen, int flags, int module_number); -ZEND_API zend_result zend_register_constant(zend_constant *c); +ZEND_API zend_constant *zend_register_constant(zend_constant *c); void zend_constant_add_attributes(zend_constant *c, HashTable *attributes); #ifdef ZTS void zend_copy_constants(HashTable *target, HashTable *source); diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index be7bc8b37b7dd..51aaf635b3b30 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -8261,7 +8261,7 @@ ZEND_VM_HANDLER(143, ZEND_DECLARE_CONST, CONST, CONST) ZEND_CONSTANT_SET_FLAGS(&c, 0, PHP_USER_CONSTANT); c.name = zend_string_copy(Z_STR_P(name)); - if (zend_register_constant(&c) == FAILURE) { + if (zend_register_constant(&c) == NULL) { } FREE_OP1(); @@ -8274,7 +8274,7 @@ ZEND_VM_HANDLER(210, ZEND_DECLARE_ATTRIBUTED_CONST, CONST, CONST) USE_OPLINE zval *name; zval *val; - zend_constant c; + zend_constant c, *registered; SAVE_OPLINE(); name = GET_OP1_ZVAL_PTR(BP_VAR_R); @@ -8293,7 +8293,8 @@ ZEND_VM_HANDLER(210, ZEND_DECLARE_ATTRIBUTED_CONST, CONST, CONST) ZEND_CONSTANT_SET_FLAGS(&c, 0, PHP_USER_CONSTANT); c.name = zend_string_copy(Z_STR_P(name)); - if (zend_register_constant(&c) == FAILURE) { + registered = zend_register_constant(&c); + if (registered == NULL) { FREE_OP1(); FREE_OP2(); /* two opcodes used, second one is the data with attributes */ @@ -8301,9 +8302,7 @@ ZEND_VM_HANDLER(210, ZEND_DECLARE_ATTRIBUTED_CONST, CONST, CONST) } HashTable *attributes = Z_PTR_P(GET_OP_DATA_ZVAL_PTR(BP_VAR_R)); - zend_constant *registered = zend_get_constant_ptr(c.name); ZEND_ASSERT(attributes != NULL); - ZEND_ASSERT(registered != NULL); zend_constant_add_attributes(registered, attributes); FREE_OP1(); diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 3a13f4244d361..f29c6b4726145 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -8043,7 +8043,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DECLARE_CONST_SPEC_CONST_CONST ZEND_CONSTANT_SET_FLAGS(&c, 0, PHP_USER_CONSTANT); c.name = zend_string_copy(Z_STR_P(name)); - if (zend_register_constant(&c) == FAILURE) { + if (zend_register_constant(&c) == NULL) { } @@ -8055,7 +8055,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DECLARE_ATTRIBUTED_CONST_SPEC_ USE_OPLINE zval *name; zval *val; - zend_constant c; + zend_constant c, *registered; SAVE_OPLINE(); name = RT_CONSTANT(opline, opline->op1); @@ -8074,7 +8074,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DECLARE_ATTRIBUTED_CONST_SPEC_ ZEND_CONSTANT_SET_FLAGS(&c, 0, PHP_USER_CONSTANT); c.name = zend_string_copy(Z_STR_P(name)); - if (zend_register_constant(&c) == FAILURE) { + registered = zend_register_constant(&c); + if (registered == NULL) { /* two opcodes used, second one is the data with attributes */ @@ -8082,9 +8083,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DECLARE_ATTRIBUTED_CONST_SPEC_ } HashTable *attributes = Z_PTR_P(get_op_data_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1)); - zend_constant *registered = zend_get_constant_ptr(c.name); ZEND_ASSERT(attributes != NULL); - ZEND_ASSERT(registered != NULL); zend_constant_add_attributes(registered, attributes); From 85522c0d4803688c957c5bcccfc4f38696392bb9 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Fri, 27 Jun 2025 09:14:54 +0200 Subject: [PATCH 196/682] Add FreeBSD ZTS nightly build Closes GH-18959 --- .github/actions/freebsd/action.yml | 8 +++++++- .github/workflows/nightly.yml | 14 +++++++++++++- .github/workflows/root.yml | 1 + 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/.github/actions/freebsd/action.yml b/.github/actions/freebsd/action.yml index 1f7c670f27227..fd37e92bbe5f9 100644 --- a/.github/actions/freebsd/action.yml +++ b/.github/actions/freebsd/action.yml @@ -1,4 +1,8 @@ name: FreeBSD +inputs: + configurationParameters: + default: '' + required: false runs: using: composite steps: @@ -79,7 +83,9 @@ runs: --with-mhash \ --with-sodium \ --with-config-file-path=/etc \ - --with-config-file-scan-dir=/etc/php.d + --with-config-file-scan-dir=/etc/php.d \ + ${{ inputs.configurationParameters }} + gmake -j2 mkdir /etc/php.d gmake install > /dev/null diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 1b1532af7f799..1167b9c5d2934 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -23,6 +23,9 @@ on: run_macos_arm64: required: true type: boolean + run_freebsd_zts: + required: true + type: boolean ubuntu_version: required: true type: string @@ -1052,7 +1055,13 @@ jobs: - name: Test run: .github/scripts/windows/test.bat FREEBSD: - name: FREEBSD + strategy: + fail-fast: false + matrix: + zts: [true, false] + exclude: + - zts: ${{ !inputs.run_freebsd_zts && true || '*never*' }} + name: "FREEBSD_${{ matrix.zts && 'ZTS' || 'NTS' }}" runs-on: ubuntu-latest steps: - name: git checkout @@ -1061,3 +1070,6 @@ jobs: ref: ${{ inputs.branch }} - name: FreeBSD uses: ./.github/actions/freebsd + with: + configurationParameters: >- + --${{ matrix.zts && 'enable' || 'disable' }}-zts diff --git a/.github/workflows/root.yml b/.github/workflows/root.yml index 78e0d47aa1d15..30abc1da852bb 100644 --- a/.github/workflows/root.yml +++ b/.github/workflows/root.yml @@ -55,6 +55,7 @@ jobs: run_alpine: ${{ (matrix.branch.version[0] == 8 && matrix.branch.version[1] >= 4) || matrix.branch.version[0] >= 9 }} run_linux_ppc64: ${{ (matrix.branch.version[0] == 8 && matrix.branch.version[1] >= 4) || matrix.branch.version[0] >= 9 }} run_macos_arm64: ${{ (matrix.branch.version[0] == 8 && matrix.branch.version[1] >= 4) || matrix.branch.version[0] >= 9 }} + run_freebsd_zts: ${{ (matrix.branch.version[0] == 8 && matrix.branch.version[1] >= 3) || matrix.branch.version[0] >= 9 }} ubuntu_version: ${{ (((matrix.branch.version[0] == 8 && matrix.branch.version[1] >= 5) || matrix.branch.version[0] >= 9) && '24.04') || '22.04' }} From c9249e2d3aa401bda5d9a3071e86e0594807ed00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Mon, 30 Jun 2025 12:31:27 +0200 Subject: [PATCH 197/682] Support every argument syntax for `clone()` (#18938) * zend_language_parser: Support every argument syntax for `clone()` * zend_language_parser: Adjust `clone()` grammar to avoid conflicts * zend_language_parser: Add explanatory comment for `clone_argument_list` --- Zend/tests/clone/ast.phpt | 63 +++++++++++++++++++++++++++++++++++++ Zend/zend_language_parser.y | 43 ++++++++++++++++++++----- 2 files changed, 99 insertions(+), 7 deletions(-) diff --git a/Zend/tests/clone/ast.phpt b/Zend/tests/clone/ast.phpt index 89a1a0a481000..e482854a94482 100644 --- a/Zend/tests/clone/ast.phpt +++ b/Zend/tests/clone/ast.phpt @@ -18,6 +18,60 @@ try { echo $e->getMessage(), PHP_EOL; } +try { + assert(false && $y = clone($x, )); +} catch (Error $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + assert(false && $y = clone($x, [ "foo" => $foo, "bar" => $bar ])); +} catch (Error $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + assert(false && $y = clone($x, $array)); +} catch (Error $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + assert(false && $y = clone($x, $array, $extraParameter, $trailingComma, )); +} catch (Error $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + assert(false && $y = clone(object: $x, withProperties: [ "foo" => $foo, "bar" => $bar ])); +} catch (Error $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + assert(false && $y = clone($x, withProperties: [ "foo" => $foo, "bar" => $bar ])); +} catch (Error $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + assert(false && $y = clone(object: $x)); +} catch (Error $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + assert(false && $y = clone(object: $x, [ "foo" => $foo, "bar" => $bar ])); +} catch (Error $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + assert(false && $y = clone(...["object" => $x, "withProperties" => [ "foo" => $foo, "bar" => $bar ]])); +} catch (Error $e) { + echo $e->getMessage(), PHP_EOL; +} + try { assert(false && $y = clone(...)); } catch (Error $e) { @@ -28,4 +82,13 @@ try { --EXPECT-- assert(false && ($y = \clone($x))) assert(false && ($y = \clone($x))) +assert(false && ($y = \clone($x))) +assert(false && ($y = \clone($x, ['foo' => $foo, 'bar' => $bar]))) +assert(false && ($y = \clone($x, $array))) +assert(false && ($y = \clone($x, $array, $extraParameter, $trailingComma))) +assert(false && ($y = \clone(object: $x, withProperties: ['foo' => $foo, 'bar' => $bar]))) +assert(false && ($y = \clone($x, withProperties: ['foo' => $foo, 'bar' => $bar]))) +assert(false && ($y = \clone(object: $x))) +assert(false && ($y = \clone(object: $x, ['foo' => $foo, 'bar' => $bar]))) +assert(false && ($y = \clone(...['object' => $x, 'withProperties' => ['foo' => $foo, 'bar' => $bar]]))) assert(false && ($y = \clone(...))) diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index 016c6e5c9d098..805f378cb983c 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -259,7 +259,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*); %type unprefixed_use_declarations const_decl inner_statement %type expr optional_expr while_statement for_statement foreach_variable %type foreach_statement declare_statement finally_statement unset_variable variable -%type extends_from parameter optional_type_without_static argument global_var +%type extends_from parameter optional_type_without_static argument argument_no_expr global_var %type static_var class_statement trait_adaptation trait_precedence trait_alias %type absolute_trait_method_reference trait_method_reference property echo_expr %type new_dereferenceable new_non_dereferenceable anonymous_class class_name class_name_reference simple_variable @@ -287,7 +287,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*); %type enum_declaration_statement enum_backing_type enum_case enum_case_expr %type function_name non_empty_member_modifiers %type property_hook property_hook_list optional_property_hook_list hooked_property property_hook_body -%type optional_parameter_list +%type optional_parameter_list clone_argument_list non_empty_clone_argument_list %type returns_ref function fn is_reference is_variadic property_modifiers property_hook_modifiers %type method_modifiers class_const_modifiers member_modifier optional_cpp_modifiers @@ -914,13 +914,42 @@ non_empty_argument_list: { $$ = zend_ast_list_add($1, $3); } ; -argument: - expr { $$ = $1; } - | identifier ':' expr +/* `clone_argument_list` is necessary to resolve a parser ambiguity (shift-reduce conflict) + * of `clone($expr)`, which could either be parsed as a function call with `$expr` as the first + * argument or as a use of the `clone` language construct with an expression with useless + * parenthesis. Both would be valid and result in the same AST / the same semantics. + * `clone_argument_list` is defined in a way that an `expr` in the first position needs to + * be followed by a `,` which is not valid syntax for a parenthesized `expr`, ensuring + * that calling `clone()` with a single unnamed parameter is handled by the language construct + * syntax. + */ +clone_argument_list: + '(' ')' { $$ = zend_ast_create_list(0, ZEND_AST_ARG_LIST); } + | '(' non_empty_clone_argument_list possible_comma ')' { $$ = $2; } + | '(' expr ',' ')' { $$ = zend_ast_create_list(1, ZEND_AST_ARG_LIST, $2); } + | '(' T_ELLIPSIS ')' { $$ = zend_ast_create_fcc(); } +; + +non_empty_clone_argument_list: + expr ',' argument + { $$ = zend_ast_create_list(2, ZEND_AST_ARG_LIST, $1, $3); } + | argument_no_expr + { $$ = zend_ast_create_list(1, ZEND_AST_ARG_LIST, $1); } + | non_empty_clone_argument_list ',' argument + { $$ = zend_ast_list_add($1, $3); } +; + +argument_no_expr: + identifier ':' expr { $$ = zend_ast_create(ZEND_AST_NAMED_ARG, $1, $3); } | T_ELLIPSIS expr { $$ = zend_ast_create(ZEND_AST_UNPACK, $2); } ; +argument: + expr { $$ = $1; } + | argument_no_expr { $$ = $1; } +; + global_var_list: global_var_list ',' global_var { $$ = zend_ast_list_add($1, $3); } | global_var { $$ = zend_ast_create_list(1, ZEND_AST_STMT_LIST, $1); } @@ -1228,10 +1257,10 @@ expr: { $$ = zend_ast_create(ZEND_AST_ASSIGN, $1, $3); } | variable '=' ampersand variable { $$ = zend_ast_create(ZEND_AST_ASSIGN_REF, $1, $4); } - | T_CLONE '(' T_ELLIPSIS ')' { + | T_CLONE clone_argument_list { zend_ast *name = zend_ast_create_zval_from_str(ZSTR_KNOWN(ZEND_STR_CLONE)); name->attr = ZEND_NAME_FQ; - $$ = zend_ast_create(ZEND_AST_CALL, name, zend_ast_create_fcc()); + $$ = zend_ast_create(ZEND_AST_CALL, name, $2); } | T_CLONE expr { zend_ast *name = zend_ast_create_zval_from_str(ZSTR_KNOWN(ZEND_STR_CLONE)); From 8ddc210bf783d6dbe1ec756996ad9a0fb2c77469 Mon Sep 17 00:00:00 2001 From: Shivam Mathur Date: Mon, 30 Jun 2025 20:00:25 +0530 Subject: [PATCH 198/682] Fix PHP_BUILD_CRT input in the nightly workflow (#18982) --- .github/workflows/nightly.yml | 5 ++++- .github/workflows/root.yml | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 1167b9c5d2934..5817c647a871a 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -32,6 +32,9 @@ on: windows_version: required: true type: string + vs_crt_version: + required: true + type: string skip_laravel: required: true type: boolean @@ -1034,7 +1037,7 @@ jobs: PHP_BUILD_OBJ_DIR: C:\obj PHP_BUILD_CACHE_SDK_DIR: C:\build-cache\sdk PHP_BUILD_SDK_BRANCH: php-sdk-2.3.0 - PHP_BUILD_CRT: ${{ inputs.windows_version == '2022' && 'vs17' || 'vs16' }} + PHP_BUILD_CRT: ${{ inputs.vs_crt_version }} PLATFORM: ${{ matrix.x64 && 'x64' || 'x86' }} THREAD_SAFE: "${{ matrix.zts && '1' || '0' }}" INTRINSICS: "${{ matrix.zts && 'AVX2' || '' }}" diff --git a/.github/workflows/root.yml b/.github/workflows/root.yml index 30abc1da852bb..96943a8cfb2aa 100644 --- a/.github/workflows/root.yml +++ b/.github/workflows/root.yml @@ -60,6 +60,7 @@ jobs: (((matrix.branch.version[0] == 8 && matrix.branch.version[1] >= 5) || matrix.branch.version[0] >= 9) && '24.04') || '22.04' }} windows_version: '2022' + vs_crt_version: ${{ ((matrix.branch.version[0] == 8 && matrix.branch.version[1] >= 4) && 'vs17') || 'vs16' }} skip_laravel: ${{ matrix.branch.version[0] == 8 && matrix.branch.version[1] == 1 }} skip_symfony: ${{ matrix.branch.version[0] == 8 && matrix.branch.version[1] == 1 }} skip_wordpress: ${{ matrix.branch.version[0] == 8 && matrix.branch.version[1] == 1 }} From f6f0aed9f3bc9c8758a3f81be7047684827b0d36 Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Mon, 30 Jun 2025 18:37:20 +0200 Subject: [PATCH 199/682] Allow using fast destruction path when ASAN is in use (#18835) --- Zend/zend_execute_API.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index 9a7803e44e66e..18e7028957f71 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -438,6 +438,12 @@ void shutdown_executor(void) /* {{{ */ zval *zv; #if ZEND_DEBUG bool fast_shutdown = 0; +#elif defined(__SANITIZE_ADDRESS__) + char *force_fast_shutdown = getenv("ZEND_ASAN_FORCE_FAST_SHUTDOWN"); + bool fast_shutdown = ( + is_zend_mm() + || (force_fast_shutdown && ZEND_ATOL(force_fast_shutdown)) + ) && !EG(full_tables_cleanup); #else bool fast_shutdown = is_zend_mm() && !EG(full_tables_cleanup); #endif From 53f2aa93ae2bfbb4291a32f27cbbce78ff6b8d13 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Mon, 23 Jun 2025 23:38:41 +0200 Subject: [PATCH 200/682] Fix GH-18898: SEGV zend_jit_op_array_hot with property hooks and preloading Property hooks were not handled for JIT+trait+preloading. Split the existing functions that handle op arrays, and add iterations for property hooks. Closes GH-18923. --- NEWS | 2 + ext/opcache/jit/zend_jit.c | 31 +++++++++-- ext/opcache/tests/jit/gh18898_1.phpt | 23 ++++++++ ext/opcache/tests/jit/gh18898_2.phpt | 23 ++++++++ ext/opcache/zend_persist.c | 83 ++++++++++++++++++++-------- 5 files changed, 133 insertions(+), 29 deletions(-) create mode 100644 ext/opcache/tests/jit/gh18898_1.phpt create mode 100644 ext/opcache/tests/jit/gh18898_2.phpt diff --git a/NEWS b/NEWS index 3f4e3a0a4a349..1af910562381d 100644 --- a/NEWS +++ b/NEWS @@ -28,6 +28,8 @@ PHP NEWS warning and opline is not set yet). (nielsdos) . Fixed bug GH-14082 (Segmentation fault on unknown address 0x600000000018 in ext/opcache/jit/zend_jit.c). (nielsdos) + . Fixed bug GH-18898 (SEGV zend_jit_op_array_hot with property hooks + and preloading). (nielsdos) - PCNTL: . Fixed bug GH-18958 (Fatal error during shutdown after pcntl_rfork() or diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c index 67e89d3e2e66d..7b451240a38b8 100644 --- a/ext/opcache/jit/zend_jit.c +++ b/ext/opcache/jit/zend_jit.c @@ -3216,6 +3216,17 @@ int zend_jit_op_array(zend_op_array *op_array, zend_script *script) return FAILURE; } +static void zend_jit_link_func_info(zend_op_array *op_array) +{ + if (!ZEND_FUNC_INFO(op_array)) { + void *jit_extension = zend_shared_alloc_get_xlat_entry(op_array->opcodes); + + if (jit_extension) { + ZEND_SET_FUNC_INFO(op_array, jit_extension); + } + } +} + int zend_jit_script(zend_script *script) { void *checkpoint; @@ -3303,6 +3314,7 @@ int zend_jit_script(zend_script *script) zend_class_entry *ce; zend_op_array *op_array; zval *zv; + zend_property_info *prop; ZEND_HASH_MAP_FOREACH_VAL(&script->class_table, zv) { if (Z_TYPE_P(zv) == IS_ALIAS_PTR) { @@ -3313,14 +3325,21 @@ int zend_jit_script(zend_script *script) ZEND_ASSERT(ce->type == ZEND_USER_CLASS); ZEND_HASH_MAP_FOREACH_PTR(&ce->function_table, op_array) { - if (!ZEND_FUNC_INFO(op_array)) { - void *jit_extension = zend_shared_alloc_get_xlat_entry(op_array->opcodes); + zend_jit_link_func_info(op_array); + } ZEND_HASH_FOREACH_END(); - if (jit_extension) { - ZEND_SET_FUNC_INFO(op_array, jit_extension); + if (ce->num_hooked_props > 0) { + ZEND_HASH_MAP_FOREACH_PTR(&ce->properties_info, prop) { + if (prop->hooks) { + for (uint32_t i = 0; i < ZEND_PROPERTY_HOOK_COUNT; i++) { + if (prop->hooks[i]) { + op_array = &prop->hooks[i]->op_array; + zend_jit_link_func_info(op_array); + } + } } - } - } ZEND_HASH_FOREACH_END(); + } ZEND_HASH_FOREACH_END(); + } } ZEND_HASH_FOREACH_END(); } diff --git a/ext/opcache/tests/jit/gh18898_1.phpt b/ext/opcache/tests/jit/gh18898_1.phpt new file mode 100644 index 0000000000000..6038f006f5e5c --- /dev/null +++ b/ext/opcache/tests/jit/gh18898_1.phpt @@ -0,0 +1,23 @@ +--TEST-- +GH-18898 (SEGV zend_jit_op_array_hot with property hooks and preloading) - jit 1235 +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.jit=1235 +opcache.jit_buffer_size=16M +opcache.preload={PWD}/../gh18534_preload.inc +--EXTENSIONS-- +opcache +--SKIPIF-- + +--FILE-- +dummyProperty2); +echo "ok"; +?> +--EXPECT-- +NULL +ok diff --git a/ext/opcache/tests/jit/gh18898_2.phpt b/ext/opcache/tests/jit/gh18898_2.phpt new file mode 100644 index 0000000000000..0ce79b859a979 --- /dev/null +++ b/ext/opcache/tests/jit/gh18898_2.phpt @@ -0,0 +1,23 @@ +--TEST-- +GH-18898 (SEGV zend_jit_op_array_hot with property hooks and preloading) - jit 1233 +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.jit=1233 +opcache.jit_buffer_size=16M +opcache.preload={PWD}/../gh18534_preload.inc +--EXTENSIONS-- +opcache +--SKIPIF-- + +--FILE-- +dummyProperty2); +echo "ok"; +?> +--EXPECT-- +NULL +ok diff --git a/ext/opcache/zend_persist.c b/ext/opcache/zend_persist.c index 1c21e031a1958..71e5bb540078f 100644 --- a/ext/opcache/zend_persist.c +++ b/ext/opcache/zend_persist.c @@ -1262,6 +1262,39 @@ void zend_update_parent_ce(zend_class_entry *ce) } } +static void zend_accel_persist_jit_op_array(zend_op_array *op_array, zend_class_entry *ce) +{ + if (op_array->type == ZEND_USER_FUNCTION) { + if (op_array->scope == ce + && !(op_array->fn_flags & ZEND_ACC_ABSTRACT) + && !(op_array->fn_flags & ZEND_ACC_TRAIT_CLONE)) { + zend_jit_op_array(op_array, ZCG(current_persistent_script) ? &ZCG(current_persistent_script)->script : NULL); + for (uint32_t i = 0; i < op_array->num_dynamic_func_defs; i++) { + zend_jit_op_array(op_array->dynamic_func_defs[i], ZCG(current_persistent_script) ? &ZCG(current_persistent_script)->script : NULL); + } + } + } +} + +static void zend_accel_persist_link_func_info(zend_op_array *op_array, zend_class_entry *ce) +{ + if (op_array->type == ZEND_USER_FUNCTION + && !(op_array->fn_flags & ZEND_ACC_ABSTRACT)) { + if ((op_array->scope != ce + || (op_array->fn_flags & ZEND_ACC_TRAIT_CLONE)) + && (JIT_G(trigger) == ZEND_JIT_ON_FIRST_EXEC + || JIT_G(trigger) == ZEND_JIT_ON_PROF_REQUEST + || JIT_G(trigger) == ZEND_JIT_ON_HOT_COUNTERS + || JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE)) { + void *jit_extension = zend_shared_alloc_get_xlat_entry(op_array->opcodes); + + if (jit_extension) { + ZEND_SET_FUNC_INFO(op_array, jit_extension); + } + } + } +} + static void zend_accel_persist_class_table(HashTable *class_table) { Bucket *p; @@ -1288,44 +1321,48 @@ static void zend_accel_persist_class_table(HashTable *class_table) if (JIT_G(on) && JIT_G(opt_level) <= ZEND_JIT_LEVEL_OPT_FUNCS && !ZCG(current_persistent_script)->corrupted) { zend_op_array *op_array; + zend_property_info *prop; ZEND_HASH_MAP_FOREACH_BUCKET(class_table, p) { if (EXPECTED(Z_TYPE(p->val) != IS_ALIAS_PTR)) { ce = Z_PTR(p->val); ZEND_HASH_MAP_FOREACH_PTR(&ce->function_table, op_array) { - if (op_array->type == ZEND_USER_FUNCTION) { - if (op_array->scope == ce - && !(op_array->fn_flags & ZEND_ACC_ABSTRACT) - && !(op_array->fn_flags & ZEND_ACC_TRAIT_CLONE)) { - zend_jit_op_array(op_array, ZCG(current_persistent_script) ? &ZCG(current_persistent_script)->script : NULL); - for (uint32_t i = 0; i < op_array->num_dynamic_func_defs; i++) { - zend_jit_op_array(op_array->dynamic_func_defs[i], ZCG(current_persistent_script) ? &ZCG(current_persistent_script)->script : NULL); + zend_accel_persist_jit_op_array(op_array, ce); + } ZEND_HASH_FOREACH_END(); + + if (ce->num_hooked_props > 0) { + ZEND_HASH_MAP_FOREACH_PTR(&ce->properties_info, prop) { + if (prop->hooks) { + for (uint32_t i = 0; i < ZEND_PROPERTY_HOOK_COUNT; i++) { + if (prop->hooks[i]) { + op_array = &prop->hooks[i]->op_array; + zend_accel_persist_jit_op_array(op_array, ce); + } } } - } - } ZEND_HASH_FOREACH_END(); + } ZEND_HASH_FOREACH_END(); + } } } ZEND_HASH_FOREACH_END(); ZEND_HASH_MAP_FOREACH_BUCKET(class_table, p) { if (EXPECTED(Z_TYPE(p->val) != IS_ALIAS_PTR)) { ce = Z_PTR(p->val); ZEND_HASH_MAP_FOREACH_PTR(&ce->function_table, op_array) { - if (op_array->type == ZEND_USER_FUNCTION - && !(op_array->fn_flags & ZEND_ACC_ABSTRACT)) { - if ((op_array->scope != ce - || (op_array->fn_flags & ZEND_ACC_TRAIT_CLONE)) - && (JIT_G(trigger) == ZEND_JIT_ON_FIRST_EXEC - || JIT_G(trigger) == ZEND_JIT_ON_PROF_REQUEST - || JIT_G(trigger) == ZEND_JIT_ON_HOT_COUNTERS - || JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE)) { - void *jit_extension = zend_shared_alloc_get_xlat_entry(op_array->opcodes); - - if (jit_extension) { - ZEND_SET_FUNC_INFO(op_array, jit_extension); + zend_accel_persist_link_func_info(op_array, ce); + } ZEND_HASH_FOREACH_END(); + + if (ce->num_hooked_props > 0) { + ZEND_HASH_MAP_FOREACH_PTR(&ce->properties_info, prop) { + if (prop->hooks) { + for (uint32_t i = 0; i < ZEND_PROPERTY_HOOK_COUNT; i++) { + if (prop->hooks[i]) { + op_array = &prop->hooks[i]->op_array; + zend_accel_persist_link_func_info(op_array, ce); + } } } - } - } ZEND_HASH_FOREACH_END(); + } ZEND_HASH_FOREACH_END(); + } } } ZEND_HASH_FOREACH_END(); } From 5ef0dc76665d089ccb4b05a8542c62220b146d2c Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Thu, 10 Apr 2025 15:15:36 +0200 Subject: [PATCH 201/682] Fix GHSA-3cr5-j632-f35r: Null byte in hostnames This fixes stream_socket_client() and fsockopen(). Specifically it adds a check to parse_ip_address_ex and it also makes sure that the \0 is not ignored in fsockopen() hostname formatting. --- ext/standard/fsock.c | 27 +++++++++++++++++-- .../tests/network/ghsa-3cr5-j632-f35r.phpt | 21 +++++++++++++++ .../tests/streams/ghsa-3cr5-j632-f35r.phpt | 26 ++++++++++++++++++ main/streams/xp_socket.c | 9 ++++--- 4 files changed, 78 insertions(+), 5 deletions(-) create mode 100644 ext/standard/tests/network/ghsa-3cr5-j632-f35r.phpt create mode 100644 ext/standard/tests/streams/ghsa-3cr5-j632-f35r.phpt diff --git a/ext/standard/fsock.c b/ext/standard/fsock.c index cb7a471e935a6..2b9e00a57554c 100644 --- a/ext/standard/fsock.c +++ b/ext/standard/fsock.c @@ -23,6 +23,28 @@ #include "php_network.h" #include "file.h" +static size_t php_fsockopen_format_host_port(char **message, const char *prefix, size_t prefix_len, + const char *host, size_t host_len, zend_long port) +{ + char portbuf[32]; + int portlen = snprintf(portbuf, sizeof(portbuf), ":" ZEND_LONG_FMT, port); + size_t total_len = prefix_len + host_len + portlen; + + char *result = emalloc(total_len + 1); + + if (prefix_len > 0) { + memcpy(result, prefix, prefix_len); + } + memcpy(result + prefix_len, host, host_len); + memcpy(result + prefix_len + host_len, portbuf, portlen); + + result[total_len] = '\0'; + + *message = result; + + return total_len; +} + /* {{{ php_fsockopen() */ static void php_fsockopen_stream(INTERNAL_FUNCTION_PARAMETERS, int persistent) @@ -62,11 +84,12 @@ static void php_fsockopen_stream(INTERNAL_FUNCTION_PARAMETERS, int persistent) } if (persistent) { - spprintf(&hashkey, 0, "pfsockopen__%s:" ZEND_LONG_FMT, host, port); + php_fsockopen_format_host_port(&hashkey, "pfsockopen__", strlen("pfsockopen__"), host, + host_len, port); } if (port > 0) { - hostname_len = spprintf(&hostname, 0, "%s:" ZEND_LONG_FMT, host, port); + hostname_len = php_fsockopen_format_host_port(&hostname, "", 0, host, host_len, port); } else { hostname_len = host_len; hostname = host; diff --git a/ext/standard/tests/network/ghsa-3cr5-j632-f35r.phpt b/ext/standard/tests/network/ghsa-3cr5-j632-f35r.phpt new file mode 100644 index 0000000000000..7556c3be94ccd --- /dev/null +++ b/ext/standard/tests/network/ghsa-3cr5-j632-f35r.phpt @@ -0,0 +1,21 @@ +--TEST-- +GHSA-3cr5-j632-f35r: Null byte termination in fsockopen() +--FILE-- + +--EXPECTF-- + +Warning: fsockopen(): Unable to connect to localhost:%d (The hostname must not contain null bytes) in %s +bool(false) diff --git a/ext/standard/tests/streams/ghsa-3cr5-j632-f35r.phpt b/ext/standard/tests/streams/ghsa-3cr5-j632-f35r.phpt new file mode 100644 index 0000000000000..52f9263c99aaa --- /dev/null +++ b/ext/standard/tests/streams/ghsa-3cr5-j632-f35r.phpt @@ -0,0 +1,26 @@ +--TEST-- +GHSA-3cr5-j632-f35r: Null byte termination in stream_socket_client() +--FILE-- + +--EXPECTF-- + +Warning: stream_socket_client(): Unable to connect to tcp://localhost\0.example.com:%d (The hostname must not contain null bytes) in %s +bool(false) diff --git a/main/streams/xp_socket.c b/main/streams/xp_socket.c index 3d035de6edb21..8623c11be004c 100644 --- a/main/streams/xp_socket.c +++ b/main/streams/xp_socket.c @@ -620,12 +620,15 @@ static inline char *parse_ip_address_ex(const char *str, size_t str_len, int *po char *colon; char *host = NULL; -#ifdef HAVE_IPV6 - char *p; + if (memchr(str, '\0', str_len)) { + *err = ZSTR_INIT_LITERAL("The hostname must not contain null bytes", 0); + return NULL; + } +#ifdef HAVE_IPV6 if (*(str) == '[' && str_len > 1) { /* IPV6 notation to specify raw address with port (i.e. [fe80::1]:80) */ - p = memchr(str + 1, ']', str_len - 2); + char *p = memchr(str + 1, ']', str_len - 2); if (!p || *(p + 1) != ':') { if (get_err) { *err = strpprintf(0, "Failed to parse IPv6 address \"%s\"", str); From 66bd809ac922338201f4efaafdc56755afafa37a Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Tue, 4 Mar 2025 17:23:01 +0100 Subject: [PATCH 202/682] Fix GHSA-hrwm-9436-5mv3: pgsql escaping no error checks This adds error checks for escape function is pgsql and pdo_pgsql extensions. It prevents possibility of storing not properly escaped data which could potentially lead to some security issues. --- ext/pdo_pgsql/pgsql_driver.c | 10 +- ext/pdo_pgsql/tests/ghsa-hrwm-9436-5mv3.phpt | 24 ++++ ext/pgsql/pgsql.c | 125 ++++++++++++++++--- ext/pgsql/tests/ghsa-hrwm-9436-5mv3.phpt | 64 ++++++++++ 4 files changed, 202 insertions(+), 21 deletions(-) create mode 100644 ext/pdo_pgsql/tests/ghsa-hrwm-9436-5mv3.phpt create mode 100644 ext/pgsql/tests/ghsa-hrwm-9436-5mv3.phpt diff --git a/ext/pdo_pgsql/pgsql_driver.c b/ext/pdo_pgsql/pgsql_driver.c index 684f7798a45f8..fc4b5b7c6640d 100644 --- a/ext/pdo_pgsql/pgsql_driver.c +++ b/ext/pdo_pgsql/pgsql_driver.c @@ -367,11 +367,15 @@ static zend_string* pgsql_handle_quoter(pdo_dbh_t *dbh, const zend_string *unquo zend_string *quoted_str; pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; size_t tmp_len; + int err; switch (paramtype) { case PDO_PARAM_LOB: /* escapedlen returned by PQescapeBytea() accounts for trailing 0 */ escaped = PQescapeByteaConn(H->server, (unsigned char *)ZSTR_VAL(unquoted), ZSTR_LEN(unquoted), &tmp_len); + if (escaped == NULL) { + return NULL; + } quotedlen = tmp_len + 1; quoted = emalloc(quotedlen + 1); memcpy(quoted+1, escaped, quotedlen-2); @@ -383,7 +387,11 @@ static zend_string* pgsql_handle_quoter(pdo_dbh_t *dbh, const zend_string *unquo default: quoted = safe_emalloc(2, ZSTR_LEN(unquoted), 3); quoted[0] = '\''; - quotedlen = PQescapeStringConn(H->server, quoted + 1, ZSTR_VAL(unquoted), ZSTR_LEN(unquoted), NULL); + quotedlen = PQescapeStringConn(H->server, quoted + 1, ZSTR_VAL(unquoted), ZSTR_LEN(unquoted), &err); + if (err) { + efree(quoted); + return NULL; + } quoted[quotedlen + 1] = '\''; quoted[quotedlen + 2] = '\0'; quotedlen += 2; diff --git a/ext/pdo_pgsql/tests/ghsa-hrwm-9436-5mv3.phpt b/ext/pdo_pgsql/tests/ghsa-hrwm-9436-5mv3.phpt new file mode 100644 index 0000000000000..8566a26753b40 --- /dev/null +++ b/ext/pdo_pgsql/tests/ghsa-hrwm-9436-5mv3.phpt @@ -0,0 +1,24 @@ +--TEST-- +#GHSA-hrwm-9436-5mv3: pdo_pgsql extension does not check for errors during escaping +--EXTENSIONS-- +pdo +pdo_pgsql +--SKIPIF-- + +--FILE-- +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + +$invalid = "ABC\xff\x30';"; +var_dump($db->quote($invalid)); + +?> +--EXPECT-- +bool(false) diff --git a/ext/pgsql/pgsql.c b/ext/pgsql/pgsql.c index 4e5020d8c0965..7e43360e61d81 100644 --- a/ext/pgsql/pgsql.c +++ b/ext/pgsql/pgsql.c @@ -3528,8 +3528,14 @@ PHP_FUNCTION(pg_escape_string) to = zend_string_safe_alloc(ZSTR_LEN(from), 2, 0, 0); if (link) { + int err; pgsql = link->conn; - ZSTR_LEN(to) = PQescapeStringConn(pgsql, ZSTR_VAL(to), ZSTR_VAL(from), ZSTR_LEN(from), NULL); + ZSTR_LEN(to) = PQescapeStringConn(pgsql, ZSTR_VAL(to), ZSTR_VAL(from), ZSTR_LEN(from), &err); + if (err) { + zend_argument_value_error(ZEND_NUM_ARGS(), "Escaping string failed"); + zend_string_efree(to); + RETURN_THROWS(); + } } else { ZSTR_LEN(to) = PQescapeString(ZSTR_VAL(to), ZSTR_VAL(from), ZSTR_LEN(from)); @@ -3575,6 +3581,10 @@ PHP_FUNCTION(pg_escape_bytea) } else { to = (char *)PQescapeBytea((unsigned char *)ZSTR_VAL(from), ZSTR_LEN(from), &to_len); } + if (to == NULL) { + zend_argument_value_error(ZEND_NUM_ARGS(), "Escape failure"); + RETURN_THROWS(); + } RETVAL_STRINGL(to, to_len-1); /* to_len includes additional '\0' */ PQfreemem(to); @@ -4523,7 +4533,7 @@ PHP_PGSQL_API zend_result php_pgsql_meta_data(PGconn *pg_link, const zend_string char *escaped; smart_str querystr = {0}; size_t new_len, len; - int i, num_rows; + int i, num_rows, err; zval elem; ZEND_ASSERT(ZSTR_LEN(table_name) != 0); @@ -4562,7 +4572,14 @@ PHP_PGSQL_API zend_result php_pgsql_meta_data(PGconn *pg_link, const zend_string } len = strlen(tmp_name2); escaped = (char *)safe_emalloc(len, 2, 1); - new_len = PQescapeStringConn(pg_link, escaped, tmp_name2, len, NULL); + new_len = PQescapeStringConn(pg_link, escaped, tmp_name2, len, &err); + if (err) { + php_error_docref(NULL, E_WARNING, "Escaping table name '%s' failed", ZSTR_VAL(table_name)); + efree(src); + efree(escaped); + smart_str_free(&querystr); + return FAILURE; + } if (new_len) { smart_str_appendl(&querystr, escaped, new_len); } @@ -4571,7 +4588,14 @@ PHP_PGSQL_API zend_result php_pgsql_meta_data(PGconn *pg_link, const zend_string smart_str_appends(&querystr, "' AND n.nspname = '"); len = strlen(tmp_name); escaped = (char *)safe_emalloc(len, 2, 1); - new_len = PQescapeStringConn(pg_link, escaped, tmp_name, len, NULL); + new_len = PQescapeStringConn(pg_link, escaped, tmp_name, len, &err); + if (err) { + php_error_docref(NULL, E_WARNING, "Escaping table namespace '%s' failed", ZSTR_VAL(table_name)); + efree(src); + efree(escaped); + smart_str_free(&querystr); + return FAILURE; + } if (new_len) { smart_str_appendl(&querystr, escaped, new_len); } @@ -4826,7 +4850,7 @@ PHP_PGSQL_API zend_result php_pgsql_convert(PGconn *pg_link, const zend_string * { zend_string *field = NULL; zval meta, *def, *type, *not_null, *has_default, *is_enum, *val, new_val; - int err = 0, skip_field; + int err = 0, escape_err = 0, skip_field; php_pgsql_data_type data_type; ZEND_ASSERT(pg_link != NULL); @@ -5072,8 +5096,13 @@ PHP_PGSQL_API zend_result php_pgsql_convert(PGconn *pg_link, const zend_string * /* PostgreSQL ignores \0 */ str = zend_string_alloc(Z_STRLEN_P(val) * 2, 0); /* better to use PGSQLescapeLiteral since PGescapeStringConn does not handle special \ */ - ZSTR_LEN(str) = PQescapeStringConn(pg_link, ZSTR_VAL(str), Z_STRVAL_P(val), Z_STRLEN_P(val), NULL); - ZVAL_STR(&new_val, php_pgsql_add_quotes(str)); + ZSTR_LEN(str) = PQescapeStringConn(pg_link, ZSTR_VAL(str), + Z_STRVAL_P(val), Z_STRLEN_P(val), &escape_err); + if (escape_err) { + err = 1; + } else { + ZVAL_STR(&new_val, php_pgsql_add_quotes(str)); + } zend_string_release_ex(str, false); } break; @@ -5096,7 +5125,14 @@ PHP_PGSQL_API zend_result php_pgsql_convert(PGconn *pg_link, const zend_string * } PGSQL_CONV_CHECK_IGNORE(); if (err) { - zend_type_error("%s(): Field \"%s\" must be of type string|null, %s given", get_active_function_name(), ZSTR_VAL(field), Z_STRVAL_P(type)); + if (escape_err) { + php_error_docref(NULL, E_NOTICE, + "String value escaping failed for PostgreSQL '%s' (%s)", + Z_STRVAL_P(type), ZSTR_VAL(field)); + } else { + zend_type_error("%s(): Field \"%s\" must be of type string|null, %s given", + get_active_function_name(), ZSTR_VAL(field), Z_STRVAL_P(type)); + } } break; @@ -5330,6 +5366,11 @@ PHP_PGSQL_API zend_result php_pgsql_convert(PGconn *pg_link, const zend_string * zend_string *tmp_zstr; tmp = PQescapeByteaConn(pg_link, (unsigned char *)Z_STRVAL_P(val), Z_STRLEN_P(val), &to_len); + if (tmp == NULL) { + php_error_docref(NULL, E_NOTICE, "Escaping value failed for %s field (%s)", Z_STRVAL_P(type), ZSTR_VAL(field)); + err = 1; + break; + } tmp_zstr = zend_string_init((char *)tmp, to_len - 1, false); /* PQescapeBytea's to_len includes additional '\0' */ PQfreemem(tmp); @@ -5406,6 +5447,12 @@ PHP_PGSQL_API zend_result php_pgsql_convert(PGconn *pg_link, const zend_string * zend_hash_update(Z_ARRVAL_P(result), field, &new_val); } else { char *escaped = PQescapeIdentifier(pg_link, ZSTR_VAL(field), ZSTR_LEN(field)); + if (escaped == NULL) { + /* This cannot fail because of invalid string but only due to failed memory allocation */ + php_error_docref(NULL, E_NOTICE, "Escaping field '%s' failed", ZSTR_VAL(field)); + err = 1; + break; + } add_assoc_zval(result, escaped, &new_val); PQfreemem(escaped); } @@ -5488,7 +5535,7 @@ static bool do_exec(smart_str *querystr, ExecStatusType expect, PGconn *pg_link, } /* }}} */ -static inline void build_tablename(smart_str *querystr, PGconn *pg_link, const zend_string *table) /* {{{ */ +static inline zend_result build_tablename(smart_str *querystr, PGconn *pg_link, const zend_string *table) /* {{{ */ { /* schema.table should be "schema"."table" */ const char *dot = memchr(ZSTR_VAL(table), '.', ZSTR_LEN(table)); @@ -5498,6 +5545,10 @@ static inline void build_tablename(smart_str *querystr, PGconn *pg_link, const z smart_str_appendl(querystr, ZSTR_VAL(table), len); } else { char *escaped = PQescapeIdentifier(pg_link, ZSTR_VAL(table), len); + if (escaped == NULL) { + php_error_docref(NULL, E_NOTICE, "Failed to escape table name '%s'", ZSTR_VAL(table)); + return FAILURE; + } smart_str_appends(querystr, escaped); PQfreemem(escaped); } @@ -5510,11 +5561,17 @@ static inline void build_tablename(smart_str *querystr, PGconn *pg_link, const z smart_str_appendl(querystr, after_dot, len); } else { char *escaped = PQescapeIdentifier(pg_link, after_dot, len); + if (escaped == NULL) { + php_error_docref(NULL, E_NOTICE, "Failed to escape table name '%s'", ZSTR_VAL(table)); + return FAILURE; + } smart_str_appendc(querystr, '.'); smart_str_appends(querystr, escaped); PQfreemem(escaped); } } + + return SUCCESS; } /* }}} */ @@ -5535,7 +5592,9 @@ PHP_PGSQL_API zend_result php_pgsql_insert(PGconn *pg_link, const zend_string *t ZVAL_UNDEF(&converted); if (zend_hash_num_elements(Z_ARRVAL_P(var_array)) == 0) { smart_str_appends(&querystr, "INSERT INTO "); - build_tablename(&querystr, pg_link, table); + if (build_tablename(&querystr, pg_link, table) == FAILURE) { + goto cleanup; + } smart_str_appends(&querystr, " DEFAULT VALUES"); goto no_values; @@ -5551,7 +5610,9 @@ PHP_PGSQL_API zend_result php_pgsql_insert(PGconn *pg_link, const zend_string *t } smart_str_appends(&querystr, "INSERT INTO "); - build_tablename(&querystr, pg_link, table); + if (build_tablename(&querystr, pg_link, table) == FAILURE) { + goto cleanup; + } smart_str_appends(&querystr, " ("); ZEND_HASH_FOREACH_STR_KEY(Z_ARRVAL_P(var_array), fld) { @@ -5561,6 +5622,10 @@ PHP_PGSQL_API zend_result php_pgsql_insert(PGconn *pg_link, const zend_string *t } if (opt & PGSQL_DML_ESCAPE) { tmp = PQescapeIdentifier(pg_link, ZSTR_VAL(fld), ZSTR_LEN(fld) + 1); + if (tmp == NULL) { + php_error_docref(NULL, E_NOTICE, "Failed to escape field '%s'", ZSTR_VAL(fld)); + goto cleanup; + } smart_str_appends(&querystr, tmp); PQfreemem(tmp); } else { @@ -5572,15 +5637,19 @@ PHP_PGSQL_API zend_result php_pgsql_insert(PGconn *pg_link, const zend_string *t smart_str_appends(&querystr, ") VALUES ("); /* make values string */ - ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(var_array), val) { + ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(var_array), fld, val) { /* we can avoid the key_type check here, because we tested it in the other loop */ switch (Z_TYPE_P(val)) { case IS_STRING: if (opt & PGSQL_DML_ESCAPE) { - size_t new_len; - char *tmp; - tmp = (char *)safe_emalloc(Z_STRLEN_P(val), 2, 1); - new_len = PQescapeStringConn(pg_link, tmp, Z_STRVAL_P(val), Z_STRLEN_P(val), NULL); + int error; + char *tmp = safe_emalloc(Z_STRLEN_P(val), 2, 1); + size_t new_len = PQescapeStringConn(pg_link, tmp, Z_STRVAL_P(val), Z_STRLEN_P(val), &error); + if (error) { + php_error_docref(NULL, E_NOTICE, "Failed to escape field '%s' value", ZSTR_VAL(fld)); + efree(tmp); + goto cleanup; + } smart_str_appendc(&querystr, '\''); smart_str_appendl(&querystr, tmp, new_len); smart_str_appendc(&querystr, '\''); @@ -5738,6 +5807,10 @@ static inline int build_assignment_string(PGconn *pg_link, smart_str *querystr, } if (opt & PGSQL_DML_ESCAPE) { char *tmp = PQescapeIdentifier(pg_link, ZSTR_VAL(fld), ZSTR_LEN(fld) + 1); + if (tmp == NULL) { + php_error_docref(NULL, E_NOTICE, "Failed to escape field '%s'", ZSTR_VAL(fld)); + return -1; + } smart_str_appends(querystr, tmp); PQfreemem(tmp); } else { @@ -5753,8 +5826,14 @@ static inline int build_assignment_string(PGconn *pg_link, smart_str *querystr, switch (Z_TYPE_P(val)) { case IS_STRING: if (opt & PGSQL_DML_ESCAPE) { + int error; char *tmp = (char *)safe_emalloc(Z_STRLEN_P(val), 2, 1); - size_t new_len = PQescapeStringConn(pg_link, tmp, Z_STRVAL_P(val), Z_STRLEN_P(val), NULL); + size_t new_len = PQescapeStringConn(pg_link, tmp, Z_STRVAL_P(val), Z_STRLEN_P(val), &error); + if (error) { + php_error_docref(NULL, E_NOTICE, "Failed to escape field '%s' value", ZSTR_VAL(fld)); + efree(tmp); + return -1; + } smart_str_appendc(querystr, '\''); smart_str_appendl(querystr, tmp, new_len); smart_str_appendc(querystr, '\''); @@ -5822,7 +5901,9 @@ PHP_PGSQL_API zend_result php_pgsql_update(PGconn *pg_link, const zend_string *t } smart_str_appends(&querystr, "UPDATE "); - build_tablename(&querystr, pg_link, table); + if (build_tablename(&querystr, pg_link, table) == FAILURE) { + goto cleanup; + } smart_str_appends(&querystr, " SET "); if (build_assignment_string(pg_link, &querystr, Z_ARRVAL_P(var_array), 0, ",", 1, opt)) @@ -5928,7 +6009,9 @@ PHP_PGSQL_API zend_result php_pgsql_delete(PGconn *pg_link, const zend_string *t } smart_str_appends(&querystr, "DELETE FROM "); - build_tablename(&querystr, pg_link, table); + if (build_tablename(&querystr, pg_link, table) == FAILURE) { + goto cleanup; + } smart_str_appends(&querystr, " WHERE "); if (build_assignment_string(pg_link, &querystr, Z_ARRVAL_P(ids_array), 1, " AND ", sizeof(" AND ")-1, opt)) @@ -6072,7 +6155,9 @@ PHP_PGSQL_API zend_result php_pgsql_select(PGconn *pg_link, const zend_string *t } smart_str_appends(&querystr, "SELECT * FROM "); - build_tablename(&querystr, pg_link, table); + if (build_tablename(&querystr, pg_link, table) == FAILURE) { + goto cleanup; + } if (is_valid_ids_array) { smart_str_appends(&querystr, " WHERE "); diff --git a/ext/pgsql/tests/ghsa-hrwm-9436-5mv3.phpt b/ext/pgsql/tests/ghsa-hrwm-9436-5mv3.phpt new file mode 100644 index 0000000000000..6cbfe6d1f5859 --- /dev/null +++ b/ext/pgsql/tests/ghsa-hrwm-9436-5mv3.phpt @@ -0,0 +1,64 @@ +--TEST-- +#GHSA-hrwm-9436-5mv3: pgsql extension does not check for errors during escaping +--EXTENSIONS-- +pgsql +--SKIPIF-- + +--FILE-- + 'test'])); // table name str escape in php_pgsql_meta_data +var_dump(pg_insert($db, "$invalid.tbl", ['bar' => 'test'])); // schema name str escape in php_pgsql_meta_data +var_dump(pg_insert($db, 'ghsa_hrmw_9436_5mv3', ['bar' => $invalid])); // converted value str escape in php_pgsql_convert +var_dump(pg_insert($db, $invalid, [])); // ident escape in build_tablename +var_dump(pg_insert($db, 'ghsa_hrmw_9436_5mv3', [$invalid => 'foo'], $flags)); // ident escape for field php_pgsql_insert +var_dump(pg_insert($db, 'ghsa_hrmw_9436_5mv3', ['bar' => $invalid], $flags)); // str escape for field value in php_pgsql_insert +var_dump(pg_update($db, 'ghsa_hrmw_9436_5mv3', ['bar' => 'val'], [$invalid => 'test'], $flags)); // ident escape in build_assignment_string +var_dump(pg_update($db, 'ghsa_hrmw_9436_5mv3', ['bar' => 'val'], ['bar' => $invalid], $flags)); // invalid str escape in build_assignment_string +var_dump(pg_escape_literal($db, $invalid)); // pg_escape_literal escape +var_dump(pg_escape_identifier($db, $invalid)); // pg_escape_identifier escape + +?> +--EXPECTF-- + +Warning: pg_insert(): Escaping table name 'ABC%s';' failed in %s on line %d +bool(false) + +Warning: pg_insert(): Escaping table namespace 'ABC%s';.tbl' failed in %s on line %d +bool(false) + +Notice: pg_insert(): String value escaping failed for PostgreSQL 'text' (bar) in %s on line %d +bool(false) + +Notice: pg_insert(): Failed to escape table name 'ABC%s';' in %s on line %d +bool(false) + +Notice: pg_insert(): Failed to escape field 'ABC%s';' in %s on line %d +bool(false) + +Notice: pg_insert(): Failed to escape field 'bar' value in %s on line %d +bool(false) + +Notice: pg_update(): Failed to escape field 'ABC%s';' in %s on line %d +bool(false) + +Notice: pg_update(): Failed to escape field 'bar' value in %s on line %d +bool(false) + +Warning: pg_escape_literal(): Failed to escape in %s on line %d +bool(false) + +Warning: pg_escape_identifier(): Failed to escape in %s on line %d +bool(false) From a179e39c38001af3d40a0dbd7e0e57bbd7b66814 Mon Sep 17 00:00:00 2001 From: Ahmed Lekssays Date: Tue, 3 Jun 2025 09:00:55 +0000 Subject: [PATCH 203/682] Fix GHSA-453j-q27h-5p8x Libxml versions prior to 2.13 cannot correctly handle a call to xmlNodeSetName() with a name longer than 2G. It will leave the node object in an invalid state with a NULL name. This later causes a NULL pointer dereference when using the name during message serialization. To solve this, implement a workaround that resets the name to the sentinel name if this situation arises. Versions of libxml of 2.13 and higher are not affected. This can be exploited if a SoapVar is created with a fully qualified name that is longer than 2G. This would be possible if some application code uses a namespace prefix from an untrusted source like from a remote SOAP service. Co-authored-by: Niels Dossche <7771979+nielsdos@users.noreply.github.com> --- ext/soap/soap.c | 6 ++-- ext/soap/tests/soap_qname_crash.phpt | 48 ++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 2 deletions(-) create mode 100644 ext/soap/tests/soap_qname_crash.phpt diff --git a/ext/soap/soap.c b/ext/soap/soap.c index 6a718f3af14ae..afd4be8c24c0a 100644 --- a/ext/soap/soap.c +++ b/ext/soap/soap.c @@ -4134,8 +4134,10 @@ static xmlNodePtr serialize_zval(zval *val, sdlParamPtr param, const char *param } xmlParam = master_to_xml(enc, val, style, parent); zval_ptr_dtor(&defval); - if (!strcmp((char*)xmlParam->name, "BOGUS")) { - xmlNodeSetName(xmlParam, BAD_CAST(paramName)); + if (xmlParam != NULL) { + if (xmlParam->name == NULL || strcmp((char*)xmlParam->name, "BOGUS") == 0) { + xmlNodeSetName(xmlParam, BAD_CAST(paramName)); + } } return xmlParam; } diff --git a/ext/soap/tests/soap_qname_crash.phpt b/ext/soap/tests/soap_qname_crash.phpt new file mode 100644 index 0000000000000..bcf01d574fab4 --- /dev/null +++ b/ext/soap/tests/soap_qname_crash.phpt @@ -0,0 +1,48 @@ +--TEST-- +Test SoapClient with excessively large QName prefix in SoapVar +--EXTENSIONS-- +soap +--SKIPIF-- + +--INI-- +memory_limit=6144M +--FILE-- + 'http://127.0.0.1/', + 'uri' => 'urn:dummy', + 'trace' => 1, + 'exceptions' => true, +]; +$client = new TestSoapClient(null, $options); +$client->__soapCall("DummyFunction", [$var]); +?> +--EXPECT-- +Attempting to create SoapVar with very large QName +Attempting encoding + +value From c57ec92eb6afc7dd89559f8c145ff47b5c2d1065 Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Tue, 1 Jul 2025 11:41:24 +0200 Subject: [PATCH 204/682] Fix missing HAVE_JIT guard Closes GH-18993 --- ext/opcache/zend_persist.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ext/opcache/zend_persist.c b/ext/opcache/zend_persist.c index 71e5bb540078f..f8e11e4d01ec9 100644 --- a/ext/opcache/zend_persist.c +++ b/ext/opcache/zend_persist.c @@ -1262,6 +1262,7 @@ void zend_update_parent_ce(zend_class_entry *ce) } } +#ifdef HAVE_JIT static void zend_accel_persist_jit_op_array(zend_op_array *op_array, zend_class_entry *ce) { if (op_array->type == ZEND_USER_FUNCTION) { @@ -1294,6 +1295,7 @@ static void zend_accel_persist_link_func_info(zend_op_array *op_array, zend_clas } } } +#endif static void zend_accel_persist_class_table(HashTable *class_table) { From cf0c39723ee05fac979e8f11a0f0c0645e61a83a Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Thu, 10 Apr 2025 15:15:36 +0200 Subject: [PATCH 205/682] Fix GHSA-3cr5-j632-f35r: Null byte in hostnames This fixes stream_socket_client() and fsockopen(). Specifically it adds a check to parse_ip_address_ex and it also makes sure that the \0 is not ignored in fsockopen() hostname formatting. --- ext/standard/fsock.c | 27 +++++++++++++++++-- .../tests/network/ghsa-3cr5-j632-f35r.phpt | 21 +++++++++++++++ .../tests/streams/ghsa-3cr5-j632-f35r.phpt | 26 ++++++++++++++++++ main/streams/xp_socket.c | 9 ++++--- 4 files changed, 78 insertions(+), 5 deletions(-) create mode 100644 ext/standard/tests/network/ghsa-3cr5-j632-f35r.phpt create mode 100644 ext/standard/tests/streams/ghsa-3cr5-j632-f35r.phpt diff --git a/ext/standard/fsock.c b/ext/standard/fsock.c index cb7a471e935a6..2b9e00a57554c 100644 --- a/ext/standard/fsock.c +++ b/ext/standard/fsock.c @@ -23,6 +23,28 @@ #include "php_network.h" #include "file.h" +static size_t php_fsockopen_format_host_port(char **message, const char *prefix, size_t prefix_len, + const char *host, size_t host_len, zend_long port) +{ + char portbuf[32]; + int portlen = snprintf(portbuf, sizeof(portbuf), ":" ZEND_LONG_FMT, port); + size_t total_len = prefix_len + host_len + portlen; + + char *result = emalloc(total_len + 1); + + if (prefix_len > 0) { + memcpy(result, prefix, prefix_len); + } + memcpy(result + prefix_len, host, host_len); + memcpy(result + prefix_len + host_len, portbuf, portlen); + + result[total_len] = '\0'; + + *message = result; + + return total_len; +} + /* {{{ php_fsockopen() */ static void php_fsockopen_stream(INTERNAL_FUNCTION_PARAMETERS, int persistent) @@ -62,11 +84,12 @@ static void php_fsockopen_stream(INTERNAL_FUNCTION_PARAMETERS, int persistent) } if (persistent) { - spprintf(&hashkey, 0, "pfsockopen__%s:" ZEND_LONG_FMT, host, port); + php_fsockopen_format_host_port(&hashkey, "pfsockopen__", strlen("pfsockopen__"), host, + host_len, port); } if (port > 0) { - hostname_len = spprintf(&hostname, 0, "%s:" ZEND_LONG_FMT, host, port); + hostname_len = php_fsockopen_format_host_port(&hostname, "", 0, host, host_len, port); } else { hostname_len = host_len; hostname = host; diff --git a/ext/standard/tests/network/ghsa-3cr5-j632-f35r.phpt b/ext/standard/tests/network/ghsa-3cr5-j632-f35r.phpt new file mode 100644 index 0000000000000..7556c3be94ccd --- /dev/null +++ b/ext/standard/tests/network/ghsa-3cr5-j632-f35r.phpt @@ -0,0 +1,21 @@ +--TEST-- +GHSA-3cr5-j632-f35r: Null byte termination in fsockopen() +--FILE-- + +--EXPECTF-- + +Warning: fsockopen(): Unable to connect to localhost:%d (The hostname must not contain null bytes) in %s +bool(false) diff --git a/ext/standard/tests/streams/ghsa-3cr5-j632-f35r.phpt b/ext/standard/tests/streams/ghsa-3cr5-j632-f35r.phpt new file mode 100644 index 0000000000000..52f9263c99aaa --- /dev/null +++ b/ext/standard/tests/streams/ghsa-3cr5-j632-f35r.phpt @@ -0,0 +1,26 @@ +--TEST-- +GHSA-3cr5-j632-f35r: Null byte termination in stream_socket_client() +--FILE-- + +--EXPECTF-- + +Warning: stream_socket_client(): Unable to connect to tcp://localhost\0.example.com:%d (The hostname must not contain null bytes) in %s +bool(false) diff --git a/main/streams/xp_socket.c b/main/streams/xp_socket.c index 606d1499456a2..b1d89bc44cb2b 100644 --- a/main/streams/xp_socket.c +++ b/main/streams/xp_socket.c @@ -616,12 +616,15 @@ static inline char *parse_ip_address_ex(const char *str, size_t str_len, int *po char *colon; char *host = NULL; -#ifdef HAVE_IPV6 - char *p; + if (memchr(str, '\0', str_len)) { + *err = ZSTR_INIT_LITERAL("The hostname must not contain null bytes", 0); + return NULL; + } +#ifdef HAVE_IPV6 if (*(str) == '[' && str_len > 1) { /* IPV6 notation to specify raw address with port (i.e. [fe80::1]:80) */ - p = memchr(str + 1, ']', str_len - 2); + char *p = memchr(str + 1, ']', str_len - 2); if (!p || *(p + 1) != ':') { if (get_err) { *err = strpprintf(0, "Failed to parse IPv6 address \"%s\"", str); From 545d1536d8b2c16ff9c844791d8d9d39530e0313 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Tue, 4 Mar 2025 17:23:01 +0100 Subject: [PATCH 206/682] Fix GHSA-hrwm-9436-5mv3: pgsql escaping no error checks This adds error checks for escape function is pgsql and pdo_pgsql extensions. It prevents possibility of storing not properly escaped data which could potentially lead to some security issues. --- ext/pdo_pgsql/pgsql_driver.c | 10 +- ext/pdo_pgsql/tests/ghsa-hrwm-9436-5mv3.phpt | 24 ++++ ext/pgsql/pgsql.c | 125 ++++++++++++++++--- ext/pgsql/tests/ghsa-hrwm-9436-5mv3.phpt | 64 ++++++++++ 4 files changed, 202 insertions(+), 21 deletions(-) create mode 100644 ext/pdo_pgsql/tests/ghsa-hrwm-9436-5mv3.phpt create mode 100644 ext/pgsql/tests/ghsa-hrwm-9436-5mv3.phpt diff --git a/ext/pdo_pgsql/pgsql_driver.c b/ext/pdo_pgsql/pgsql_driver.c index 46b3f25f4086b..1cccfd2ab07ae 100644 --- a/ext/pdo_pgsql/pgsql_driver.c +++ b/ext/pdo_pgsql/pgsql_driver.c @@ -353,11 +353,15 @@ static zend_string* pgsql_handle_quoter(pdo_dbh_t *dbh, const zend_string *unquo zend_string *quoted_str; pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; size_t tmp_len; + int err; switch (paramtype) { case PDO_PARAM_LOB: /* escapedlen returned by PQescapeBytea() accounts for trailing 0 */ escaped = PQescapeByteaConn(H->server, (unsigned char *)ZSTR_VAL(unquoted), ZSTR_LEN(unquoted), &tmp_len); + if (escaped == NULL) { + return NULL; + } quotedlen = tmp_len + 1; quoted = emalloc(quotedlen + 1); memcpy(quoted+1, escaped, quotedlen-2); @@ -369,7 +373,11 @@ static zend_string* pgsql_handle_quoter(pdo_dbh_t *dbh, const zend_string *unquo default: quoted = safe_emalloc(2, ZSTR_LEN(unquoted), 3); quoted[0] = '\''; - quotedlen = PQescapeStringConn(H->server, quoted + 1, ZSTR_VAL(unquoted), ZSTR_LEN(unquoted), NULL); + quotedlen = PQescapeStringConn(H->server, quoted + 1, ZSTR_VAL(unquoted), ZSTR_LEN(unquoted), &err); + if (err) { + efree(quoted); + return NULL; + } quoted[quotedlen + 1] = '\''; quoted[quotedlen + 2] = '\0'; quotedlen += 2; diff --git a/ext/pdo_pgsql/tests/ghsa-hrwm-9436-5mv3.phpt b/ext/pdo_pgsql/tests/ghsa-hrwm-9436-5mv3.phpt new file mode 100644 index 0000000000000..8566a26753b40 --- /dev/null +++ b/ext/pdo_pgsql/tests/ghsa-hrwm-9436-5mv3.phpt @@ -0,0 +1,24 @@ +--TEST-- +#GHSA-hrwm-9436-5mv3: pdo_pgsql extension does not check for errors during escaping +--EXTENSIONS-- +pdo +pdo_pgsql +--SKIPIF-- + +--FILE-- +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + +$invalid = "ABC\xff\x30';"; +var_dump($db->quote($invalid)); + +?> +--EXPECT-- +bool(false) diff --git a/ext/pgsql/pgsql.c b/ext/pgsql/pgsql.c index 63acd26ea01f5..11ce814cbec0f 100644 --- a/ext/pgsql/pgsql.c +++ b/ext/pgsql/pgsql.c @@ -3269,8 +3269,14 @@ PHP_FUNCTION(pg_escape_string) to = zend_string_safe_alloc(ZSTR_LEN(from), 2, 0, 0); if (link) { + int err; pgsql = link->conn; - ZSTR_LEN(to) = PQescapeStringConn(pgsql, ZSTR_VAL(to), ZSTR_VAL(from), ZSTR_LEN(from), NULL); + ZSTR_LEN(to) = PQescapeStringConn(pgsql, ZSTR_VAL(to), ZSTR_VAL(from), ZSTR_LEN(from), &err); + if (err) { + zend_argument_value_error(ZEND_NUM_ARGS(), "Escaping string failed"); + zend_string_efree(to); + RETURN_THROWS(); + } } else { ZSTR_LEN(to) = PQescapeString(ZSTR_VAL(to), ZSTR_VAL(from), ZSTR_LEN(from)); @@ -3313,6 +3319,10 @@ PHP_FUNCTION(pg_escape_bytea) } else { to = (char *)PQescapeBytea((unsigned char *)ZSTR_VAL(from), ZSTR_LEN(from), &to_len); } + if (to == NULL) { + zend_argument_value_error(ZEND_NUM_ARGS(), "Escape failure"); + RETURN_THROWS(); + } RETVAL_STRINGL(to, to_len-1); /* to_len includes additional '\0' */ PQfreemem(to); @@ -4245,7 +4255,7 @@ PHP_PGSQL_API zend_result php_pgsql_meta_data(PGconn *pg_link, const zend_string char *escaped; smart_str querystr = {0}; size_t new_len; - int i, num_rows; + int i, num_rows, err; zval elem; ZEND_ASSERT(ZSTR_LEN(table_name) != 0); @@ -4283,7 +4293,14 @@ PHP_PGSQL_API zend_result php_pgsql_meta_data(PGconn *pg_link, const zend_string "WHERE a.attnum > 0 AND c.relname = '"); } escaped = (char *)safe_emalloc(strlen(tmp_name2), 2, 1); - new_len = PQescapeStringConn(pg_link, escaped, tmp_name2, strlen(tmp_name2), NULL); + new_len = PQescapeStringConn(pg_link, escaped, tmp_name2, strlen(tmp_name2), &err); + if (err) { + php_error_docref(NULL, E_WARNING, "Escaping table name '%s' failed", ZSTR_VAL(table_name)); + efree(src); + efree(escaped); + smart_str_free(&querystr); + return FAILURE; + } if (new_len) { smart_str_appendl(&querystr, escaped, new_len); } @@ -4291,7 +4308,14 @@ PHP_PGSQL_API zend_result php_pgsql_meta_data(PGconn *pg_link, const zend_string smart_str_appends(&querystr, "' AND n.nspname = '"); escaped = (char *)safe_emalloc(strlen(tmp_name), 2, 1); - new_len = PQescapeStringConn(pg_link, escaped, tmp_name, strlen(tmp_name), NULL); + new_len = PQescapeStringConn(pg_link, escaped, tmp_name, strlen(tmp_name), &err); + if (err) { + php_error_docref(NULL, E_WARNING, "Escaping table namespace '%s' failed", ZSTR_VAL(table_name)); + efree(src); + efree(escaped); + smart_str_free(&querystr); + return FAILURE; + } if (new_len) { smart_str_appendl(&querystr, escaped, new_len); } @@ -4552,7 +4576,7 @@ PHP_PGSQL_API zend_result php_pgsql_convert(PGconn *pg_link, const zend_string * { zend_string *field = NULL; zval meta, *def, *type, *not_null, *has_default, *is_enum, *val, new_val; - int err = 0, skip_field; + int err = 0, escape_err = 0, skip_field; php_pgsql_data_type data_type; ZEND_ASSERT(pg_link != NULL); @@ -4804,8 +4828,13 @@ PHP_PGSQL_API zend_result php_pgsql_convert(PGconn *pg_link, const zend_string * /* PostgreSQL ignores \0 */ str = zend_string_alloc(Z_STRLEN_P(val) * 2, 0); /* better to use PGSQLescapeLiteral since PGescapeStringConn does not handle special \ */ - ZSTR_LEN(str) = PQescapeStringConn(pg_link, ZSTR_VAL(str), Z_STRVAL_P(val), Z_STRLEN_P(val), NULL); - ZVAL_STR(&new_val, php_pgsql_add_quotes(str)); + ZSTR_LEN(str) = PQescapeStringConn(pg_link, ZSTR_VAL(str), + Z_STRVAL_P(val), Z_STRLEN_P(val), &escape_err); + if (escape_err) { + err = 1; + } else { + ZVAL_STR(&new_val, php_pgsql_add_quotes(str)); + } zend_string_release_ex(str, false); } break; @@ -4828,7 +4857,14 @@ PHP_PGSQL_API zend_result php_pgsql_convert(PGconn *pg_link, const zend_string * } PGSQL_CONV_CHECK_IGNORE(); if (err) { - zend_type_error("%s(): Field \"%s\" must be of type string|null, %s given", get_active_function_name(), ZSTR_VAL(field), Z_STRVAL_P(type)); + if (escape_err) { + php_error_docref(NULL, E_NOTICE, + "String value escaping failed for PostgreSQL '%s' (%s)", + Z_STRVAL_P(type), ZSTR_VAL(field)); + } else { + zend_type_error("%s(): Field \"%s\" must be of type string|null, %s given", + get_active_function_name(), ZSTR_VAL(field), Z_STRVAL_P(type)); + } } break; @@ -5103,6 +5139,11 @@ PHP_PGSQL_API zend_result php_pgsql_convert(PGconn *pg_link, const zend_string * zend_string *tmp_zstr; tmp = PQescapeByteaConn(pg_link, (unsigned char *)Z_STRVAL_P(val), Z_STRLEN_P(val), &to_len); + if (tmp == NULL) { + php_error_docref(NULL, E_NOTICE, "Escaping value failed for %s field (%s)", Z_STRVAL_P(type), ZSTR_VAL(field)); + err = 1; + break; + } tmp_zstr = zend_string_init((char *)tmp, to_len - 1, false); /* PQescapeBytea's to_len includes additional '\0' */ PQfreemem(tmp); @@ -5181,6 +5222,12 @@ PHP_PGSQL_API zend_result php_pgsql_convert(PGconn *pg_link, const zend_string * zend_hash_update(Z_ARRVAL_P(result), field, &new_val); } else { char *escaped = PQescapeIdentifier(pg_link, ZSTR_VAL(field), ZSTR_LEN(field)); + if (escaped == NULL) { + /* This cannot fail because of invalid string but only due to failed memory allocation */ + php_error_docref(NULL, E_NOTICE, "Escaping field '%s' failed", ZSTR_VAL(field)); + err = 1; + break; + } add_assoc_zval(result, escaped, &new_val); PQfreemem(escaped); } @@ -5259,7 +5306,7 @@ static bool do_exec(smart_str *querystr, ExecStatusType expect, PGconn *pg_link, } /* }}} */ -static inline void build_tablename(smart_str *querystr, PGconn *pg_link, const zend_string *table) /* {{{ */ +static inline zend_result build_tablename(smart_str *querystr, PGconn *pg_link, const zend_string *table) /* {{{ */ { /* schema.table should be "schema"."table" */ const char *dot = memchr(ZSTR_VAL(table), '.', ZSTR_LEN(table)); @@ -5269,6 +5316,10 @@ static inline void build_tablename(smart_str *querystr, PGconn *pg_link, const z smart_str_appendl(querystr, ZSTR_VAL(table), len); } else { char *escaped = PQescapeIdentifier(pg_link, ZSTR_VAL(table), len); + if (escaped == NULL) { + php_error_docref(NULL, E_NOTICE, "Failed to escape table name '%s'", ZSTR_VAL(table)); + return FAILURE; + } smart_str_appends(querystr, escaped); PQfreemem(escaped); } @@ -5281,11 +5332,17 @@ static inline void build_tablename(smart_str *querystr, PGconn *pg_link, const z smart_str_appendl(querystr, after_dot, len); } else { char *escaped = PQescapeIdentifier(pg_link, after_dot, len); + if (escaped == NULL) { + php_error_docref(NULL, E_NOTICE, "Failed to escape table name '%s'", ZSTR_VAL(table)); + return FAILURE; + } smart_str_appendc(querystr, '.'); smart_str_appends(querystr, escaped); PQfreemem(escaped); } } + + return SUCCESS; } /* }}} */ @@ -5306,7 +5363,9 @@ PHP_PGSQL_API zend_result php_pgsql_insert(PGconn *pg_link, const zend_string *t ZVAL_UNDEF(&converted); if (zend_hash_num_elements(Z_ARRVAL_P(var_array)) == 0) { smart_str_appends(&querystr, "INSERT INTO "); - build_tablename(&querystr, pg_link, table); + if (build_tablename(&querystr, pg_link, table) == FAILURE) { + goto cleanup; + } smart_str_appends(&querystr, " DEFAULT VALUES"); goto no_values; @@ -5322,7 +5381,9 @@ PHP_PGSQL_API zend_result php_pgsql_insert(PGconn *pg_link, const zend_string *t } smart_str_appends(&querystr, "INSERT INTO "); - build_tablename(&querystr, pg_link, table); + if (build_tablename(&querystr, pg_link, table) == FAILURE) { + goto cleanup; + } smart_str_appends(&querystr, " ("); ZEND_HASH_FOREACH_STR_KEY(Z_ARRVAL_P(var_array), fld) { @@ -5332,6 +5393,10 @@ PHP_PGSQL_API zend_result php_pgsql_insert(PGconn *pg_link, const zend_string *t } if (opt & PGSQL_DML_ESCAPE) { tmp = PQescapeIdentifier(pg_link, ZSTR_VAL(fld), ZSTR_LEN(fld) + 1); + if (tmp == NULL) { + php_error_docref(NULL, E_NOTICE, "Failed to escape field '%s'", ZSTR_VAL(fld)); + goto cleanup; + } smart_str_appends(&querystr, tmp); PQfreemem(tmp); } else { @@ -5343,15 +5408,19 @@ PHP_PGSQL_API zend_result php_pgsql_insert(PGconn *pg_link, const zend_string *t smart_str_appends(&querystr, ") VALUES ("); /* make values string */ - ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(var_array), val) { + ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(var_array), fld, val) { /* we can avoid the key_type check here, because we tested it in the other loop */ switch (Z_TYPE_P(val)) { case IS_STRING: if (opt & PGSQL_DML_ESCAPE) { - size_t new_len; - char *tmp; - tmp = (char *)safe_emalloc(Z_STRLEN_P(val), 2, 1); - new_len = PQescapeStringConn(pg_link, tmp, Z_STRVAL_P(val), Z_STRLEN_P(val), NULL); + int error; + char *tmp = safe_emalloc(Z_STRLEN_P(val), 2, 1); + size_t new_len = PQescapeStringConn(pg_link, tmp, Z_STRVAL_P(val), Z_STRLEN_P(val), &error); + if (error) { + php_error_docref(NULL, E_NOTICE, "Failed to escape field '%s' value", ZSTR_VAL(fld)); + efree(tmp); + goto cleanup; + } smart_str_appendc(&querystr, '\''); smart_str_appendl(&querystr, tmp, new_len); smart_str_appendc(&querystr, '\''); @@ -5507,6 +5576,10 @@ static inline int build_assignment_string(PGconn *pg_link, smart_str *querystr, } if (opt & PGSQL_DML_ESCAPE) { char *tmp = PQescapeIdentifier(pg_link, ZSTR_VAL(fld), ZSTR_LEN(fld) + 1); + if (tmp == NULL) { + php_error_docref(NULL, E_NOTICE, "Failed to escape field '%s'", ZSTR_VAL(fld)); + return -1; + } smart_str_appends(querystr, tmp); PQfreemem(tmp); } else { @@ -5522,8 +5595,14 @@ static inline int build_assignment_string(PGconn *pg_link, smart_str *querystr, switch (Z_TYPE_P(val)) { case IS_STRING: if (opt & PGSQL_DML_ESCAPE) { + int error; char *tmp = (char *)safe_emalloc(Z_STRLEN_P(val), 2, 1); - size_t new_len = PQescapeStringConn(pg_link, tmp, Z_STRVAL_P(val), Z_STRLEN_P(val), NULL); + size_t new_len = PQescapeStringConn(pg_link, tmp, Z_STRVAL_P(val), Z_STRLEN_P(val), &error); + if (error) { + php_error_docref(NULL, E_NOTICE, "Failed to escape field '%s' value", ZSTR_VAL(fld)); + efree(tmp); + return -1; + } smart_str_appendc(querystr, '\''); smart_str_appendl(querystr, tmp, new_len); smart_str_appendc(querystr, '\''); @@ -5591,7 +5670,9 @@ PHP_PGSQL_API zend_result php_pgsql_update(PGconn *pg_link, const zend_string *t } smart_str_appends(&querystr, "UPDATE "); - build_tablename(&querystr, pg_link, table); + if (build_tablename(&querystr, pg_link, table) == FAILURE) { + goto cleanup; + } smart_str_appends(&querystr, " SET "); if (build_assignment_string(pg_link, &querystr, Z_ARRVAL_P(var_array), 0, ",", 1, opt)) @@ -5694,7 +5775,9 @@ PHP_PGSQL_API zend_result php_pgsql_delete(PGconn *pg_link, const zend_string *t } smart_str_appends(&querystr, "DELETE FROM "); - build_tablename(&querystr, pg_link, table); + if (build_tablename(&querystr, pg_link, table) == FAILURE) { + goto cleanup; + } smart_str_appends(&querystr, " WHERE "); if (build_assignment_string(pg_link, &querystr, Z_ARRVAL_P(ids_array), 1, " AND ", sizeof(" AND ")-1, opt)) @@ -5834,7 +5917,9 @@ PHP_PGSQL_API zend_result php_pgsql_select(PGconn *pg_link, const zend_string *t } smart_str_appends(&querystr, "SELECT * FROM "); - build_tablename(&querystr, pg_link, table); + if (build_tablename(&querystr, pg_link, table) == FAILURE) { + goto cleanup; + } smart_str_appends(&querystr, " WHERE "); if (build_assignment_string(pg_link, &querystr, Z_ARRVAL_P(ids_array), 1, " AND ", sizeof(" AND ")-1, opt)) diff --git a/ext/pgsql/tests/ghsa-hrwm-9436-5mv3.phpt b/ext/pgsql/tests/ghsa-hrwm-9436-5mv3.phpt new file mode 100644 index 0000000000000..c1c5e05dce623 --- /dev/null +++ b/ext/pgsql/tests/ghsa-hrwm-9436-5mv3.phpt @@ -0,0 +1,64 @@ +--TEST-- +#GHSA-hrwm-9436-5mv3: pgsql extension does not check for errors during escaping +--EXTENSIONS-- +pgsql +--SKIPIF-- + +--FILE-- + 'test'])); // table name str escape in php_pgsql_meta_data +var_dump(pg_insert($db, "$invalid.tbl", ['bar' => 'test'])); // schema name str escape in php_pgsql_meta_data +var_dump(pg_insert($db, 'ghsa_hrmw_9436_5mv3', ['bar' => $invalid])); // converted value str escape in php_pgsql_convert +var_dump(pg_insert($db, $invalid, [])); // ident escape in build_tablename +var_dump(pg_insert($db, 'ghsa_hrmw_9436_5mv3', [$invalid => 'foo'], $flags)); // ident escape for field php_pgsql_insert +var_dump(pg_insert($db, 'ghsa_hrmw_9436_5mv3', ['bar' => $invalid], $flags)); // str escape for field value in php_pgsql_insert +var_dump(pg_update($db, 'ghsa_hrmw_9436_5mv3', ['bar' => 'val'], [$invalid => 'test'], $flags)); // ident escape in build_assignment_string +var_dump(pg_update($db, 'ghsa_hrmw_9436_5mv3', ['bar' => 'val'], ['bar' => $invalid], $flags)); // invalid str escape in build_assignment_string +var_dump(pg_escape_literal($db, $invalid)); // pg_escape_literal escape +var_dump(pg_escape_identifier($db, $invalid)); // pg_escape_identifier escape + +?> +--EXPECTF-- + +Warning: pg_insert(): Escaping table name 'ABC%s';' failed in %s on line %d +bool(false) + +Warning: pg_insert(): Escaping table namespace 'ABC%s';.tbl' failed in %s on line %d +bool(false) + +Notice: pg_insert(): String value escaping failed for PostgreSQL 'text' (bar) in %s on line %d +bool(false) + +Notice: pg_insert(): Failed to escape table name 'ABC%s';' in %s on line %d +bool(false) + +Notice: pg_insert(): Failed to escape field 'ABC%s';' in %s on line %d +bool(false) + +Notice: pg_insert(): Failed to escape field 'bar' value in %s on line %d +bool(false) + +Notice: pg_update(): Failed to escape field 'ABC%s';' in %s on line %d +bool(false) + +Notice: pg_update(): Failed to escape field 'bar' value in %s on line %d +bool(false) + +Warning: pg_escape_literal(): Failed to escape in %s on line %d +bool(false) + +Warning: pg_escape_identifier(): Failed to escape in %s on line %d +bool(false) From dd060656d31fc2ba3fe9acd42bbc19d1c96ff914 Mon Sep 17 00:00:00 2001 From: Ahmed Lekssays Date: Tue, 3 Jun 2025 09:00:55 +0000 Subject: [PATCH 207/682] Fix GHSA-453j-q27h-5p8x Libxml versions prior to 2.13 cannot correctly handle a call to xmlNodeSetName() with a name longer than 2G. It will leave the node object in an invalid state with a NULL name. This later causes a NULL pointer dereference when using the name during message serialization. To solve this, implement a workaround that resets the name to the sentinel name if this situation arises. Versions of libxml of 2.13 and higher are not affected. This can be exploited if a SoapVar is created with a fully qualified name that is longer than 2G. This would be possible if some application code uses a namespace prefix from an untrusted source like from a remote SOAP service. Co-authored-by: Niels Dossche <7771979+nielsdos@users.noreply.github.com> --- ext/soap/soap.c | 6 ++-- ext/soap/tests/soap_qname_crash.phpt | 48 ++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 2 deletions(-) create mode 100644 ext/soap/tests/soap_qname_crash.phpt diff --git a/ext/soap/soap.c b/ext/soap/soap.c index 3f8a50ba6ed2c..fc51e32658f3a 100644 --- a/ext/soap/soap.c +++ b/ext/soap/soap.c @@ -3980,8 +3980,10 @@ static xmlNodePtr serialize_zval(zval *val, sdlParamPtr param, char *paramName, } xmlParam = master_to_xml(enc, val, style, parent); zval_ptr_dtor(&defval); - if (!strcmp((char*)xmlParam->name, "BOGUS")) { - xmlNodeSetName(xmlParam, BAD_CAST(paramName)); + if (xmlParam != NULL) { + if (xmlParam->name == NULL || strcmp((char*)xmlParam->name, "BOGUS") == 0) { + xmlNodeSetName(xmlParam, BAD_CAST(paramName)); + } } return xmlParam; } diff --git a/ext/soap/tests/soap_qname_crash.phpt b/ext/soap/tests/soap_qname_crash.phpt new file mode 100644 index 0000000000000..bcf01d574fab4 --- /dev/null +++ b/ext/soap/tests/soap_qname_crash.phpt @@ -0,0 +1,48 @@ +--TEST-- +Test SoapClient with excessively large QName prefix in SoapVar +--EXTENSIONS-- +soap +--SKIPIF-- + +--INI-- +memory_limit=6144M +--FILE-- + 'http://127.0.0.1/', + 'uri' => 'urn:dummy', + 'trace' => 1, + 'exceptions' => true, +]; +$client = new TestSoapClient(null, $options); +$client->__soapCall("DummyFunction", [$var]); +?> +--EXPECT-- +Attempting to create SoapVar with very large QName +Attempting encoding + +value From fc49d334496c865dd7e60d8b6b360313823162ef Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Thu, 26 Jun 2025 11:29:28 +0200 Subject: [PATCH 208/682] Update NEWS with entries for security fixes --- NEWS | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/NEWS b/NEWS index 267681cfa265e..44c964099bc8e 100644 --- a/NEWS +++ b/NEWS @@ -91,6 +91,8 @@ PHP NEWS - PGSQL: . Fix warning not being emitted when failure to cancel a query with pg_cancel_query(). (Girgias) + . Fixed GHSA-hrwm-9436-5mv3 (pgsql extension does not check for errors during + escaping). (CVE-2025-1735) (Jakub Zelenka) - Random: . Fix reference type confusion and leak in user random engine. @@ -102,6 +104,12 @@ PHP NEWS - Soap: . Fix memory leaks in php_http.c when call_user_function() fails. (nielsdos) + . Fixed GHSA-453j-q27h-5p8x (NULL Pointer Dereference in PHP SOAP ExtensionAdd commentMore actions + via Large XML Namespace Prefix). (CVE-2025-6491) (Lekssays, nielsdos) + +- Standard: + . Fixed GHSA-3cr5-j632-f35r (Null byte termination in hostnames). + (CVE-2025-1220) (Jakub Zelenka) - Tidy: . Fix memory leak in tidy output handler on error. (nielsdos) From 27e67cc3712ce0bc94fd4b2bfbb7155d89ec52d6 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Thu, 10 Apr 2025 15:15:36 +0200 Subject: [PATCH 209/682] Fix GHSA-3cr5-j632-f35r: Null byte in hostnames This fixes stream_socket_client() and fsockopen(). Specifically it adds a check to parse_ip_address_ex and it also makes sure that the \0 is not ignored in fsockopen() hostname formatting. --- ext/standard/fsock.c | 27 +++++++++++++++++-- .../tests/network/ghsa-3cr5-j632-f35r.phpt | 21 +++++++++++++++ .../tests/streams/ghsa-3cr5-j632-f35r.phpt | 26 ++++++++++++++++++ main/streams/xp_socket.c | 9 ++++--- 4 files changed, 78 insertions(+), 5 deletions(-) create mode 100644 ext/standard/tests/network/ghsa-3cr5-j632-f35r.phpt create mode 100644 ext/standard/tests/streams/ghsa-3cr5-j632-f35r.phpt diff --git a/ext/standard/fsock.c b/ext/standard/fsock.c index cb7a471e935a6..2b9e00a57554c 100644 --- a/ext/standard/fsock.c +++ b/ext/standard/fsock.c @@ -23,6 +23,28 @@ #include "php_network.h" #include "file.h" +static size_t php_fsockopen_format_host_port(char **message, const char *prefix, size_t prefix_len, + const char *host, size_t host_len, zend_long port) +{ + char portbuf[32]; + int portlen = snprintf(portbuf, sizeof(portbuf), ":" ZEND_LONG_FMT, port); + size_t total_len = prefix_len + host_len + portlen; + + char *result = emalloc(total_len + 1); + + if (prefix_len > 0) { + memcpy(result, prefix, prefix_len); + } + memcpy(result + prefix_len, host, host_len); + memcpy(result + prefix_len + host_len, portbuf, portlen); + + result[total_len] = '\0'; + + *message = result; + + return total_len; +} + /* {{{ php_fsockopen() */ static void php_fsockopen_stream(INTERNAL_FUNCTION_PARAMETERS, int persistent) @@ -62,11 +84,12 @@ static void php_fsockopen_stream(INTERNAL_FUNCTION_PARAMETERS, int persistent) } if (persistent) { - spprintf(&hashkey, 0, "pfsockopen__%s:" ZEND_LONG_FMT, host, port); + php_fsockopen_format_host_port(&hashkey, "pfsockopen__", strlen("pfsockopen__"), host, + host_len, port); } if (port > 0) { - hostname_len = spprintf(&hostname, 0, "%s:" ZEND_LONG_FMT, host, port); + hostname_len = php_fsockopen_format_host_port(&hostname, "", 0, host, host_len, port); } else { hostname_len = host_len; hostname = host; diff --git a/ext/standard/tests/network/ghsa-3cr5-j632-f35r.phpt b/ext/standard/tests/network/ghsa-3cr5-j632-f35r.phpt new file mode 100644 index 0000000000000..7556c3be94ccd --- /dev/null +++ b/ext/standard/tests/network/ghsa-3cr5-j632-f35r.phpt @@ -0,0 +1,21 @@ +--TEST-- +GHSA-3cr5-j632-f35r: Null byte termination in fsockopen() +--FILE-- + +--EXPECTF-- + +Warning: fsockopen(): Unable to connect to localhost:%d (The hostname must not contain null bytes) in %s +bool(false) diff --git a/ext/standard/tests/streams/ghsa-3cr5-j632-f35r.phpt b/ext/standard/tests/streams/ghsa-3cr5-j632-f35r.phpt new file mode 100644 index 0000000000000..52f9263c99aaa --- /dev/null +++ b/ext/standard/tests/streams/ghsa-3cr5-j632-f35r.phpt @@ -0,0 +1,26 @@ +--TEST-- +GHSA-3cr5-j632-f35r: Null byte termination in stream_socket_client() +--FILE-- + +--EXPECTF-- + +Warning: stream_socket_client(): Unable to connect to tcp://localhost\0.example.com:%d (The hostname must not contain null bytes) in %s +bool(false) diff --git a/main/streams/xp_socket.c b/main/streams/xp_socket.c index 9987f871a7afa..ef4fa6f86e384 100644 --- a/main/streams/xp_socket.c +++ b/main/streams/xp_socket.c @@ -594,12 +594,15 @@ static inline char *parse_ip_address_ex(const char *str, size_t str_len, int *po char *colon; char *host = NULL; -#ifdef HAVE_IPV6 - char *p; + if (memchr(str, '\0', str_len)) { + *err = ZSTR_INIT_LITERAL("The hostname must not contain null bytes", 0); + return NULL; + } +#ifdef HAVE_IPV6 if (*(str) == '[' && str_len > 1) { /* IPV6 notation to specify raw address with port (i.e. [fe80::1]:80) */ - p = memchr(str + 1, ']', str_len - 2); + char *p = memchr(str + 1, ']', str_len - 2); if (!p || *(p + 1) != ':') { if (get_err) { *err = strpprintf(0, "Failed to parse IPv6 address \"%s\"", str); From a2cdff5583ad2bfe9a27fba71e2b1fc423296dc0 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Tue, 4 Mar 2025 17:23:01 +0100 Subject: [PATCH 210/682] Fix GHSA-hrwm-9436-5mv3: pgsql escaping no error checks This adds error checks for escape function is pgsql and pdo_pgsql extensions. It prevents possibility of storing not properly escaped data which could potentially lead to some security issues. --- ext/pdo_pgsql/pgsql_driver.c | 10 +- ext/pdo_pgsql/tests/ghsa-hrwm-9436-5mv3.phpt | 24 ++++ ext/pgsql/pgsql.c | 126 ++++++++++++++++--- ext/pgsql/tests/ghsa-hrwm-9436-5mv3.phpt | 64 ++++++++++ 4 files changed, 203 insertions(+), 21 deletions(-) create mode 100644 ext/pdo_pgsql/tests/ghsa-hrwm-9436-5mv3.phpt create mode 100644 ext/pgsql/tests/ghsa-hrwm-9436-5mv3.phpt diff --git a/ext/pdo_pgsql/pgsql_driver.c b/ext/pdo_pgsql/pgsql_driver.c index ec4d5ec65866b..ed3aeaf2f772e 100644 --- a/ext/pdo_pgsql/pgsql_driver.c +++ b/ext/pdo_pgsql/pgsql_driver.c @@ -353,11 +353,15 @@ static zend_string* pgsql_handle_quoter(pdo_dbh_t *dbh, const zend_string *unquo zend_string *quoted_str; pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; size_t tmp_len; + int err; switch (paramtype) { case PDO_PARAM_LOB: /* escapedlen returned by PQescapeBytea() accounts for trailing 0 */ escaped = PQescapeByteaConn(H->server, (unsigned char *)ZSTR_VAL(unquoted), ZSTR_LEN(unquoted), &tmp_len); + if (escaped == NULL) { + return NULL; + } quotedlen = tmp_len + 1; quoted = emalloc(quotedlen + 1); memcpy(quoted+1, escaped, quotedlen-2); @@ -369,7 +373,11 @@ static zend_string* pgsql_handle_quoter(pdo_dbh_t *dbh, const zend_string *unquo default: quoted = safe_emalloc(2, ZSTR_LEN(unquoted), 3); quoted[0] = '\''; - quotedlen = PQescapeStringConn(H->server, quoted + 1, ZSTR_VAL(unquoted), ZSTR_LEN(unquoted), NULL); + quotedlen = PQescapeStringConn(H->server, quoted + 1, ZSTR_VAL(unquoted), ZSTR_LEN(unquoted), &err); + if (err) { + efree(quoted); + return NULL; + } quoted[quotedlen + 1] = '\''; quoted[quotedlen + 2] = '\0'; quotedlen += 2; diff --git a/ext/pdo_pgsql/tests/ghsa-hrwm-9436-5mv3.phpt b/ext/pdo_pgsql/tests/ghsa-hrwm-9436-5mv3.phpt new file mode 100644 index 0000000000000..8566a26753b40 --- /dev/null +++ b/ext/pdo_pgsql/tests/ghsa-hrwm-9436-5mv3.phpt @@ -0,0 +1,24 @@ +--TEST-- +#GHSA-hrwm-9436-5mv3: pdo_pgsql extension does not check for errors during escaping +--EXTENSIONS-- +pdo +pdo_pgsql +--SKIPIF-- + +--FILE-- +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + +$invalid = "ABC\xff\x30';"; +var_dump($db->quote($invalid)); + +?> +--EXPECT-- +bool(false) diff --git a/ext/pgsql/pgsql.c b/ext/pgsql/pgsql.c index 6e04848bdea0b..cfa915206b8ec 100644 --- a/ext/pgsql/pgsql.c +++ b/ext/pgsql/pgsql.c @@ -3203,8 +3203,14 @@ PHP_FUNCTION(pg_escape_string) to = zend_string_safe_alloc(ZSTR_LEN(from), 2, 0, 0); if (link) { + int err; pgsql = link->conn; - ZSTR_LEN(to) = PQescapeStringConn(pgsql, ZSTR_VAL(to), ZSTR_VAL(from), ZSTR_LEN(from), NULL); + ZSTR_LEN(to) = PQescapeStringConn(pgsql, ZSTR_VAL(to), ZSTR_VAL(from), ZSTR_LEN(from), &err); + if (err) { + zend_argument_value_error(ZEND_NUM_ARGS(), "Escaping string failed"); + zend_string_efree(to); + RETURN_THROWS(); + } } else { ZSTR_LEN(to) = PQescapeString(ZSTR_VAL(to), ZSTR_VAL(from), ZSTR_LEN(from)); @@ -3247,6 +3253,10 @@ PHP_FUNCTION(pg_escape_bytea) } else { to = (char *)PQescapeBytea((unsigned char *)ZSTR_VAL(from), ZSTR_LEN(from), &to_len); } + if (to == NULL) { + zend_argument_value_error(ZEND_NUM_ARGS(), "Escape failure"); + RETURN_THROWS(); + } RETVAL_STRINGL(to, to_len-1); /* to_len includes additional '\0' */ PQfreemem(to); @@ -4163,7 +4173,7 @@ PHP_PGSQL_API zend_result php_pgsql_meta_data(PGconn *pg_link, const zend_string char *escaped; smart_str querystr = {0}; size_t new_len; - int i, num_rows; + int i, num_rows, err; zval elem; ZEND_ASSERT(ZSTR_LEN(table_name) != 0); @@ -4202,7 +4212,14 @@ PHP_PGSQL_API zend_result php_pgsql_meta_data(PGconn *pg_link, const zend_string "WHERE a.attnum > 0 AND c.relname = '"); } escaped = (char *)safe_emalloc(strlen(tmp_name2), 2, 1); - new_len = PQescapeStringConn(pg_link, escaped, tmp_name2, strlen(tmp_name2), NULL); + new_len = PQescapeStringConn(pg_link, escaped, tmp_name2, strlen(tmp_name2), &err); + if (err) { + php_error_docref(NULL, E_WARNING, "Escaping table name '%s' failed", ZSTR_VAL(table_name)); + efree(src); + efree(escaped); + smart_str_free(&querystr); + return FAILURE; + } if (new_len) { smart_str_appendl(&querystr, escaped, new_len); } @@ -4210,7 +4227,14 @@ PHP_PGSQL_API zend_result php_pgsql_meta_data(PGconn *pg_link, const zend_string smart_str_appends(&querystr, "' AND n.nspname = '"); escaped = (char *)safe_emalloc(strlen(tmp_name), 2, 1); - new_len = PQescapeStringConn(pg_link, escaped, tmp_name, strlen(tmp_name), NULL); + new_len = PQescapeStringConn(pg_link, escaped, tmp_name, strlen(tmp_name), &err); + if (err) { + php_error_docref(NULL, E_WARNING, "Escaping table namespace '%s' failed", ZSTR_VAL(table_name)); + efree(src); + efree(escaped); + smart_str_free(&querystr); + return FAILURE; + } if (new_len) { smart_str_appendl(&querystr, escaped, new_len); } @@ -4471,7 +4495,7 @@ PHP_PGSQL_API zend_result php_pgsql_convert(PGconn *pg_link, const zend_string * { zend_string *field = NULL; zval meta, *def, *type, *not_null, *has_default, *is_enum, *val, new_val; - int err = 0, skip_field; + int err = 0, escape_err = 0, skip_field; php_pgsql_data_type data_type; ZEND_ASSERT(pg_link != NULL); @@ -4724,8 +4748,13 @@ PHP_PGSQL_API zend_result php_pgsql_convert(PGconn *pg_link, const zend_string * /* PostgreSQL ignores \0 */ str = zend_string_alloc(Z_STRLEN_P(val) * 2, 0); /* better to use PGSQLescapeLiteral since PGescapeStringConn does not handle special \ */ - ZSTR_LEN(str) = PQescapeStringConn(pg_link, ZSTR_VAL(str), Z_STRVAL_P(val), Z_STRLEN_P(val), NULL); - ZVAL_STR(&new_val, php_pgsql_add_quotes(str)); + ZSTR_LEN(str) = PQescapeStringConn(pg_link, ZSTR_VAL(str), + Z_STRVAL_P(val), Z_STRLEN_P(val), &escape_err); + if (escape_err) { + err = 1; + } else { + ZVAL_STR(&new_val, php_pgsql_add_quotes(str)); + } zend_string_release_ex(str, false); } break; @@ -4748,7 +4777,15 @@ PHP_PGSQL_API zend_result php_pgsql_convert(PGconn *pg_link, const zend_string * } PGSQL_CONV_CHECK_IGNORE(); if (err) { - php_error_docref(NULL, E_NOTICE, "Expects NULL, string, long or double value for PostgreSQL '%s' (%s)", Z_STRVAL_P(type), ZSTR_VAL(field)); + if (escape_err) { + php_error_docref(NULL, E_NOTICE, + "String value escaping failed for PostgreSQL '%s' (%s)", + Z_STRVAL_P(type), ZSTR_VAL(field)); + } else { + php_error_docref(NULL, E_NOTICE, + "Expects NULL, string, long or double value for PostgreSQL '%s' (%s)", + Z_STRVAL_P(type), ZSTR_VAL(field)); + } } break; @@ -5019,6 +5056,11 @@ PHP_PGSQL_API zend_result php_pgsql_convert(PGconn *pg_link, const zend_string * zend_string *tmp_zstr; tmp = PQescapeByteaConn(pg_link, (unsigned char *)Z_STRVAL_P(val), Z_STRLEN_P(val), &to_len); + if (tmp == NULL) { + php_error_docref(NULL, E_NOTICE, "Escaping value failed for %s field (%s)", Z_STRVAL_P(type), ZSTR_VAL(field)); + err = 1; + break; + } tmp_zstr = zend_string_init((char *)tmp, to_len - 1, false); /* PQescapeBytea's to_len includes additional '\0' */ PQfreemem(tmp); @@ -5097,6 +5139,12 @@ PHP_PGSQL_API zend_result php_pgsql_convert(PGconn *pg_link, const zend_string * zend_hash_update(Z_ARRVAL_P(result), field, &new_val); } else { char *escaped = PQescapeIdentifier(pg_link, ZSTR_VAL(field), ZSTR_LEN(field)); + if (escaped == NULL) { + /* This cannot fail because of invalid string but only due to failed memory allocation */ + php_error_docref(NULL, E_NOTICE, "Escaping field '%s' failed", ZSTR_VAL(field)); + err = 1; + break; + } add_assoc_zval(result, escaped, &new_val); PQfreemem(escaped); } @@ -5175,7 +5223,7 @@ static bool do_exec(smart_str *querystr, ExecStatusType expect, PGconn *pg_link, } /* }}} */ -static inline void build_tablename(smart_str *querystr, PGconn *pg_link, const zend_string *table) /* {{{ */ +static inline zend_result build_tablename(smart_str *querystr, PGconn *pg_link, const zend_string *table) /* {{{ */ { /* schema.table should be "schema"."table" */ const char *dot = memchr(ZSTR_VAL(table), '.', ZSTR_LEN(table)); @@ -5185,6 +5233,10 @@ static inline void build_tablename(smart_str *querystr, PGconn *pg_link, const z smart_str_appendl(querystr, ZSTR_VAL(table), len); } else { char *escaped = PQescapeIdentifier(pg_link, ZSTR_VAL(table), len); + if (escaped == NULL) { + php_error_docref(NULL, E_NOTICE, "Failed to escape table name '%s'", ZSTR_VAL(table)); + return FAILURE; + } smart_str_appends(querystr, escaped); PQfreemem(escaped); } @@ -5197,11 +5249,17 @@ static inline void build_tablename(smart_str *querystr, PGconn *pg_link, const z smart_str_appendl(querystr, after_dot, len); } else { char *escaped = PQescapeIdentifier(pg_link, after_dot, len); + if (escaped == NULL) { + php_error_docref(NULL, E_NOTICE, "Failed to escape table name '%s'", ZSTR_VAL(table)); + return FAILURE; + } smart_str_appendc(querystr, '.'); smart_str_appends(querystr, escaped); PQfreemem(escaped); } } + + return SUCCESS; } /* }}} */ @@ -5222,7 +5280,9 @@ PHP_PGSQL_API zend_result php_pgsql_insert(PGconn *pg_link, const zend_string *t ZVAL_UNDEF(&converted); if (zend_hash_num_elements(Z_ARRVAL_P(var_array)) == 0) { smart_str_appends(&querystr, "INSERT INTO "); - build_tablename(&querystr, pg_link, table); + if (build_tablename(&querystr, pg_link, table) == FAILURE) { + goto cleanup; + } smart_str_appends(&querystr, " DEFAULT VALUES"); goto no_values; @@ -5238,7 +5298,9 @@ PHP_PGSQL_API zend_result php_pgsql_insert(PGconn *pg_link, const zend_string *t } smart_str_appends(&querystr, "INSERT INTO "); - build_tablename(&querystr, pg_link, table); + if (build_tablename(&querystr, pg_link, table) == FAILURE) { + goto cleanup; + } smart_str_appends(&querystr, " ("); ZEND_HASH_FOREACH_STR_KEY(Z_ARRVAL_P(var_array), fld) { @@ -5248,6 +5310,10 @@ PHP_PGSQL_API zend_result php_pgsql_insert(PGconn *pg_link, const zend_string *t } if (opt & PGSQL_DML_ESCAPE) { tmp = PQescapeIdentifier(pg_link, ZSTR_VAL(fld), ZSTR_LEN(fld) + 1); + if (tmp == NULL) { + php_error_docref(NULL, E_NOTICE, "Failed to escape field '%s'", ZSTR_VAL(fld)); + goto cleanup; + } smart_str_appends(&querystr, tmp); PQfreemem(tmp); } else { @@ -5259,15 +5325,19 @@ PHP_PGSQL_API zend_result php_pgsql_insert(PGconn *pg_link, const zend_string *t smart_str_appends(&querystr, ") VALUES ("); /* make values string */ - ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(var_array), val) { + ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(var_array), fld, val) { /* we can avoid the key_type check here, because we tested it in the other loop */ switch (Z_TYPE_P(val)) { case IS_STRING: if (opt & PGSQL_DML_ESCAPE) { - size_t new_len; - char *tmp; - tmp = (char *)safe_emalloc(Z_STRLEN_P(val), 2, 1); - new_len = PQescapeStringConn(pg_link, tmp, Z_STRVAL_P(val), Z_STRLEN_P(val), NULL); + int error; + char *tmp = safe_emalloc(Z_STRLEN_P(val), 2, 1); + size_t new_len = PQescapeStringConn(pg_link, tmp, Z_STRVAL_P(val), Z_STRLEN_P(val), &error); + if (error) { + php_error_docref(NULL, E_NOTICE, "Failed to escape field '%s' value", ZSTR_VAL(fld)); + efree(tmp); + goto cleanup; + } smart_str_appendc(&querystr, '\''); smart_str_appendl(&querystr, tmp, new_len); smart_str_appendc(&querystr, '\''); @@ -5423,6 +5493,10 @@ static inline int build_assignment_string(PGconn *pg_link, smart_str *querystr, } if (opt & PGSQL_DML_ESCAPE) { char *tmp = PQescapeIdentifier(pg_link, ZSTR_VAL(fld), ZSTR_LEN(fld) + 1); + if (tmp == NULL) { + php_error_docref(NULL, E_NOTICE, "Failed to escape field '%s'", ZSTR_VAL(fld)); + return -1; + } smart_str_appends(querystr, tmp); PQfreemem(tmp); } else { @@ -5438,8 +5512,14 @@ static inline int build_assignment_string(PGconn *pg_link, smart_str *querystr, switch (Z_TYPE_P(val)) { case IS_STRING: if (opt & PGSQL_DML_ESCAPE) { + int error; char *tmp = (char *)safe_emalloc(Z_STRLEN_P(val), 2, 1); - size_t new_len = PQescapeStringConn(pg_link, tmp, Z_STRVAL_P(val), Z_STRLEN_P(val), NULL); + size_t new_len = PQescapeStringConn(pg_link, tmp, Z_STRVAL_P(val), Z_STRLEN_P(val), &error); + if (error) { + php_error_docref(NULL, E_NOTICE, "Failed to escape field '%s' value", ZSTR_VAL(fld)); + efree(tmp); + return -1; + } smart_str_appendc(querystr, '\''); smart_str_appendl(querystr, tmp, new_len); smart_str_appendc(querystr, '\''); @@ -5507,7 +5587,9 @@ PHP_PGSQL_API zend_result php_pgsql_update(PGconn *pg_link, const zend_string *t } smart_str_appends(&querystr, "UPDATE "); - build_tablename(&querystr, pg_link, table); + if (build_tablename(&querystr, pg_link, table) == FAILURE) { + goto cleanup; + } smart_str_appends(&querystr, " SET "); if (build_assignment_string(pg_link, &querystr, Z_ARRVAL_P(var_array), 0, ",", 1, opt)) @@ -5610,7 +5692,9 @@ PHP_PGSQL_API zend_result php_pgsql_delete(PGconn *pg_link, const zend_string *t } smart_str_appends(&querystr, "DELETE FROM "); - build_tablename(&querystr, pg_link, table); + if (build_tablename(&querystr, pg_link, table) == FAILURE) { + goto cleanup; + } smart_str_appends(&querystr, " WHERE "); if (build_assignment_string(pg_link, &querystr, Z_ARRVAL_P(ids_array), 1, " AND ", sizeof(" AND ")-1, opt)) @@ -5750,7 +5834,9 @@ PHP_PGSQL_API zend_result php_pgsql_select(PGconn *pg_link, const zend_string *t } smart_str_appends(&querystr, "SELECT * FROM "); - build_tablename(&querystr, pg_link, table); + if (build_tablename(&querystr, pg_link, table) == FAILURE) { + goto cleanup; + } smart_str_appends(&querystr, " WHERE "); if (build_assignment_string(pg_link, &querystr, Z_ARRVAL_P(ids_array), 1, " AND ", sizeof(" AND ")-1, opt)) diff --git a/ext/pgsql/tests/ghsa-hrwm-9436-5mv3.phpt b/ext/pgsql/tests/ghsa-hrwm-9436-5mv3.phpt new file mode 100644 index 0000000000000..c1c5e05dce623 --- /dev/null +++ b/ext/pgsql/tests/ghsa-hrwm-9436-5mv3.phpt @@ -0,0 +1,64 @@ +--TEST-- +#GHSA-hrwm-9436-5mv3: pgsql extension does not check for errors during escaping +--EXTENSIONS-- +pgsql +--SKIPIF-- + +--FILE-- + 'test'])); // table name str escape in php_pgsql_meta_data +var_dump(pg_insert($db, "$invalid.tbl", ['bar' => 'test'])); // schema name str escape in php_pgsql_meta_data +var_dump(pg_insert($db, 'ghsa_hrmw_9436_5mv3', ['bar' => $invalid])); // converted value str escape in php_pgsql_convert +var_dump(pg_insert($db, $invalid, [])); // ident escape in build_tablename +var_dump(pg_insert($db, 'ghsa_hrmw_9436_5mv3', [$invalid => 'foo'], $flags)); // ident escape for field php_pgsql_insert +var_dump(pg_insert($db, 'ghsa_hrmw_9436_5mv3', ['bar' => $invalid], $flags)); // str escape for field value in php_pgsql_insert +var_dump(pg_update($db, 'ghsa_hrmw_9436_5mv3', ['bar' => 'val'], [$invalid => 'test'], $flags)); // ident escape in build_assignment_string +var_dump(pg_update($db, 'ghsa_hrmw_9436_5mv3', ['bar' => 'val'], ['bar' => $invalid], $flags)); // invalid str escape in build_assignment_string +var_dump(pg_escape_literal($db, $invalid)); // pg_escape_literal escape +var_dump(pg_escape_identifier($db, $invalid)); // pg_escape_identifier escape + +?> +--EXPECTF-- + +Warning: pg_insert(): Escaping table name 'ABC%s';' failed in %s on line %d +bool(false) + +Warning: pg_insert(): Escaping table namespace 'ABC%s';.tbl' failed in %s on line %d +bool(false) + +Notice: pg_insert(): String value escaping failed for PostgreSQL 'text' (bar) in %s on line %d +bool(false) + +Notice: pg_insert(): Failed to escape table name 'ABC%s';' in %s on line %d +bool(false) + +Notice: pg_insert(): Failed to escape field 'ABC%s';' in %s on line %d +bool(false) + +Notice: pg_insert(): Failed to escape field 'bar' value in %s on line %d +bool(false) + +Notice: pg_update(): Failed to escape field 'ABC%s';' in %s on line %d +bool(false) + +Notice: pg_update(): Failed to escape field 'bar' value in %s on line %d +bool(false) + +Warning: pg_escape_literal(): Failed to escape in %s on line %d +bool(false) + +Warning: pg_escape_identifier(): Failed to escape in %s on line %d +bool(false) From 0298837252fda06e0f86be3dfe91f166f45e85d4 Mon Sep 17 00:00:00 2001 From: Ahmed Lekssays Date: Tue, 3 Jun 2025 09:00:55 +0000 Subject: [PATCH 211/682] Fix GHSA-453j-q27h-5p8x Libxml versions prior to 2.13 cannot correctly handle a call to xmlNodeSetName() with a name longer than 2G. It will leave the node object in an invalid state with a NULL name. This later causes a NULL pointer dereference when using the name during message serialization. To solve this, implement a workaround that resets the name to the sentinel name if this situation arises. Versions of libxml of 2.13 and higher are not affected. This can be exploited if a SoapVar is created with a fully qualified name that is longer than 2G. This would be possible if some application code uses a namespace prefix from an untrusted source like from a remote SOAP service. Co-authored-by: Niels Dossche <7771979+nielsdos@users.noreply.github.com> --- ext/soap/soap.c | 6 ++-- ext/soap/tests/soap_qname_crash.phpt | 48 ++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 2 deletions(-) create mode 100644 ext/soap/tests/soap_qname_crash.phpt diff --git a/ext/soap/soap.c b/ext/soap/soap.c index 0996927cee092..1350c0c0a3448 100644 --- a/ext/soap/soap.c +++ b/ext/soap/soap.c @@ -3980,8 +3980,10 @@ static xmlNodePtr serialize_zval(zval *val, sdlParamPtr param, char *paramName, } xmlParam = master_to_xml(enc, val, style, parent); zval_ptr_dtor(&defval); - if (!strcmp((char*)xmlParam->name, "BOGUS")) { - xmlNodeSetName(xmlParam, BAD_CAST(paramName)); + if (xmlParam != NULL) { + if (xmlParam->name == NULL || strcmp((char*)xmlParam->name, "BOGUS") == 0) { + xmlNodeSetName(xmlParam, BAD_CAST(paramName)); + } } return xmlParam; } diff --git a/ext/soap/tests/soap_qname_crash.phpt b/ext/soap/tests/soap_qname_crash.phpt new file mode 100644 index 0000000000000..bcf01d574fab4 --- /dev/null +++ b/ext/soap/tests/soap_qname_crash.phpt @@ -0,0 +1,48 @@ +--TEST-- +Test SoapClient with excessively large QName prefix in SoapVar +--EXTENSIONS-- +soap +--SKIPIF-- + +--INI-- +memory_limit=6144M +--FILE-- + 'http://127.0.0.1/', + 'uri' => 'urn:dummy', + 'trace' => 1, + 'exceptions' => true, +]; +$client = new TestSoapClient(null, $options); +$client->__soapCall("DummyFunction", [$var]); +?> +--EXPECT-- +Attempting to create SoapVar with very large QName +Attempting encoding + +value From 165e5169a945a0d7de43835436bce6361a737ffe Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Thu, 26 Jun 2025 11:27:48 +0200 Subject: [PATCH 212/682] Update NEWS with entries for security fixes --- NEWS | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 9fdb96e790d39..dbb434af8e936 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,18 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -?? ??? ????, PHP 8.2.29 +03 Jul 2025, PHP 8.2.29 + +- PGSQL: + . Fixed GHSA-hrwm-9436-5mv3 (pgsql extension does not check for errors during + escaping). (CVE-2025-1735) (Jakub Zelenka) + +- SOAP: + . Fixed GHSA-453j-q27h-5p8x (NULL Pointer Dereference in PHP SOAP Extension + via Large XML Namespace Prefix). (CVE-2025-6491) (Lekssays, nielsdos) + +- Standard: + . Fixed GHSA-3cr5-j632-f35r (Null byte termination in hostnames). + (CVE-2025-1220) (Jakub Zelenka) 13 Mar 2025, PHP 8.2.28 From 3d8cc222d590a989646954e46343ff7228c70ecf Mon Sep 17 00:00:00 2001 From: Sergey Panteleev Date: Tue, 1 Jul 2025 19:49:50 +0300 Subject: [PATCH 213/682] PHP-8.2 is now for PHP 8.2.30-dev --- NEWS | 3 +++ Zend/zend.h | 2 +- configure.ac | 2 +- main/php_version.h | 6 +++--- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/NEWS b/NEWS index dbb434af8e936..ed786b668584d 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,8 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +?? ??? ????, PHP 8.2.30 + + 03 Jul 2025, PHP 8.2.29 - PGSQL: diff --git a/Zend/zend.h b/Zend/zend.h index 71e5908f334f0..e3a416096f2af 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -20,7 +20,7 @@ #ifndef ZEND_H #define ZEND_H -#define ZEND_VERSION "4.2.29-dev" +#define ZEND_VERSION "4.2.30-dev" #define ZEND_ENGINE_3 diff --git a/configure.ac b/configure.ac index c70ce436721a8..4c43bd7d330f7 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.29-dev],[https://github.com/php/php-src/issues],[php],[https://www.php.net]) +AC_INIT([PHP],[8.2.30-dev],[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/main/php_version.h b/main/php_version.h index cd9671425f86f..6251ec54ebe62 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 29 +#define PHP_RELEASE_VERSION 30 #define PHP_EXTRA_VERSION "-dev" -#define PHP_VERSION "8.2.29-dev" -#define PHP_VERSION_ID 80229 +#define PHP_VERSION "8.2.30-dev" +#define PHP_VERSION_ID 80230 From 91749844e629b4813d8cc24e4462be678fb03e91 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Thu, 26 Jun 2025 23:30:16 +0200 Subject: [PATCH 214/682] Fix OSS-Fuzz #427814456 The first warning may trigger an error handler, destroying the operand and its string. So we need to protect the string in that case. Care was taken to avoid unnecessary refcounts and to avoid touching the hot code path. Closes GH-18951. --- NEWS | 1 + Zend/tests/numeric_strings/oss_fuzz_427814456.phpt | 11 +++++++++++ Zend/zend_operators.c | 7 ++++++- 3 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 Zend/tests/numeric_strings/oss_fuzz_427814456.phpt diff --git a/NEWS b/NEWS index 44c964099bc8e..259270fdfcf82 100644 --- a/NEWS +++ b/NEWS @@ -8,6 +8,7 @@ PHP NEWS - Core: . Fixed bug GH-18833 (Use after free with weakmaps dependent on destruction order). (Daniil Gentili) + . Fix OSS-Fuzz #427814456. (nielsdos) - Curl: . Fix memory leaks when returning refcounted value from curl callback. diff --git a/Zend/tests/numeric_strings/oss_fuzz_427814456.phpt b/Zend/tests/numeric_strings/oss_fuzz_427814456.phpt new file mode 100644 index 0000000000000..f91563385e9f5 --- /dev/null +++ b/Zend/tests/numeric_strings/oss_fuzz_427814456.phpt @@ -0,0 +1,11 @@ +--TEST-- +OSS-Fuzz #427814456 +--FILE-- + +--EXPECT-- +Done diff --git a/Zend/zend_operators.c b/Zend/zend_operators.c index 890c19c0ab223..38c87dfe98dbd 100644 --- a/Zend/zend_operators.c +++ b/Zend/zend_operators.c @@ -401,6 +401,7 @@ static zend_never_inline zend_long ZEND_FASTCALL zendi_try_get_long(const zval * zend_long lval; double dval; bool trailing_data = false; + zend_string *op_str = NULL; /* protect against error handlers */ /* For BC reasons we allow errors so that we can warn on leading numeric string */ type = is_numeric_string_ex(Z_STRVAL_P(op), Z_STRLEN_P(op), &lval, &dval, @@ -410,6 +411,9 @@ static zend_never_inline zend_long ZEND_FASTCALL zendi_try_get_long(const zval * return 0; } if (UNEXPECTED(trailing_data)) { + if (type != IS_LONG) { + op_str = zend_string_copy(Z_STR_P(op)); + } zend_error(E_WARNING, "A non-numeric value encountered"); if (UNEXPECTED(EG(exception))) { *failed = 1; @@ -425,11 +429,12 @@ static zend_never_inline zend_long ZEND_FASTCALL zendi_try_get_long(const zval * */ lval = zend_dval_to_lval_cap(dval); if (!zend_is_long_compatible(dval, lval)) { - zend_incompatible_string_to_long_error(Z_STR_P(op)); + zend_incompatible_string_to_long_error(op_str ? op_str : Z_STR_P(op)); if (UNEXPECTED(EG(exception))) { *failed = 1; } } + zend_tmp_string_release(op_str); return lval; } } From 1d5089e5740b6831485b17bddf482f4d91894305 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Mon, 30 Jun 2025 18:48:27 +0200 Subject: [PATCH 215/682] Fix GH-18979: DOM\XMLDocument::createComment() triggers undefined behavior with null byte Closes GH-18983. --- NEWS | 4 ++++ ext/dom/tests/modern/xml/gh18979.phpt | 13 +++++++++++++ ext/dom/xml_serializer.c | 6 +++++- 3 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 ext/dom/tests/modern/xml/gh18979.phpt diff --git a/NEWS b/NEWS index 1af910562381d..6bc566be39047 100644 --- a/NEWS +++ b/NEWS @@ -14,6 +14,10 @@ PHP NEWS . Fix memory leaks when returning refcounted value from curl callback. (nielsdos) +- DOM: + . Fixed bug GH-18979 (Dom\XMLDocument::createComment() triggers undefined + behavior with null byte). (nielsdos) + - LDAP: . Fixed GH-18902 ldap_exop/ldap_exop_sync assert triggered on empty request OID. (David Carlier) diff --git a/ext/dom/tests/modern/xml/gh18979.phpt b/ext/dom/tests/modern/xml/gh18979.phpt new file mode 100644 index 0000000000000..3a90bd583773b --- /dev/null +++ b/ext/dom/tests/modern/xml/gh18979.phpt @@ -0,0 +1,13 @@ +--TEST-- +GH-18979 (DOM\XMLDocument::createComment() triggers undefined behavior with null byte) +--EXTENSIONS-- +dom +--FILE-- +createElement("container"); +$container->append($dom->createComment("\0")); +var_dump($container->innerHTML); +?> +--EXPECT-- +string(7) "" diff --git a/ext/dom/xml_serializer.c b/ext/dom/xml_serializer.c index debbb41fdadeb..a4b46082b0ee5 100644 --- a/ext/dom/xml_serializer.c +++ b/ext/dom/xml_serializer.c @@ -640,7 +640,11 @@ static int dom_xml_serialize_comment_node(xmlOutputBufferPtr out, xmlNodePtr com const xmlChar *ptr = comment->content; if (ptr != NULL) { TRY(dom_xml_check_char_production(ptr)); - if (strstr((const char *) ptr, "--") != NULL || ptr[strlen((const char *) ptr) - 1] == '-') { + if (strstr((const char *) ptr, "--") != NULL) { + return -1; + } + size_t len = strlen((const char *) ptr); + if (len > 0 && ptr[len - 1] == '-') { return -1; } } From 59dd0f8a48c48fe17a0426d59716cf33858b810b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Tue, 1 Jul 2025 20:24:11 +0200 Subject: [PATCH 216/682] Zend: Use `zend_bad_method_call()` when cloning from the wrong scope (#18999) --- Zend/zend_builtin_functions.c | 6 +----- Zend/zend_execute.c | 9 --------- Zend/zend_vm_def.h | 2 +- Zend/zend_vm_execute.h | 8 ++++---- tests/classes/factory_and_singleton_007.phpt | 2 +- tests/classes/factory_and_singleton_008.phpt | 2 +- 6 files changed, 8 insertions(+), 21 deletions(-) diff --git a/Zend/zend_builtin_functions.c b/Zend/zend_builtin_functions.c index fc3b0f57d85e7..48e0e86d3b2f8 100644 --- a/Zend/zend_builtin_functions.c +++ b/Zend/zend_builtin_functions.c @@ -93,11 +93,7 @@ ZEND_FUNCTION(clone) if (clone->common.scope != scope) { if (UNEXPECTED(clone->common.fn_flags & ZEND_ACC_PRIVATE) || UNEXPECTED(!zend_check_protected(zend_get_function_root_class(clone), scope))) { - zend_throw_error(NULL, "Call to %s %s::__clone() from %s%s", - zend_visibility_string(clone->common.fn_flags), ZSTR_VAL(clone->common.scope->name), - scope ? "scope " : "global scope", - scope ? ZSTR_VAL(scope->name) : "" - ); + zend_bad_method_call(clone, clone->common.function_name, scope); RETURN_THROWS(); } } diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 5d8d9f4caeb86..e1593cadce7d0 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -4123,15 +4123,6 @@ static zend_never_inline void zend_fetch_this_var(int type OPLINE_DC EXECUTE_DAT } } -static zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_wrong_clone_call(zend_function *clone, zend_class_entry *scope) -{ - zend_throw_error(NULL, "Call to %s %s::__clone() from %s%s", - zend_visibility_string(clone->common.fn_flags), ZSTR_VAL(clone->common.scope->name), - scope ? "scope " : "global scope", - scope ? ZSTR_VAL(scope->name) : "" - ); -} - #if ZEND_INTENSIVE_DEBUGGING #define CHECK_SYMBOL_TABLES() \ diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 51aaf635b3b30..9f7042386a152 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -6046,7 +6046,7 @@ ZEND_VM_COLD_CONST_HANDLER(110, ZEND_CLONE, CONST|TMPVAR|UNUSED|THIS|CV, ANY) if (clone->common.scope != scope) { if (UNEXPECTED(clone->common.fn_flags & ZEND_ACC_PRIVATE) || UNEXPECTED(!zend_check_protected(zend_get_function_root_class(clone), scope))) { - zend_wrong_clone_call(clone, scope); + zend_bad_method_call(clone, clone->common.function_name, scope); FREE_OP1(); ZVAL_UNDEF(EX_VAR(opline->result.var)); HANDLE_EXCEPTION(); diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index f29c6b4726145..cb2af9e49dd64 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -5220,7 +5220,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CLONE_SPEC_CONST_ if (clone->common.scope != scope) { if (UNEXPECTED(clone->common.fn_flags & ZEND_ACC_PRIVATE) || UNEXPECTED(!zend_check_protected(zend_get_function_root_class(clone), scope))) { - zend_wrong_clone_call(clone, scope); + zend_bad_method_call(clone, clone->common.function_name, scope); ZVAL_UNDEF(EX_VAR(opline->result.var)); HANDLE_EXCEPTION(); @@ -15469,7 +15469,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CLONE_SPEC_TMPVAR_HANDLER(ZEND if (clone->common.scope != scope) { if (UNEXPECTED(clone->common.fn_flags & ZEND_ACC_PRIVATE) || UNEXPECTED(!zend_check_protected(zend_get_function_root_class(clone), scope))) { - zend_wrong_clone_call(clone, scope); + zend_bad_method_call(clone, clone->common.function_name, scope); zval_ptr_dtor_nogc(EX_VAR(opline->op1.var)); ZVAL_UNDEF(EX_VAR(opline->result.var)); HANDLE_EXCEPTION(); @@ -33566,7 +33566,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CLONE_SPEC_UNUSED_HANDLER(ZEND if (clone->common.scope != scope) { if (UNEXPECTED(clone->common.fn_flags & ZEND_ACC_PRIVATE) || UNEXPECTED(!zend_check_protected(zend_get_function_root_class(clone), scope))) { - zend_wrong_clone_call(clone, scope); + zend_bad_method_call(clone, clone->common.function_name, scope); ZVAL_UNDEF(EX_VAR(opline->result.var)); HANDLE_EXCEPTION(); @@ -41087,7 +41087,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CLONE_SPEC_CV_HANDLER(ZEND_OPC if (clone->common.scope != scope) { if (UNEXPECTED(clone->common.fn_flags & ZEND_ACC_PRIVATE) || UNEXPECTED(!zend_check_protected(zend_get_function_root_class(clone), scope))) { - zend_wrong_clone_call(clone, scope); + zend_bad_method_call(clone, clone->common.function_name, scope); ZVAL_UNDEF(EX_VAR(opline->result.var)); HANDLE_EXCEPTION(); diff --git a/tests/classes/factory_and_singleton_007.phpt b/tests/classes/factory_and_singleton_007.phpt index fc232bdb8655a..6cf120e18befa 100644 --- a/tests/classes/factory_and_singleton_007.phpt +++ b/tests/classes/factory_and_singleton_007.phpt @@ -17,4 +17,4 @@ try { ?> --EXPECT-- -Error: Call to protected test::__clone() from global scope +Error: Call to protected method test::__clone() from global scope diff --git a/tests/classes/factory_and_singleton_008.phpt b/tests/classes/factory_and_singleton_008.phpt index 672c083270730..a23af647592f5 100644 --- a/tests/classes/factory_and_singleton_008.phpt +++ b/tests/classes/factory_and_singleton_008.phpt @@ -17,4 +17,4 @@ try { ?> --EXPECT-- -Error: Call to private test::__clone() from global scope +Error: Call to private method test::__clone() from global scope From 66376389feb55a1fc3afd38e78af7f67fc0ab374 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Tue, 1 Jul 2025 21:53:33 +0200 Subject: [PATCH 217/682] Update uriparser to commit 5f7c6d88c50f548d0c7f499c22d36f51d34775b3 While there, fix Windows build by adding UriResolve.c to the sources. --- ext/uri/config.m4 | 4 +- ext/uri/config.w32 | 2 +- ext/uri/uriparser/include/uriparser/Uri.h | 41 +++ ext/uri/uriparser/include/uriparser/UriBase.h | 3 +- ext/uri/uriparser/src/UriCommon.c | 40 ++- ext/uri/uriparser/src/UriCommon.h | 7 +- ext/uri/uriparser/src/UriCopy.c | 234 ++++++++++++++++++ ext/uri/uriparser/src/UriCopy.h | 78 ++++++ ext/uri/uriparser/src/UriNormalize.c | 107 +++++++- ext/uri/uriparser/src/UriNormalize.h | 76 ++++++ ext/uri/uriparser/src/UriRecompose.c | 4 +- ext/uri/uriparser/src/UriResolve.c | 4 +- 12 files changed, 574 insertions(+), 26 deletions(-) create mode 100644 ext/uri/uriparser/src/UriCopy.c create mode 100644 ext/uri/uriparser/src/UriCopy.h create mode 100644 ext/uri/uriparser/src/UriNormalize.h diff --git a/ext/uri/config.m4 b/ext/uri/config.m4 index 08dc044d8d29f..3631ad3c5c06d 100644 --- a/ext/uri/config.m4 +++ b/ext/uri/config.m4 @@ -11,8 +11,8 @@ AC_DEFINE([URI_ENABLE_ANSI], [1], [Define to 1 for enabling ANSI support of urip AC_DEFINE([URI_NO_UNICODE], [1], [Define to 1 for disabling unicode support of uriparser.]) URIPARSER_DIR="uriparser" -URIPARSER_SOURCES="$URIPARSER_DIR/src/UriCommon.c $URIPARSER_DIR/src/UriCompare.c $URIPARSER_DIR/src/UriEscape.c \ -$URIPARSER_DIR/src/UriFile.c $URIPARSER_DIR/src/UriIp4.c $URIPARSER_DIR/src/UriIp4Base.c \ +URIPARSER_SOURCES="$URIPARSER_DIR/src/UriCommon.c $URIPARSER_DIR/src/UriCompare.c $URIPARSER_DIR/src/UriCopy.c \ +$URIPARSER_DIR/src/UriEscape.c $URIPARSER_DIR/src/UriFile.c $URIPARSER_DIR/src/UriIp4.c $URIPARSER_DIR/src/UriIp4Base.c \ $URIPARSER_DIR/src/UriMemory.c $URIPARSER_DIR/src/UriNormalize.c $URIPARSER_DIR/src/UriNormalizeBase.c \ $URIPARSER_DIR/src/UriParse.c $URIPARSER_DIR/src/UriParseBase.c $URIPARSER_DIR/src/UriQuery.c \ $URIPARSER_DIR/src/UriRecompose.c $URIPARSER_DIR/src/UriResolve.c $URIPARSER_DIR/src/UriShorten.c" diff --git a/ext/uri/config.w32 b/ext/uri/config.w32 index 9c6af0cc5fa7b..8086b4b9bfc93 100644 --- a/ext/uri/config.w32 +++ b/ext/uri/config.w32 @@ -5,5 +5,5 @@ AC_DEFINE("URI_NO_UNICODE", 1, "Define to 1 for disabling unicode support of uri ADD_FLAG("CFLAGS_URI", "/D URI_STATIC_BUILD"); ADD_EXTENSION_DEP('uri', 'lexbor'); -ADD_SOURCES("ext/uri/uriparser/src", "UriCommon.c UriCompare.c UriEscape.c UriFile.c UriIp4.c UriIp4Base.c UriMemory.c UriNormalize.c UriNormalizeBase.c UriParse.c UriParseBase.c UriQuery.c UriRecompose.c UriShorten.c", "uri"); +ADD_SOURCES("ext/uri/uriparser/src", "UriCommon.c UriCompare.c UriCopy.c UriEscape.c UriFile.c UriIp4.c UriIp4Base.c UriMemory.c UriNormalize.c UriNormalizeBase.c UriParse.c UriParseBase.c UriQuery.c UriRecompose.c UriResolve.c UriShorten.c", "uri"); PHP_INSTALL_HEADERS("ext/uri", "php_lexbor.h php_uri.h php_uri_common.h uriparser/src uriparser/include"); diff --git a/ext/uri/uriparser/include/uriparser/Uri.h b/ext/uri/uriparser/include/uriparser/Uri.h index 44bc5acc54b5f..f0f2ad9a34bd0 100644 --- a/ext/uri/uriparser/include/uriparser/Uri.h +++ b/ext/uri/uriparser/include/uriparser/Uri.h @@ -201,6 +201,17 @@ typedef struct URI_TYPE(QueryListStruct) { } URI_TYPE(QueryList); /**< @copydoc UriQueryListStructA */ +/** + * Checks if a URI has the host component set. + * + * @param uri IN: %URI to check + * @return URI_TRUE when host is set, URI_FALSE otherwise + * + * @since 0.9.9 + */ +URI_PUBLIC UriBool URI_FUNC(HasHost)(const URI_TYPE(Uri) * uri); + + /** * Parses a RFC 3986 %URI. @@ -644,6 +655,36 @@ URI_PUBLIC int URI_FUNC(ToString)(URI_CHAR * dest, const URI_TYPE(Uri) * uri, +/** + * Copies a %URI structure. + * + * @param destUri OUT: Output destination + * @param sourceUri IN: %URI to copy + * @param memory IN: Memory manager to use, NULL for default libc + * @return Error code or 0 on success + * + * @see uriCopyUriA + * @since 0.9.9 + */ +URI_PUBLIC int URI_FUNC(CopyUriMm)(URI_TYPE(Uri) * destUri, + const URI_TYPE(Uri) * sourceUri, UriMemoryManager * memory); + + + +/** + * Copies a %URI structure. + * + * @param destUri OUT: Output destination + * @param sourceUri IN: %URI to copy + * @return Error code or 0 on success + * + * @see uriCopyUriMmA + * @since 0.9.9 + */ +URI_PUBLIC int URI_FUNC(CopyUri)(URI_TYPE(Uri) * destUri, const URI_TYPE(Uri) * sourceUri); + + + /** * Determines the components of a %URI that are not normalized. * diff --git a/ext/uri/uriparser/include/uriparser/UriBase.h b/ext/uri/uriparser/include/uriparser/UriBase.h index dc3883e65167c..46c02135bb112 100644 --- a/ext/uri/uriparser/include/uriparser/UriBase.h +++ b/ext/uri/uriparser/include/uriparser/UriBase.h @@ -258,7 +258,8 @@ typedef enum UriNormalizationMaskEnum { URI_NORMALIZE_HOST = 1 << 2, /**< Normalize host (fix uppercase letters) */ URI_NORMALIZE_PATH = 1 << 3, /**< Normalize path (fix uppercase percent-encodings and redundant dot segments) */ URI_NORMALIZE_QUERY = 1 << 4, /**< Normalize query (fix uppercase percent-encodings) */ - URI_NORMALIZE_FRAGMENT = 1 << 5 /**< Normalize fragment (fix uppercase percent-encodings) */ + URI_NORMALIZE_FRAGMENT = 1 << 5, /**< Normalize fragment (fix uppercase percent-encodings) */ + URI_NORMALIZE_PORT = 1 << 6 /**< Normalize port (drop leading zeros) @since 0.9.9 */ } UriNormalizationMask; /**< @copydoc UriNormalizationMaskEnum */ diff --git a/ext/uri/uriparser/src/UriCommon.c b/ext/uri/uriparser/src/UriCommon.c index 88e2767d71cb2..ccec5d4d5c8bf 100644 --- a/ext/uri/uriparser/src/UriCommon.c +++ b/ext/uri/uriparser/src/UriCommon.c @@ -119,6 +119,40 @@ int URI_FUNC(CompareRange)( +UriBool URI_FUNC(CopyRange)(URI_TYPE(TextRange) * destRange, + const URI_TYPE(TextRange) * sourceRange, UriMemoryManager * memory) { + const int lenInChars = (int)(sourceRange->afterLast - sourceRange->first); + const int lenInBytes = lenInChars * sizeof(URI_CHAR); + URI_CHAR * dup = memory->malloc(memory, lenInBytes); + if (dup == NULL) { + return URI_FALSE; + } + memcpy(dup, sourceRange->first, lenInBytes); + destRange->first = dup; + destRange->afterLast = dup + lenInChars; + + return URI_TRUE; +} + + + +UriBool URI_FUNC(CopyRangeAsNeeded)(URI_TYPE(TextRange) * destRange, + const URI_TYPE(TextRange) * sourceRange, UriBool useSafe, UriMemoryManager * memory) { + if (sourceRange->first == NULL) { + destRange->first = NULL; + destRange->afterLast = NULL; + } else if (sourceRange->first == sourceRange->afterLast && useSafe) { + destRange->first = URI_FUNC(SafeToPointTo); + destRange->afterLast = URI_FUNC(SafeToPointTo); + } else { + return URI_FUNC(CopyRange)(destRange, sourceRange, memory); + } + + return URI_TRUE; +} + + + UriBool URI_FUNC(RemoveDotSegmentsEx)(URI_TYPE(Uri) * uri, UriBool relative, UriBool pathOwned, UriMemoryManager * memory) { URI_TYPE(PathSegment) * walker; @@ -189,7 +223,7 @@ UriBool URI_FUNC(RemoveDotSegmentsEx)(URI_TYPE(Uri) * uri, if (prev == NULL) { /* Last and first */ - if (URI_FUNC(IsHostSet)(uri)) { + if (URI_FUNC(HasHost)(uri)) { /* Replace "." with empty segment to represent trailing slash */ walker->text.first = URI_FUNC(SafeToPointTo); walker->text.afterLast = URI_FUNC(SafeToPointTo); @@ -463,7 +497,7 @@ URI_CHAR URI_FUNC(HexToLetterEx)(unsigned int value, UriBool uppercase) { /* Checks if a URI has the host component set. */ -UriBool URI_FUNC(IsHostSet)(const URI_TYPE(Uri) * uri) { +UriBool URI_FUNC(HasHost)(const URI_TYPE(Uri) * uri) { return (uri != NULL) && ((uri->hostText.first != NULL) || (uri->hostData.ip4 != NULL) @@ -601,7 +635,7 @@ void URI_FUNC(FixEmptyTrailSegment)(URI_TYPE(Uri) * uri, UriMemoryManager * memory) { /* Fix path if only one empty segment */ if (!uri->absolutePath - && !URI_FUNC(IsHostSet)(uri) + && !URI_FUNC(HasHost)(uri) && (uri->pathHead != NULL) && (uri->pathHead->next == NULL) && (uri->pathHead->text.first == uri->pathHead->text.afterLast)) { diff --git a/ext/uri/uriparser/src/UriCommon.h b/ext/uri/uriparser/src/UriCommon.h index 42311ddc98b2d..8dffab9f9f602 100644 --- a/ext/uri/uriparser/src/UriCommon.h +++ b/ext/uri/uriparser/src/UriCommon.h @@ -82,6 +82,11 @@ int URI_FUNC(CompareRange)( const URI_TYPE(TextRange) * a, const URI_TYPE(TextRange) * b); +UriBool URI_FUNC(CopyRange)(URI_TYPE(TextRange) * destRange, + const URI_TYPE(TextRange) * sourceRange, UriMemoryManager * memory); +UriBool URI_FUNC(CopyRangeAsNeeded)(URI_TYPE(TextRange) * destRange, + const URI_TYPE(TextRange) * sourceRange, UriBool useSafe, UriMemoryManager * memory); + UriBool URI_FUNC(RemoveDotSegmentsAbsolute)(URI_TYPE(Uri) * uri, UriMemoryManager * memory); UriBool URI_FUNC(RemoveDotSegmentsEx)(URI_TYPE(Uri) * uri, @@ -91,8 +96,6 @@ unsigned char URI_FUNC(HexdigToInt)(URI_CHAR hexdig); URI_CHAR URI_FUNC(HexToLetter)(unsigned int value); URI_CHAR URI_FUNC(HexToLetterEx)(unsigned int value, UriBool uppercase); -UriBool URI_FUNC(IsHostSet)(const URI_TYPE(Uri) * uri); - UriBool URI_FUNC(CopyPath)(URI_TYPE(Uri) * dest, const URI_TYPE(Uri) * source, UriMemoryManager * memory); UriBool URI_FUNC(CopyAuthority)(URI_TYPE(Uri) * dest, diff --git a/ext/uri/uriparser/src/UriCopy.c b/ext/uri/uriparser/src/UriCopy.c new file mode 100644 index 0000000000000..0974ec5c0406d --- /dev/null +++ b/ext/uri/uriparser/src/UriCopy.c @@ -0,0 +1,234 @@ +/* + * uriparser - RFC 3986 URI parsing library + * + * Copyright (C) 2007, Weijia Song + * Copyright (C) 2007, Sebastian Pipping + * Copyright (C) 2025, Máté Kocsis + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of + * its contributors may be used to endorse or promote products + * derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file UriCopy.c + * Holds the RFC 3986 %URI normalization implementation. + * NOTE: This source file includes itself twice. + */ + +/* What encodings are enabled? */ +#include +#if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE)) +/* Include SELF twice */ +# ifdef URI_ENABLE_ANSI +# define URI_PASS_ANSI 1 +# include "UriCopy.c" +# undef URI_PASS_ANSI +# endif +# ifdef URI_ENABLE_UNICODE +# define URI_PASS_UNICODE 1 +# include "UriCopy.c" +# undef URI_PASS_UNICODE +# endif +#else +# ifdef URI_PASS_ANSI +# include +# else +# include +# include +# endif + + + +#ifndef URI_DOXYGEN +# include +# include "UriCommon.h" +# include "UriMemory.h" +# include "UriNormalize.h" +# include "UriCopy.h" +#endif + + + +static void URI_FUNC(PreventLeakageAfterCopy)(URI_TYPE(Uri) * uri, + unsigned int revertMask, UriMemoryManager * memory) { + URI_FUNC(PreventLeakage)(uri, revertMask, memory); + + if (uri->hostData.ip4 != NULL) { + memory->free(memory, uri->hostData.ip4); + uri->hostData.ip4 = NULL; + } else if (uri->hostData.ip6 != NULL) { + memory->free(memory, uri->hostData.ip6); + uri->hostData.ip6 = NULL; + } + + if (revertMask & URI_NORMALIZE_PORT) { + if (uri->portText.first != uri->portText.afterLast) { + memory->free(memory, (URI_CHAR *)uri->portText.first); + } + uri->portText.first = NULL; + uri->portText.afterLast = NULL; + } +} + + + +int URI_FUNC(CopyUriMm)(URI_TYPE(Uri) * destUri, + const URI_TYPE(Uri) * sourceUri, UriMemoryManager * memory) { + unsigned int doneMask = URI_NORMALIZED; + + if (sourceUri == NULL || destUri == NULL) { + return URI_ERROR_NULL; + } + + URI_CHECK_MEMORY_MANAGER(memory); /* may return */ + + if (URI_FUNC(CopyRangeAsNeeded)(&destUri->scheme, &sourceUri->scheme, URI_FALSE, memory) == URI_FALSE) { + return URI_ERROR_MALLOC; + } + + doneMask |= URI_NORMALIZE_SCHEME; + + if (URI_FUNC(CopyRangeAsNeeded)(&destUri->userInfo, &sourceUri->userInfo, URI_FALSE, memory) == URI_FALSE) { + URI_FUNC(PreventLeakageAfterCopy)(destUri, doneMask, memory); + return URI_ERROR_MALLOC; + } + + doneMask |= URI_NORMALIZE_USER_INFO; + + if (URI_FUNC(CopyRangeAsNeeded)(&destUri->hostText, &sourceUri->hostText, URI_TRUE, memory) == URI_FALSE) { + URI_FUNC(PreventLeakageAfterCopy)(destUri, doneMask, memory); + return URI_ERROR_MALLOC; + } + + doneMask |= URI_NORMALIZE_HOST; + + if (sourceUri->hostData.ip4 == NULL) { + destUri->hostData.ip4 = NULL; + } else { + destUri->hostData.ip4 = memory->malloc(memory, sizeof(UriIp4)); + if (destUri->hostData.ip4 == NULL) { + URI_FUNC(PreventLeakageAfterCopy)(destUri, doneMask, memory); + return URI_ERROR_MALLOC; + } + *(destUri->hostData.ip4) = *(sourceUri->hostData.ip4); + } + + if (sourceUri->hostData.ip6 == NULL) { + destUri->hostData.ip6 = NULL; + } else { + destUri->hostData.ip6 = memory->malloc(memory, sizeof(UriIp6)); + if (destUri->hostData.ip6 == NULL) { + URI_FUNC(PreventLeakageAfterCopy)(destUri, doneMask, memory); + return URI_ERROR_MALLOC; + } + *(destUri->hostData.ip6) = *(sourceUri->hostData.ip6); + } + + if (URI_FUNC(CopyRangeAsNeeded)(&destUri->hostData.ipFuture, &sourceUri->hostData.ipFuture, URI_FALSE, memory) == URI_FALSE) { + URI_FUNC(PreventLeakageAfterCopy)(destUri, doneMask, memory); + return URI_ERROR_MALLOC; + } + + if (URI_FUNC(CopyRangeAsNeeded)(&destUri->portText, &sourceUri->portText, URI_FALSE, memory) == URI_FALSE) { + URI_FUNC(PreventLeakageAfterCopy)(destUri, doneMask, memory); + return URI_ERROR_MALLOC; + } + + doneMask |= URI_NORMALIZE_PORT; + + destUri->pathHead = NULL; + destUri->pathTail = NULL; + + if (sourceUri->pathHead != NULL) { + URI_TYPE(PathSegment) * sourceWalker = sourceUri->pathHead; + URI_TYPE(PathSegment) * destPrev = NULL; + + while (sourceWalker != NULL) { + URI_TYPE(PathSegment) * destWalker = memory->malloc(memory, sizeof(URI_TYPE(PathSegment))); + if (destWalker == NULL) { + URI_FUNC(PreventLeakageAfterCopy)(destUri, doneMask, memory); + return URI_ERROR_MALLOC; + } + + destWalker->text.first = NULL; + destWalker->text.afterLast = NULL; + destWalker->next = NULL; + destWalker->reserved = NULL; + + if (destUri->pathHead == NULL) { + destUri->pathHead = destWalker; + doneMask |= URI_NORMALIZE_PATH; + } + + if (URI_FUNC(CopyRangeAsNeeded)(&destWalker->text, &sourceWalker->text, URI_TRUE, memory) == URI_FALSE) { + URI_FUNC(PreventLeakageAfterCopy)(destUri, doneMask, memory); + return URI_ERROR_MALLOC; + } + + if (destPrev != NULL) { + destPrev->next = destWalker; + } + + destPrev = destWalker; + sourceWalker = sourceWalker->next; + + destUri->pathTail = destWalker; + } + } + + if (URI_FUNC(CopyRangeAsNeeded)(&destUri->query, &sourceUri->query, URI_FALSE, memory) == URI_FALSE) { + URI_FUNC(PreventLeakageAfterCopy)(destUri, doneMask, memory); + return URI_ERROR_MALLOC; + } + + doneMask |= URI_NORMALIZE_QUERY; + + if (URI_FUNC(CopyRangeAsNeeded)(&destUri->fragment, &sourceUri->fragment, URI_FALSE, memory) == URI_FALSE) { + URI_FUNC(PreventLeakageAfterCopy)(destUri, doneMask, memory); + return URI_ERROR_MALLOC; + } + + destUri->absolutePath = sourceUri->absolutePath; + destUri->owner = URI_TRUE; + destUri->reserved = NULL; + + return URI_SUCCESS; +} + + + +int URI_FUNC(CopyUri)(URI_TYPE(Uri) * destUri, + const URI_TYPE(Uri) * sourceUri) { + return URI_FUNC(CopyUriMm)(destUri, sourceUri, NULL); +} + +#endif diff --git a/ext/uri/uriparser/src/UriCopy.h b/ext/uri/uriparser/src/UriCopy.h new file mode 100644 index 0000000000000..952b1df4f9cb3 --- /dev/null +++ b/ext/uri/uriparser/src/UriCopy.h @@ -0,0 +1,78 @@ +/* + * uriparser - RFC 3986 URI parsing library + * + * Copyright (C) 2007, Weijia Song + * Copyright (C) 2007, Sebastian Pipping + * Copyright (C) 2025, Máté Kocsis + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of + * its contributors may be used to endorse or promote products + * derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if (defined(URI_PASS_ANSI) && !defined(URI_COPY_H_ANSI)) \ + || (defined(URI_PASS_UNICODE) && !defined(URI_COPY_H_UNICODE)) \ + || (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE)) +/* What encodings are enabled? */ +#include +#if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE)) +/* Include SELF twice */ +# ifdef URI_ENABLE_ANSI +# define URI_PASS_ANSI 1 +# include "UriCopy.h" +# undef URI_PASS_ANSI +# endif +# ifdef URI_ENABLE_UNICODE +# define URI_PASS_UNICODE 1 +# include "UriCopy.h" +# undef URI_PASS_UNICODE +# endif +/* Only one pass for each encoding */ +#elif (defined(URI_PASS_ANSI) && !defined(URI_COPY_H_ANSI) \ + && defined(URI_ENABLE_ANSI)) || (defined(URI_PASS_UNICODE) \ + && !defined(URI_COPY_H_UNICODE) && defined(URI_ENABLE_UNICODE)) +# ifdef URI_PASS_ANSI +# define URI_COPY_H_ANSI 1 +# include +# else +# define URI_COPY_H_UNICODE 1 +# include +# endif + + + +int URI_FUNC(CopyUriMm)(URI_TYPE(Uri) * destUri, + const URI_TYPE(Uri) * sourceUri, UriMemoryManager * memory); +int URI_FUNC(CopyUri)(URI_TYPE(Uri) * destUri, + const URI_TYPE(Uri) * sourceUri); + +#endif +#endif diff --git a/ext/uri/uriparser/src/UriNormalize.c b/ext/uri/uriparser/src/UriNormalize.c index 0cf353f111119..56b19573665e5 100644 --- a/ext/uri/uriparser/src/UriNormalize.c +++ b/ext/uri/uriparser/src/UriNormalize.c @@ -109,12 +109,9 @@ static void URI_FUNC(LowercaseInplaceExceptPercentEncoding)(const URI_CHAR * fir static UriBool URI_FUNC(LowercaseMalloc)(const URI_CHAR ** first, const URI_CHAR ** afterLast, UriMemoryManager * memory); -static void URI_FUNC(PreventLeakage)(URI_TYPE(Uri) * uri, - unsigned int revertMask, UriMemoryManager * memory); - -static URI_INLINE void URI_FUNC(PreventLeakage)(URI_TYPE(Uri) * uri, +void URI_FUNC(PreventLeakage)(URI_TYPE(Uri) * uri, unsigned int revertMask, UriMemoryManager * memory) { if (revertMask & URI_NORMALIZE_SCHEME) { /* NOTE: A scheme cannot be the empty string @@ -407,15 +404,9 @@ static URI_INLINE UriBool URI_FUNC(MakeRangeOwner)(unsigned int * doneMask, && (range->first != NULL) && (range->afterLast != NULL) && (range->afterLast > range->first)) { - const int lenInChars = (int)(range->afterLast - range->first); - const int lenInBytes = lenInChars * sizeof(URI_CHAR); - URI_CHAR * dup = memory->malloc(memory, lenInBytes); - if (dup == NULL) { - return URI_FALSE; /* Raises malloc error */ - } - memcpy(dup, range->first, lenInBytes); - range->first = dup; - range->afterLast = dup + lenInChars; + if (URI_FUNC(CopyRange)(range, range, memory) == URI_FALSE) { + return URI_FALSE; + } *doneMask |= maskTest; } return URI_TRUE; @@ -557,6 +548,75 @@ int URI_FUNC(NormalizeSyntax)(URI_TYPE(Uri) * uri) { } +static const URI_CHAR * URI_FUNC(PastLeadingZeros)(const URI_CHAR * first, const URI_CHAR * afterLast) { + assert(first != NULL); + assert(afterLast != NULL); + assert(first != afterLast); + + { + /* Find the first non-zero character */ + const URI_CHAR * remainderFirst = first; + while ((remainderFirst < afterLast) && (remainderFirst[0] == _UT('0'))) { + remainderFirst++; + } + + /* Is the string /all/ zeros? */ + if (remainderFirst == afterLast) { + /* Yes, and length is >=1 because we ruled out the empty string earlier; + * pull back onto rightmost zero */ + assert(remainderFirst > first); + remainderFirst--; + assert(remainderFirst[0] == _UT('0')); + } + + return remainderFirst; + } +} + + + +static void URI_FUNC(DropLeadingZerosInplace)(URI_CHAR * first, const URI_CHAR ** afterLast) { + assert(first != NULL); + assert(afterLast != NULL); + assert(*afterLast != NULL); + + if (first == *afterLast) { + return; + } + + { + const URI_CHAR * const remainderFirst = URI_FUNC(PastLeadingZeros)(first, *afterLast); + + if (remainderFirst > first) { + const size_t remainderLen = *afterLast - remainderFirst; + memmove(first, remainderFirst, remainderLen * sizeof(URI_CHAR)); + first[remainderLen] = _UT('\0'); + *afterLast = first + remainderLen; + } + } +} + + + +static void URI_FUNC(AdvancePastLeadingZeros)( + const URI_CHAR ** first, const URI_CHAR * afterLast) { + assert(first != NULL); + assert(*first != NULL); + assert(afterLast != NULL); + + if (*first == afterLast) { + return; + } + + { + const URI_CHAR * const remainderFirst = URI_FUNC(PastLeadingZeros)(*first, afterLast); + + /* Cut off leading zeros */ + *first = remainderFirst; + } +} + + static URI_INLINE int URI_FUNC(NormalizeSyntaxEngine)(URI_TYPE(Uri) * uri, unsigned int inMask, unsigned int * outMask, @@ -658,6 +718,27 @@ static URI_INLINE int URI_FUNC(NormalizeSyntaxEngine)(URI_TYPE(Uri) * uri, } } + /* Port */ + if (outMask != NULL) { + /* Is there a port even? */ + if (uri->portText.first != NULL) { + /* Determine whether the port is already normalized, i.e. either "", "0" or no leading zeros */ + const size_t portLen = uri->portText.afterLast - uri->portText.first; + if ((portLen > 1) && (uri->portText.first[0] == _UT('0'))) { + *outMask |= URI_NORMALIZE_PORT; + } + } + } else { + /* Normalize the port, i.e. drop leading zeros (except for string "0") */ + if ((inMask & URI_NORMALIZE_PORT) && (uri->portText.first != NULL)) { + if (uri->owner) { + URI_FUNC(DropLeadingZerosInplace)((URI_CHAR *)uri->portText.first, &(uri->portText.afterLast)); + } else { + URI_FUNC(AdvancePastLeadingZeros)(&(uri->portText.first), uri->portText.afterLast); + } + } + } + /* User info */ if (outMask != NULL) { const UriBool normalizeUserInfo = URI_FUNC(ContainsUglyPercentEncoding)( diff --git a/ext/uri/uriparser/src/UriNormalize.h b/ext/uri/uriparser/src/UriNormalize.h new file mode 100644 index 0000000000000..cb58085b7d318 --- /dev/null +++ b/ext/uri/uriparser/src/UriNormalize.h @@ -0,0 +1,76 @@ +/* + * uriparser - RFC 3986 URI parsing library + * + * Copyright (C) 2018, Weijia Song + * Copyright (C) 2018, Sebastian Pipping + * Copyright (C) 2025, Máté Kocsis + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of + * its contributors may be used to endorse or promote products + * derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if (defined(URI_PASS_ANSI) && !defined(URI_NORMALIZE_H_ANSI)) \ + || (defined(URI_PASS_UNICODE) && !defined(URI_NORMALIZE_H_UNICODE)) \ + || (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE)) +/* What encodings are enabled? */ +#include +#if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE)) +/* Include SELF twice */ +# ifdef URI_ENABLE_ANSI +# define URI_PASS_ANSI 1 +# include "UriNormalize.h" +# undef URI_PASS_ANSI +# endif +# ifdef URI_ENABLE_UNICODE +# define URI_PASS_UNICODE 1 +# include "UriNormalize.h" +# undef URI_PASS_UNICODE +# endif +/* Only one pass for each encoding */ +#elif (defined(URI_PASS_ANSI) && !defined(URI_NORMALIZE_H_ANSI) \ + && defined(URI_ENABLE_ANSI)) || (defined(URI_PASS_UNICODE) \ + && !defined(URI_NORMALIZE_H_UNICODE) && defined(URI_ENABLE_UNICODE)) +# ifdef URI_PASS_ANSI +# define URI_NORMALIZE_H_ANSI 1 +# include +# else +# define URI_NORMALIZE_H_UNICODE 1 +# include +# endif + + + +void URI_FUNC(PreventLeakage)(URI_TYPE(Uri) * uri, + unsigned int revertMask, UriMemoryManager * memory); + +#endif +#endif diff --git a/ext/uri/uriparser/src/UriRecompose.c b/ext/uri/uriparser/src/UriRecompose.c index 5027eca6cfa33..1567efc81dcbf 100644 --- a/ext/uri/uriparser/src/UriRecompose.c +++ b/ext/uri/uriparser/src/UriRecompose.c @@ -152,7 +152,7 @@ static URI_INLINE int URI_FUNC(ToStringEngine)(URI_CHAR * dest, /* [05/19] endif; */ } /* [06/19] if defined(authority) then */ - if (URI_FUNC(IsHostSet)(uri)) { + if (URI_FUNC(HasHost)(uri)) { /* [07/19] append "//" to result; */ if (dest != NULL) { if (written + 2 <= maxChars) { @@ -422,7 +422,7 @@ static URI_INLINE int URI_FUNC(ToStringEngine)(URI_CHAR * dest, /* [10/19] append path to result; */ /* Slash needed here? */ if (uri->absolutePath || ((uri->pathHead != NULL) - && URI_FUNC(IsHostSet)(uri))) { + && URI_FUNC(HasHost)(uri))) { if (dest != NULL) { if (written + 1 <= maxChars) { memcpy(dest + written, _UT("/"), diff --git a/ext/uri/uriparser/src/UriResolve.c b/ext/uri/uriparser/src/UriResolve.c index 80031a894d437..8e47e6af8c6f9 100644 --- a/ext/uri/uriparser/src/UriResolve.c +++ b/ext/uri/uriparser/src/UriResolve.c @@ -128,7 +128,7 @@ static int URI_FUNC(ResolveAbsolutePathFlag)(URI_TYPE(Uri) * absWork, return URI_ERROR_NULL; } - if (URI_FUNC(IsHostSet)(absWork) && absWork->absolutePath) { + if (URI_FUNC(HasHost)(absWork) && absWork->absolutePath) { /* Empty segment needed, instead? */ if (absWork->pathHead == NULL) { URI_TYPE(PathSegment) * const segment = memory->malloc(memory, sizeof(URI_TYPE(PathSegment))); @@ -203,7 +203,7 @@ static int URI_FUNC(AddBaseUriImpl)(URI_TYPE(Uri) * absDest, /* [06/32] else */ } else { /* [07/32] if defined(R.authority) then */ - if (URI_FUNC(IsHostSet)(relSource)) { + if (URI_FUNC(HasHost)(relSource)) { /* [08/32] T.authority = R.authority; */ if (!URI_FUNC(CopyAuthority)(absDest, relSource, memory)) { return URI_ERROR_MALLOC; From ca09f4dba463034a1c312426630ed1672e7d90d0 Mon Sep 17 00:00:00 2001 From: Ben Ramsey Date: Tue, 1 Jul 2025 15:17:40 -0500 Subject: [PATCH 218/682] PHP-8.1 is now for PHP 8.1.34-dev --- NEWS | 4 ++++ Zend/zend.h | 2 +- configure.ac | 2 +- main/php_version.h | 6 +++--- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/NEWS b/NEWS index 8c8b28fb98186..d52050e3a8e34 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,9 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +?? ??? ????, PHP 8.1.34 + + + 03 Jul 2025, PHP 8.1.33 - PGSQL: diff --git a/Zend/zend.h b/Zend/zend.h index 6562119e8363c..29bb98c85fca2 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -20,7 +20,7 @@ #ifndef ZEND_H #define ZEND_H -#define ZEND_VERSION "4.1.33-dev" +#define ZEND_VERSION "4.1.34-dev" #define ZEND_ENGINE_3 diff --git a/configure.ac b/configure.ac index 0f45db92ba7de..51256f6b7c177 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.1.33-dev],[https://github.com/php/php-src/issues],[php],[https://www.php.net]) +AC_INIT([PHP],[8.1.34-dev],[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/main/php_version.h b/main/php_version.h index 2aee6f19d16a4..96bd615c07389 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 1 -#define PHP_RELEASE_VERSION 33 +#define PHP_RELEASE_VERSION 34 #define PHP_EXTRA_VERSION "-dev" -#define PHP_VERSION "8.1.33-dev" -#define PHP_VERSION_ID 80133 +#define PHP_VERSION "8.1.34-dev" +#define PHP_VERSION_ID 80134 From 642d72984755b1d96d2fb3d5edccb6057faa50ff Mon Sep 17 00:00:00 2001 From: DanielEScherzer Date: Tue, 1 Jul 2025 13:24:44 -0700 Subject: [PATCH 219/682] release-process: update some confusing parts (#18934) Update based on my training with Pierrick * dates should correspond to when releases are released, not tagged * qa.php.net is no longer used * bugs.php.net is no longer used * multiple commits to web-php can be combined --- docs/release-process.md | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/docs/release-process.md b/docs/release-process.md index 6eec8ff9f6435..422ab1be16189 100644 --- a/docs/release-process.md +++ b/docs/release-process.md @@ -162,7 +162,10 @@ slightly different steps. We'll call attention where the steps differ. 4. Using your local-only release branch, bump the version numbers in `main/php_version.h`, `Zend/zend.h`, `configure.ac`, and possibly `NEWS`. - + + The date for NEWS should be the date of the announcement (Thursday), + *not* the date of the tagging (Tuesday). + For examples, see [Update versions for PHP 8.1.0beta3][] (for a pre-GA example) or [Update versions for PHP 8.1.6RC1][] along with [Update NEWS for PHP 8.1.6RC1][] (for a post-GA example). @@ -250,6 +253,9 @@ slightly different steps. We'll call attention where the steps differ. git commit --gpg-sign=YOURKEYID -m "[ci skip] Update NEWS for PHP X.Y.Z alpha2" ``` + The NEWS is updated at the *start* of the cycle for the next tag, e.g. + [Update NEWS for PHP 8.2.0 alpha2][] was sent as part of tagging 8.2.0 alpha **1**. + 🔷 **For post-GA releases only,** switch back to the *version branch* for your release (e.g., `PHP-8.2`) and bump the version numbers in `main/php_version.h`, `Zend/zend.h`, `configure.ac` and `NEWS`. This prepares @@ -365,6 +371,10 @@ slightly different steps. We'll call attention where the steps differ. Follow the documentation in the file for editing the QA release information. + > 🚨 **Attention** \ + > **For pre-GA releases only,** don't commit yet, because you need to add an + > announcement with the release. After updating `$QA_RELEASES`, skip to step 2 below. + Add, commit, and push your changes, when finished. ```shell @@ -408,6 +418,15 @@ slightly different steps. We'll call attention where the steps differ. text slightly to indicate progression through the pre-release cycle. For example, here are all the news posts for the pre-GA releases of PHP 8.1.0: + > 💬 **Hint** \ + > If you are going to base your language on one of these old announcements, + > remember that + > * `qa.php.net` has been replaced with https://www.php.net/release-candidates.php + > * `bugs.php.net` has been replaced with GitHub issues, use + > `https://github.com/php/php-src/issues/new?template=bug_report.yml` + > to link directly to the form for creating a new bug report. + > * Since 8.4 there have only been 4 release candidates for PHP X.Y.0, rather than 6. + * [Announce 8.1.0alpha1](https://github.com/php/web-php/commit/57b9675c8d8550493289fa1fba77427c93cdd472) * [Announce 8.1.0alpha2](https://github.com/php/web-php/commit/cec044fc0763f5cfa77d0e79479f8b6279023570) * [Announce 8.1.0alpha3](https://github.com/php/web-php/commit/5c480765f444a3fddfd575e01fe0be3fcfdde05b) @@ -431,7 +450,7 @@ slightly different steps. We'll call attention where the steps differ. > When a version is in its post-GA phase, we do not post news entries for > non-stable releases. -3. Wait for the web and qa sites to update with the new information before +3. Wait for the php site to update with the new information before sending announcements. This could take up to an hour. 4. Send *separate* announcement emails to: @@ -720,10 +739,10 @@ slightly different steps. We'll call attention where the steps differ. The array probably contains information about the RC released two weeks ago in preparation for the current release. Since the current release is now GA, - it's time to remove the RC build from the QA website. + it's time to remove the RC build from the release candidates page. It is sufficient to set the `number` property for the release to `0` to - stop displaying the RC build on the QA website. You may also remove the + stop displaying the RC build on the release candidates page. You may also remove the sha256 hashes for the RC tarballs, but it's not necessary. 9. Review all the changes in `web-php`, commit, and push them. From 45c46504e2b1c013f1cd91f99c477a315f688377 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Tue, 1 Jul 2025 15:02:55 -0700 Subject: [PATCH 220/682] [ci skip] Update NEWS for PHP 8.5.0 alpha2 --- NEWS | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index f3c92852e4db5..3646c48404f81 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,9 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -?? ??? ????, PHP 8.5.0alpha1 +?? ??? ????, PHP 8.5.0alpha2 + + +03 Jul 2025, PHP 8.5.0alpha1 - BCMath: . Simplify `bc_divide()` code. (SakiTakamachi) From 8a75b3c50ced1aedd3bc4ffcc1785deaafb1f2ae Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Wed, 2 Jul 2025 01:45:40 +0200 Subject: [PATCH 221/682] Remove conditional checks for ssize_t type (#18996) The ssize_t type is already used unconditionally in php-src code everywhere except the main/s{n,p}printf.c files. On Windows ssize_t is available as an alias to the SSIZE_T defined in BaseTsd.h (available in affected files through the included windows.h in zend_config.w32.h). This also makes the Autoconf macro PHP_CHECK_SIZEOF obsolete in favor of the AC_CHECK_SIZEOF, which is more convenient to check for types without the need to run the test program - omitting the cross-compilation issues. AC_CHECK_SIZEOF once didn't provide including additional headers (resolved in Autoconf versions after 2.13). --- UPGRADING.INTERNALS | 3 +++ build/php.m4 | 7 ++++++- configure.ac | 3 --- main/snprintf.c | 4 ---- main/spprintf.c | 4 ---- 5 files changed, 9 insertions(+), 12 deletions(-) diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS index 6b5959fe030fb..67c779ea637c8 100644 --- a/UPGRADING.INTERNALS +++ b/UPGRADING.INTERNALS @@ -72,11 +72,14 @@ PHP 8.5 INTERNALS UPGRADE NOTES . Autoconf macro PHP_AP_EXTRACT_VERSION has been removed. . Autoconf macro PHP_BUILD_THREAD_SAFE has been removed (set enable_zts manually). + . Autoconf macro PHP_CHECK_SIZEOF is obsolete (use AC_CHECK_SIZEOF). . Autoconf macro PHP_DEF_HAVE has been removed (use AC_DEFINE). . Autoconf macro PHP_OUTPUT has been removed (use AC_CONFIG_FILES). . Autoconf macro PHP_TEST_BUILD has been removed (use AC_* macros). . Preprocessor macro HAVE_PTRDIFF_T has been removed. . Preprocessor macro HAVE_INTMAX_T has been removed. + . Preprocessor macro HAVE_SSIZE_T has been removed. + . Preprocessor macro SIZEOF_SSIZE_T has been removed. ======================== 3. Module changes diff --git a/build/php.m4 b/build/php.m4 index 8cdf318083fbb..db4265c66fc67 100644 --- a/build/php.m4 +++ b/build/php.m4 @@ -1014,7 +1014,9 @@ dnl _PHP_CHECK_SIZEOF(type, cross-value, extra-headers [, found-action [, not-fo dnl dnl Internal helper macro. dnl -AC_DEFUN([_PHP_CHECK_SIZEOF], [ +AC_DEFUN([_PHP_CHECK_SIZEOF], +[m4_warn([obsolete], + [The PHP_CHECK_SIZEOF macro is obsolete. Use AC_CHECK_SIZEOF.]) php_cache_value=php_cv_sizeof_[]$1 AC_CACHE_VAL(php_cv_sizeof_[]$1, [ old_LIBS=$LIBS @@ -1056,6 +1058,9 @@ ifelse([$5],[],,[else $5]) dnl dnl PHP_CHECK_SIZEOF(type, cross-value, extra-headers) dnl +dnl Checks the size of specified "type". This macro is obsolete as of PHP 8.5 in +dnl favor of the AC_CHECK_SIZEOF. +dnl AC_DEFUN([PHP_CHECK_SIZEOF], [ AC_MSG_CHECKING([size of $1]) _PHP_CHECK_SIZEOF($1, $2, $3, [ diff --git a/configure.ac b/configure.ac index 6c8a888a1d2be..663dc32fd28c4 100644 --- a/configure.ac +++ b/configure.ac @@ -451,9 +451,6 @@ AC_CHECK_TYPES([socklen_t], [], [], [ #endif ]) -dnl These are defined elsewhere than stdio.h. -PHP_CHECK_SIZEOF([ssize_t], [8]) - dnl Check stdint types (must be after header check). PHP_CHECK_STDINT_TYPES diff --git a/main/snprintf.c b/main/snprintf.c index 61d9bdd4bddff..e9938c79659f4 100644 --- a/main/snprintf.c +++ b/main/snprintf.c @@ -709,11 +709,7 @@ static size_t format_converter(buffy * odp, const char *fmt, va_list ap) /* {{{ i_num = (int64_t) va_arg(ap, long int); break; case LM_SIZE_T: -#if SIZEOF_SSIZE_T i_num = (int64_t) va_arg(ap, ssize_t); -#else - i_num = (int64_t) va_arg(ap, size_t); -#endif break; #if SIZEOF_LONG_LONG case LM_LONG_LONG: diff --git a/main/spprintf.c b/main/spprintf.c index 3f6005e90b071..0dd7f1552e1c8 100644 --- a/main/spprintf.c +++ b/main/spprintf.c @@ -418,11 +418,7 @@ static void xbuf_format_converter(void *xbuf, bool is_char, const char *fmt, va_ i_num = (int64_t) va_arg(ap, long int); break; case LM_SIZE_T: -#if SIZEOF_SSIZE_T i_num = (int64_t) va_arg(ap, ssize_t); -#else - i_num = (int64_t) va_arg(ap, size_t); -#endif break; #if SIZEOF_LONG_LONG case LM_LONG_LONG: From d5fe1bce63a2446472a5217bc5f02b49106d26e1 Mon Sep 17 00:00:00 2001 From: Saki Takamachi Date: Wed, 2 Jul 2025 11:39:33 +0900 Subject: [PATCH 222/682] PHP-8.4 is now for PHP 8.4.11-dev --- NEWS | 4 ++-- Zend/zend.h | 2 +- configure.ac | 2 +- main/php_version.h | 6 +++--- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/NEWS b/NEWS index 93be6f405c098..9ed0c7259699a 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,6 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -?? ??? ????, PHP 8.4.10 +?? ??? ????, PHP 8.4.11 - Calendar: . Fixed jewishtojd overflow on year argument. (David Carlier) @@ -50,7 +50,7 @@ PHP NEWS . Fixed GH-13264 (fgets() and stream_get_line() do not return false on filter fatal error). (Jakub Zelenka) -03 Jul 2025, PHP 8.4.9 +03 Jul 2025, PHP 8.4.10 - BcMath: . Fixed bug GH-18641 (Accessing a BcMath\Number property by ref crashes). diff --git a/Zend/zend.h b/Zend/zend.h index 15a9b3d8189aa..682dc813b5a6f 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -20,7 +20,7 @@ #ifndef ZEND_H #define ZEND_H -#define ZEND_VERSION "4.4.10-dev" +#define ZEND_VERSION "4.4.11-dev" #define ZEND_ENGINE_3 diff --git a/configure.ac b/configure.ac index c94038e6ca5db..53481b10c056a 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.4.10-dev],[https://github.com/php/php-src/issues],[php],[https://www.php.net]) +AC_INIT([PHP],[8.4.11-dev],[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/main/php_version.h b/main/php_version.h index da01e82826df1..e518de26b2f04 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 4 -#define PHP_RELEASE_VERSION 10 +#define PHP_RELEASE_VERSION 11 #define PHP_EXTRA_VERSION "-dev" -#define PHP_VERSION "8.4.10-dev" -#define PHP_VERSION_ID 80410 +#define PHP_VERSION "8.4.11-dev" +#define PHP_VERSION_ID 80411 From f906fad985f0f87c7b9b9e94b05cbc3a11246f68 Mon Sep 17 00:00:00 2001 From: DanielEScherzer Date: Wed, 2 Jul 2025 02:48:05 -0700 Subject: [PATCH 223/682] release-process: update based on 8.5.0alpha1 (#19004) * Clarify that placeholders with `RCn` are not just for release candidates * Only PHP `X.Y.0` has pre-GA releases, no need to use `X.Y.Z` * Before `PHP-X.Y` has been created, `master` needs to be pushed for NEWS --- docs/release-process.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/docs/release-process.md b/docs/release-process.md index 422ab1be16189..299465229551e 100644 --- a/docs/release-process.md +++ b/docs/release-process.md @@ -96,6 +96,13 @@ releases. `user.signingKey` values to use with your local PHP repositories. See [Conditional Includes For Git Config][] for more information. +11. Any time you see a placeholder like `php-X.Y.ZRCn`, the `RCn` is not always + a release candidate number. The placeholder could also represent any of: + * php-8.4.0alpha1 (initial alpha version) + * php-8.4.0beta2 (one of the beta versions) + * php-8.4.0 (initial GA) + * php-8.4.9 (periodic bugfix or security release) + ## Packaging a non-stable release (alpha/beta/RC) @@ -250,7 +257,7 @@ slightly different steps. We'll call attention where the steps differ. ```shell git add -p - git commit --gpg-sign=YOURKEYID -m "[ci skip] Update NEWS for PHP X.Y.Z alpha2" + git commit --gpg-sign=YOURKEYID -m "[ci skip] Update NEWS for PHP X.Y.0 alpha2" ``` The NEWS is updated at the *start* of the cycle for the next tag, e.g. @@ -289,7 +296,8 @@ slightly different steps. We'll call attention where the steps differ. ```shell git push upstream php-X.Y.ZRCn # tag name git push upstream PHP-X.Y.Z # patch-level version branch (post-GA only) - git push upstream PHP-X.Y # version branch + git push upstream PHP-X.Y # version branch (post-branch creation only) + git push upstream master # version branch (pre-branch creation only) ``` > 🚨 **Attention** \ From ec8b016d08dbe7582102a7baf36c015cd1c5642b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Wed, 2 Jul 2025 13:57:50 +0200 Subject: [PATCH 224/682] uri: Do not create new `UrlValidationErrorType` objects (#19009) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `zend_enum_new()` is not intended to be used “at runtimeâ€, since it will create a new object, breaking the singleton property. Instead `zend_enum_get_case_cstr()` must be used. --- NEWS | 4 +++ ext/uri/php_lexbor.c | 76 ++++++++++++++++++++---------------------- ext/uri/tests/054.phpt | 18 ++++++++++ 3 files changed, 59 insertions(+), 39 deletions(-) create mode 100644 ext/uri/tests/054.phpt diff --git a/NEWS b/NEWS index 3646c48404f81..391593dc0c965 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,10 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? ????, PHP 8.5.0alpha2 +- URI: + . Return the singleton UrlValidationErrorType instances from Uri\WhatWg\Url + instead of creating new objects that are different from the singleton. + (timwolla) 03 Jul 2025, PHP 8.5.0alpha1 diff --git a/ext/uri/php_lexbor.c b/ext/uri/php_lexbor.c index 44bca30f8fda7..39b0fb7d09ce3 100644 --- a/ext/uri/php_lexbor.c +++ b/ext/uri/php_lexbor.c @@ -73,7 +73,7 @@ static void lexbor_cleanup_parser(void) * When errors is NULL, the caller is not interested in the additional error information, * so the function does nothing. */ -static zend_string *fill_errors(zval *errors) +static const char *fill_errors(zval *errors) { if (errors == NULL) { return NULL; @@ -87,140 +87,138 @@ static zend_string *fill_errors(zval *errors) return NULL; } - zend_string *result = NULL; + const char *result = NULL; lexbor_plog_entry_t *lxb_error; while ((lxb_error = lexbor_array_obj_pop(&lexbor_parser.log->list)) != NULL) { zval error; object_init_ex(&error, uri_whatwg_url_validation_error_ce); zend_update_property_string(uri_whatwg_url_validation_error_ce, Z_OBJ(error), ZEND_STRL("context"), (const char *) lxb_error->data); - zend_string *error_str; + const char *error_str; zval failure; switch (lxb_error->id) { case LXB_URL_ERROR_TYPE_DOMAIN_TO_ASCII: - error_str = ZSTR_INIT_LITERAL("DomainToAscii", false); + error_str = "DomainToAscii"; ZVAL_TRUE(&failure); break; case LXB_URL_ERROR_TYPE_DOMAIN_TO_UNICODE: - error_str = ZSTR_INIT_LITERAL("DomainToUnicode", false); + error_str = "DomainToUnicode"; ZVAL_FALSE(&failure); break; case LXB_URL_ERROR_TYPE_DOMAIN_INVALID_CODE_POINT: - error_str = ZSTR_INIT_LITERAL("DomainInvalidCodePoint", false); + error_str = "DomainInvalidCodePoint"; ZVAL_TRUE(&failure); break; case LXB_URL_ERROR_TYPE_HOST_INVALID_CODE_POINT: - error_str = ZSTR_INIT_LITERAL("HostInvalidCodePoint", false); + error_str = "HostInvalidCodePoint"; ZVAL_TRUE(&failure); break; case LXB_URL_ERROR_TYPE_IPV4_EMPTY_PART: - error_str = ZSTR_INIT_LITERAL("Ipv4EmptyPart", false); + error_str = "Ipv4EmptyPart"; ZVAL_FALSE(&failure); break; case LXB_URL_ERROR_TYPE_IPV4_TOO_MANY_PARTS: - error_str = ZSTR_INIT_LITERAL("Ipv4TooManyParts", false); + error_str = "Ipv4TooManyParts"; ZVAL_TRUE(&failure); break; case LXB_URL_ERROR_TYPE_IPV4_NON_NUMERIC_PART: - error_str = ZSTR_INIT_LITERAL("Ipv4NonNumericPart", false); + error_str = "Ipv4NonNumericPart"; ZVAL_TRUE(&failure); break; case LXB_URL_ERROR_TYPE_IPV4_NON_DECIMAL_PART: - error_str = ZSTR_INIT_LITERAL("Ipv4NonDecimalPart", false); + error_str = "Ipv4NonDecimalPart"; ZVAL_FALSE(&failure); break; case LXB_URL_ERROR_TYPE_IPV4_OUT_OF_RANGE_PART: - error_str = ZSTR_INIT_LITERAL("Ipv4OutOfRangePart", false); + error_str = "Ipv4OutOfRangePart"; ZVAL_TRUE(&failure); break; case LXB_URL_ERROR_TYPE_IPV6_UNCLOSED: - error_str = ZSTR_INIT_LITERAL("Ipv6Unclosed", false); + error_str = "Ipv6Unclosed"; ZVAL_TRUE(&failure); break; case LXB_URL_ERROR_TYPE_IPV6_INVALID_COMPRESSION: - error_str = ZSTR_INIT_LITERAL("Ipv6InvalidCompression", false); + error_str = "Ipv6InvalidCompression"; ZVAL_TRUE(&failure); break; case LXB_URL_ERROR_TYPE_IPV6_TOO_MANY_PIECES: - error_str = ZSTR_INIT_LITERAL("Ipv6TooManyPieces", false); + error_str = "Ipv6TooManyPieces"; ZVAL_TRUE(&failure); break; case LXB_URL_ERROR_TYPE_IPV6_MULTIPLE_COMPRESSION: - error_str = ZSTR_INIT_LITERAL("Ipv6MultipleCompression", false); + error_str = "Ipv6MultipleCompression"; ZVAL_TRUE(&failure); break; case LXB_URL_ERROR_TYPE_IPV6_INVALID_CODE_POINT: - error_str = ZSTR_INIT_LITERAL("Ipv6InvalidCodePoint", false); + error_str = "Ipv6InvalidCodePoint"; ZVAL_TRUE(&failure); break; case LXB_URL_ERROR_TYPE_IPV6_TOO_FEW_PIECES: - error_str = ZSTR_INIT_LITERAL("Ipv6TooFewPieces", false); + error_str = "Ipv6TooFewPieces"; ZVAL_TRUE(&failure); break; case LXB_URL_ERROR_TYPE_IPV4_IN_IPV6_TOO_MANY_PIECES: - error_str = ZSTR_INIT_LITERAL("Ipv4InIpv6TooManyPieces", false); + error_str = "Ipv4InIpv6TooManyPieces"; ZVAL_TRUE(&failure); break; case LXB_URL_ERROR_TYPE_IPV4_IN_IPV6_INVALID_CODE_POINT: - error_str = ZSTR_INIT_LITERAL("Ipv4InIpv6InvalidCodePoint", false); + error_str = "Ipv4InIpv6InvalidCodePoint"; ZVAL_TRUE(&failure); break; case LXB_URL_ERROR_TYPE_IPV4_IN_IPV6_OUT_OF_RANGE_PART: - error_str = ZSTR_INIT_LITERAL("Ipv4InIpv6OutOfRangePart", false); + error_str = "Ipv4InIpv6OutOfRangePart"; ZVAL_TRUE(&failure); break; case LXB_URL_ERROR_TYPE_IPV4_IN_IPV6_TOO_FEW_PARTS: - error_str = ZSTR_INIT_LITERAL("Ipv4InIpv6TooFewParts", false); + error_str = "Ipv4InIpv6TooFewParts"; ZVAL_TRUE(&failure); break; case LXB_URL_ERROR_TYPE_INVALID_URL_UNIT: - error_str = ZSTR_INIT_LITERAL("InvalidUrlUnit", false); + error_str = "InvalidUrlUnit"; ZVAL_FALSE(&failure); break; case LXB_URL_ERROR_TYPE_SPECIAL_SCHEME_MISSING_FOLLOWING_SOLIDUS: - error_str = ZSTR_INIT_LITERAL("SpecialSchemeMissingFollowingSolidus", false); + error_str = "SpecialSchemeMissingFollowingSolidus"; ZVAL_FALSE(&failure); break; case LXB_URL_ERROR_TYPE_MISSING_SCHEME_NON_RELATIVE_URL: - error_str = ZSTR_INIT_LITERAL("MissingSchemeNonRelativeUrl", false); + error_str = "MissingSchemeNonRelativeUrl"; ZVAL_TRUE(&failure); break; case LXB_URL_ERROR_TYPE_INVALID_REVERSE_SOLIDUS: - error_str = ZSTR_INIT_LITERAL("InvalidReverseSoldius", false); + error_str = "InvalidReverseSoldius"; ZVAL_FALSE(&failure); break; case LXB_URL_ERROR_TYPE_INVALID_CREDENTIALS: - error_str = ZSTR_INIT_LITERAL("InvalidCredentials", false); + error_str = "InvalidCredentials"; ZVAL_FALSE(&failure); break; case LXB_URL_ERROR_TYPE_HOST_MISSING: - error_str = ZSTR_INIT_LITERAL("HostMissing", false); + error_str = "HostMissing"; ZVAL_TRUE(&failure); break; case LXB_URL_ERROR_TYPE_PORT_OUT_OF_RANGE: - error_str = ZSTR_INIT_LITERAL("PortOutOfRange", false); + error_str = "PortOutOfRange"; ZVAL_TRUE(&failure); break; case LXB_URL_ERROR_TYPE_PORT_INVALID: - error_str = ZSTR_INIT_LITERAL("PortInvalid", false); + error_str = "PortInvalid"; ZVAL_TRUE(&failure); break; case LXB_URL_ERROR_TYPE_FILE_INVALID_WINDOWS_DRIVE_LETTER: - error_str = ZSTR_INIT_LITERAL("FileInvalidWindowsDriveLetter", false); + error_str = "FileInvalidWindowsDriveLetter"; ZVAL_FALSE(&failure); break; case LXB_URL_ERROR_TYPE_FILE_INVALID_WINDOWS_DRIVE_LETTER_HOST: - error_str = ZSTR_INIT_LITERAL("FileInvalidWindowsDriveLetterHost", false); + error_str = "FileInvalidWindowsDriveLetterHost"; ZVAL_FALSE(&failure); break; EMPTY_SWITCH_DEFAULT_CASE() } zval error_type; - zend_enum_new(&error_type, uri_whatwg_url_validation_error_type_ce, error_str, NULL); + ZVAL_OBJ(&error_type, zend_enum_get_case_cstr(uri_whatwg_url_validation_error_type_ce, error_str)); zend_update_property_ex(uri_whatwg_url_validation_error_ce, Z_OBJ(error), ZSTR_KNOWN(ZEND_STR_TYPE), &error_type); - zend_string_release_ex(error_str, false); - zval_ptr_dtor(&error_type); zend_update_property(uri_whatwg_url_validation_error_ce, Z_OBJ(error), ZEND_STRL("failure"), &failure); @@ -236,14 +234,14 @@ static zend_string *fill_errors(zval *errors) static void throw_invalid_url_exception_during_write(zval *errors, const char *component) { - zend_string *reason = fill_errors(errors); + const char *reason = fill_errors(errors); zend_object *exception = zend_throw_exception_ex( uri_whatwg_invalid_url_exception_ce, 0, "The specified %s is malformed%s%s%s", component, reason ? " (" : "", - reason ? ZSTR_VAL(reason) : "", + reason ? reason : "", reason ? ")" : "" ); zend_update_property(exception->ce, exception, ZEND_STRL("errors"), errors); @@ -567,10 +565,10 @@ lxb_url_t *lexbor_parse_uri_ex(const zend_string *uri_str, const lxb_url_t *lexb lexbor_cleanup_parser(); lxb_url_t *url = lxb_url_parse(&lexbor_parser, lexbor_base_url, (unsigned char *) ZSTR_VAL(uri_str), ZSTR_LEN(uri_str)); - zend_string *reason = fill_errors(errors); + const char *reason = fill_errors(errors); if (url == NULL && !silent) { - zend_object *exception = zend_throw_exception_ex(uri_whatwg_invalid_url_exception_ce, 0, "The specified URI is malformed%s%s%s", reason ? " (" : "", reason ? ZSTR_VAL(reason) : "", reason ? ")" : ""); + zend_object *exception = zend_throw_exception_ex(uri_whatwg_invalid_url_exception_ce, 0, "The specified URI is malformed%s%s%s", reason ? " (" : "", reason ? reason : "", reason ? ")" : ""); zend_update_property(exception->ce, exception, ZEND_STRL("errors"), errors); } diff --git a/ext/uri/tests/054.phpt b/ext/uri/tests/054.phpt new file mode 100644 index 0000000000000..562ba981a12a6 --- /dev/null +++ b/ext/uri/tests/054.phpt @@ -0,0 +1,18 @@ +--TEST-- +Test UrlValidationErrorType singleton +--EXTENSIONS-- +uri +--FILE-- +getMessage() . "\n"; + var_dump($e->errors[0]->type === \Uri\WhatWg\UrlValidationErrorType::PortOutOfRange); +} + +?> +--EXPECT-- +The specified URI is malformed (PortOutOfRange) +bool(true) From 1a4dfd56583588181319667d8d98b08e96fb5cab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Wed, 2 Jul 2025 13:58:05 +0200 Subject: [PATCH 225/682] random: Fix error message formatting for `Randomizer::getFloat()` (#19008) Error messages should not end with a `.`. --- ext/random/randomizer.c | 2 +- ext/random/tests/03_randomizer/methods/getFloat_error.phpt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/random/randomizer.c b/ext/random/randomizer.c index 4f63388e8f56a..2b5c98ae03313 100644 --- a/ext/random/randomizer.c +++ b/ext/random/randomizer.c @@ -196,7 +196,7 @@ PHP_METHOD(Random_Randomizer, getFloat) RETVAL_DOUBLE(php_random_gammasection_open_open(randomizer->engine, min, max)); if (UNEXPECTED(isnan(Z_DVAL_P(return_value)))) { - zend_value_error("The given interval is empty, there are no floats between argument #1 ($min) and argument #2 ($max)."); + zend_value_error("The given interval is empty, there are no floats between argument #1 ($min) and argument #2 ($max)"); RETURN_THROWS(); } diff --git a/ext/random/tests/03_randomizer/methods/getFloat_error.phpt b/ext/random/tests/03_randomizer/methods/getFloat_error.phpt index 286435e1752fb..42e933cbefb29 100644 --- a/ext/random/tests/03_randomizer/methods/getFloat_error.phpt +++ b/ext/random/tests/03_randomizer/methods/getFloat_error.phpt @@ -127,4 +127,4 @@ Random\Randomizer::getFloat(): Argument #2 ($max) must be finite Random\Randomizer::getFloat(): Argument #2 ($max) must be greater than argument #1 ($min) Random\Randomizer::getFloat(): Argument #2 ($max) must be greater than argument #1 ($min) Random\Randomizer::getFloat(): Argument #2 ($max) must be greater than argument #1 ($min) -The given interval is empty, there are no floats between argument #1 ($min) and argument #2 ($max). +The given interval is empty, there are no floats between argument #1 ($min) and argument #2 ($max) From 11ea995ff3966ae2c5bf2bceb5a6853ca68401d5 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Tue, 1 Jul 2025 00:44:50 +0200 Subject: [PATCH 226/682] curl: Remove incorrect string release on error The string is owned by the caller, and the caller releases it. Closes GH-18989. --- NEWS | 1 + ext/curl/interface.c | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 259270fdfcf82..22f667e57446e 100644 --- a/NEWS +++ b/NEWS @@ -13,6 +13,7 @@ PHP NEWS - Curl: . Fix memory leaks when returning refcounted value from curl callback. (nielsdos) + . Remove incorrect string release. (nielsdos) - LDAP: . Fixed GH-18902 ldap_exop/ldap_exop_sync assert triggered on empty diff --git a/ext/curl/interface.c b/ext/curl/interface.c index 6c480907b7629..b3139422cffa5 100644 --- a/ext/curl/interface.c +++ b/ext/curl/interface.c @@ -1369,7 +1369,6 @@ static inline CURLcode add_simple_field(struct HttpPost **first, struct HttpPost part = curl_mime_addpart(mime); if (part == NULL) { zend_tmp_string_release(tmp_postval); - zend_string_release_ex(string_key, 0); return CURLE_OUT_OF_MEMORY; } if ((form_error = curl_mime_name(part, ZSTR_VAL(string_key))) != CURLE_OK From 09c223de00af9b312e49db7bbc915aefaca5dbf8 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Tue, 1 Jul 2025 21:10:33 +0200 Subject: [PATCH 227/682] Fix leak when path is too long in ZipArchive::extractTo() I did not find an easy way to trigger this branch without also triggering some other error conditions earlier. Closes GH-19002. --- NEWS | 3 +++ ext/zip/php_zip.c | 1 + 2 files changed, 4 insertions(+) diff --git a/NEWS b/NEWS index 22f667e57446e..5df4c88e972b1 100644 --- a/NEWS +++ b/NEWS @@ -42,6 +42,9 @@ PHP NEWS . Fixed GH-13264 (fgets() and stream_get_line() do not return false on filter fatal error). (Jakub Zelenka) +- Zip: + . Fix leak when path is too long in ZipArchive::extractTo(). (nielsdos) + 03 Jul 2025, PHP 8.3.23 - Core: diff --git a/ext/zip/php_zip.c b/ext/zip/php_zip.c index 62f51ce9f35f3..3710b304c3515 100644 --- a/ext/zip/php_zip.c +++ b/ext/zip/php_zip.c @@ -218,6 +218,7 @@ static int php_zip_extract_file(struct zip * za, char *dest, const char *file, s return 0; } else if (len > MAXPATHLEN) { php_error_docref(NULL, E_WARNING, "Full extraction path exceed MAXPATHLEN (%i)", MAXPATHLEN); + efree(fullpath); efree(file_dirname_fullpath); zend_string_release_ex(file_basename, 0); CWD_STATE_FREE(new_state.cwd); From 69328ba304f2a47e0e9b3ba872db5681982efb96 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Tue, 1 Jul 2025 19:50:52 +0200 Subject: [PATCH 228/682] Fix GH-18990, bug #81029, bug #47314: SOAP HTTP socket not closing on object destruction Currently the resource is attached to the object and its refcount is increased. This means that the refcount to the resource is 2 instead of 1 as expected. A refcount of 2 is necessary in the current code because of how the error handling works: by using convert_to_null() the resource actually goes to rc_dtor_func(), dropping its refcount to 1. So on error the refcount is correct. To solve the issue, let `stream` conceptually be a borrow of the resource with refcount 1, and just use ZVAL_NULL() to prevent calling rc_dtor_func() on the resource. Closes GH-19001. --- NEWS | 4 +++ ext/soap/php_http.c | 21 +++++------- ext/soap/tests/bugs/gh18990.phpt | 58 ++++++++++++++++++++++++++++++++ 3 files changed, 71 insertions(+), 12 deletions(-) create mode 100644 ext/soap/tests/bugs/gh18990.phpt diff --git a/NEWS b/NEWS index 5df4c88e972b1..08be236abbdd0 100644 --- a/NEWS +++ b/NEWS @@ -32,6 +32,10 @@ PHP NEWS . Fixed bug GH-18958 (Fatal error during shutdown after pcntl_rfork() or pcntl_forkx() with zend-max-execution-timers). (Arnaud) +- SOAP: + . Fixed bug GH-18990, bug #81029, bug #47314 (SOAP HTTP socket not closing + on object destruction). (nielsdos) + - Standard: . Fix misleading errors in printf(). (nielsdos) . Fix RCN violations in array functions. (nielsdos) diff --git a/ext/soap/php_http.c b/ext/soap/php_http.c index c908bb4d8ff16..3dfafda4f9573 100644 --- a/ext/soap/php_http.c +++ b/ext/soap/php_http.c @@ -511,9 +511,9 @@ int make_http_soap_request(zval *this_ptr, zend_string_equals(orig->host, phpurl->host) && orig->port == phpurl->port))) { } else { + ZVAL_NULL(Z_CLIENT_HTTPSOCKET_P(this_ptr)); php_stream_close(stream); convert_to_null(Z_CLIENT_HTTPURL_P(this_ptr)); - convert_to_null(Z_CLIENT_HTTPSOCKET_P(this_ptr)); convert_to_null(Z_CLIENT_USE_PROXY_P(this_ptr)); stream = NULL; use_proxy = 0; @@ -522,9 +522,9 @@ int make_http_soap_request(zval *this_ptr, /* Check if keep-alive connection is still opened */ if (stream != NULL && php_stream_eof(stream)) { + ZVAL_NULL(Z_CLIENT_HTTPSOCKET_P(this_ptr)); php_stream_close(stream); convert_to_null(Z_CLIENT_HTTPURL_P(this_ptr)); - convert_to_null(Z_CLIENT_HTTPSOCKET_P(this_ptr)); convert_to_null(Z_CLIENT_USE_PROXY_P(this_ptr)); stream = NULL; use_proxy = 0; @@ -533,9 +533,7 @@ int make_http_soap_request(zval *this_ptr, if (!stream) { stream = http_connect(this_ptr, phpurl, use_ssl, context, &use_proxy); if (stream) { - php_stream_auto_cleanup(stream); - ZVAL_RES(Z_CLIENT_HTTPSOCKET_P(this_ptr), stream->res); - GC_ADDREF(stream->res); + php_stream_to_zval(stream, Z_CLIENT_HTTPSOCKET_P(this_ptr)); ZVAL_LONG(Z_CLIENT_USE_PROXY_P(this_ptr), use_proxy); } else { php_url_free(phpurl); @@ -555,7 +553,6 @@ int make_http_soap_request(zval *this_ptr, zval *cookies, *login, *password; zend_resource *ret = zend_register_resource(phpurl, le_url); ZVAL_RES(Z_CLIENT_HTTPURL_P(this_ptr), ret); - GC_ADDREF(ret); if (context && (tmp = php_stream_context_get_option(context, "http", "protocol_version")) != NULL && @@ -683,9 +680,9 @@ int make_http_soap_request(zval *this_ptr, if (UNEXPECTED(php_random_bytes_throw(&nonce, sizeof(nonce)) != SUCCESS)) { ZEND_ASSERT(EG(exception)); + ZVAL_NULL(Z_CLIENT_HTTPSOCKET_P(this_ptr)); php_stream_close(stream); convert_to_null(Z_CLIENT_HTTPURL_P(this_ptr)); - convert_to_null(Z_CLIENT_HTTPSOCKET_P(this_ptr)); convert_to_null(Z_CLIENT_USE_PROXY_P(this_ptr)); smart_str_free(&soap_headers_z); smart_str_free(&soap_headers); @@ -901,9 +898,9 @@ int make_http_soap_request(zval *this_ptr, if (request != buf) { zend_string_release_ex(request, 0); } + ZVAL_NULL(Z_CLIENT_HTTPSOCKET_P(this_ptr)); php_stream_close(stream); convert_to_null(Z_CLIENT_HTTPURL_P(this_ptr)); - convert_to_null(Z_CLIENT_HTTPSOCKET_P(this_ptr)); convert_to_null(Z_CLIENT_USE_PROXY_P(this_ptr)); add_soap_fault(this_ptr, "HTTP", "Failed Sending HTTP SOAP request", NULL, NULL); smart_str_free(&soap_headers_z); @@ -919,8 +916,8 @@ int make_http_soap_request(zval *this_ptr, } if (!return_value) { + ZVAL_NULL(Z_CLIENT_HTTPSOCKET_P(this_ptr)); php_stream_close(stream); - convert_to_null(Z_CLIENT_HTTPSOCKET_P(this_ptr)); convert_to_null(Z_CLIENT_USE_PROXY_P(this_ptr)); smart_str_free(&soap_headers_z); efree(http_msg); @@ -933,8 +930,8 @@ int make_http_soap_request(zval *this_ptr, if (request != buf) { zend_string_release_ex(request, 0); } + ZVAL_NULL(Z_CLIENT_HTTPSOCKET_P(this_ptr)); php_stream_close(stream); - convert_to_null(Z_CLIENT_HTTPSOCKET_P(this_ptr)); convert_to_null(Z_CLIENT_USE_PROXY_P(this_ptr)); add_soap_fault(this_ptr, "HTTP", "Error Fetching http headers", NULL, NULL); smart_str_free(&soap_headers_z); @@ -1102,9 +1099,9 @@ int make_http_soap_request(zval *this_ptr, if (request != buf) { zend_string_release_ex(request, 0); } + ZVAL_NULL(Z_CLIENT_HTTPSOCKET_P(this_ptr)); php_stream_close(stream); zend_string_release_ex(http_headers, 0); - convert_to_null(Z_CLIENT_HTTPSOCKET_P(this_ptr)); convert_to_null(Z_CLIENT_USE_PROXY_P(this_ptr)); add_soap_fault(this_ptr, "HTTP", "Error Fetching http body, No Content-Length, connection closed or chunked data", NULL, NULL); if (http_msg) { @@ -1119,8 +1116,8 @@ int make_http_soap_request(zval *this_ptr, } if (http_close) { + ZVAL_NULL(Z_CLIENT_HTTPSOCKET_P(this_ptr)); php_stream_close(stream); - convert_to_null(Z_CLIENT_HTTPSOCKET_P(this_ptr)); convert_to_null(Z_CLIENT_USE_PROXY_P(this_ptr)); stream = NULL; } diff --git a/ext/soap/tests/bugs/gh18990.phpt b/ext/soap/tests/bugs/gh18990.phpt new file mode 100644 index 0000000000000..30dbc0fe8b751 --- /dev/null +++ b/ext/soap/tests/bugs/gh18990.phpt @@ -0,0 +1,58 @@ +--TEST-- +GH-18990 (SOAP HTTP socket not closing on object destruction) +--INI-- +soap.wsdl_cache_enabled=0 +--EXTENSIONS-- +soap +--SKIPIF-- + +text0text1text2text3text4text5text6text7text8text9 +EOF; + +$responses = [ + "data://text/plain,HTTP/1.1 200 OK\r\n". + "Content-Type: text/xml;charset=utf-8\r\n". + "Connection: Keep-Alive\r\n". + "Content-Length: ".strlen($wsdl)."\r\n". + "\r\n". + $wsdl, + + "data://text/plain,HTTP/1.1 200 OK\r\n". + "Content-Type: text/xml;charset=utf-8\r\n". + "Connection: Keep-Alive\r\n". + "Content-Length: ".strlen($soap)."\r\n". + "\r\n". + $soap, +]; + +['pid' => $pid, 'uri' => $uri] = http_server($responses); + +$options = [ + 'trace' => false, + 'location' => $uri, +]; + +$cnt = count(get_resources()); + +$client = new SoapClient($uri, $options); + +var_dump(count($client->getItems())); + +http_server_kill($pid); + +unset($client); +var_dump(count(get_resources()) - $cnt); +?> +--EXPECT-- +int(10) +int(0) From 4492a4219ac4086d61e8583206bb15e9e1bb89dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Thu, 3 Jul 2025 08:48:49 +0200 Subject: [PATCH 229/682] random: Remove useless `zend_string` allocation in `randomizer_common_init()` (#19007) --- ext/random/randomizer.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/ext/random/randomizer.c b/ext/random/randomizer.c index 2b5c98ae03313..fc3c93fc2b053 100644 --- a/ext/random/randomizer.c +++ b/ext/random/randomizer.c @@ -43,16 +43,9 @@ static inline void randomizer_common_init(php_random_randomizer *randomizer, zen .state = state, }; - zend_string *mname; - zend_function *generate_method; - - mname = ZSTR_INIT_LITERAL("generate", 0); - generate_method = zend_hash_find_ptr(&engine_object->ce->function_table, mname); - zend_string_release(mname); - /* Create compatible state */ state->object = engine_object; - state->generate_method = generate_method; + state->generate_method = zend_hash_str_find_ptr(&engine_object->ce->function_table, "generate", strlen("generate")); /* Mark self-allocated for memory management */ randomizer->is_userland_algo = true; From f91f80ca19cb0a9d34b3505a0b24cbb0a47ecd90 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Thu, 3 Jul 2025 12:01:11 +0100 Subject: [PATCH 230/682] Zend: Return anonymous closure names in `zend_get_callable_name_ex()` (#19011) This returns the usual `{closure:FILE_NAME/FUNCTION:LINE_NO}` for anonymous functions rather than `Closure::__invoke` this is visible for `is_callable()` and any Engine call that uses `zend_fcall_info_init()` to get the name of the callable. Related to GH-18063. --- Zend/tests/closures/closure_016.phpt | 4 ++-- Zend/zend_API.c | 9 +++------ tests/output/ob_013.phpt | 6 +++--- 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/Zend/tests/closures/closure_016.phpt b/Zend/tests/closures/closure_016.phpt index 0f87f20f435aa..33860189a57ea 100644 --- a/Zend/tests/closures/closure_016.phpt +++ b/Zend/tests/closures/closure_016.phpt @@ -42,9 +42,9 @@ Foo::__invoke bool(true) Foo::__invoke bool(true) -Closure::__invoke +{closure:foo():9} bool(true) -Closure::__invoke +{closure:foo():9} bool(true) Closure::__invoke bool(true) diff --git a/Zend/zend_API.c b/Zend/zend_API.c index df8b4252c42ad..e05422395ec19 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -4160,13 +4160,10 @@ ZEND_API zend_string *zend_get_callable_name_ex(zval *callable, zend_object *obj if (ce == zend_ce_closure) { const zend_function *fn = zend_get_closure_method_def(Z_OBJ_P(callable)); - if (fn->common.fn_flags & ZEND_ACC_FAKE_CLOSURE) { - if (fn->common.scope) { - return zend_create_member_string(fn->common.scope->name, fn->common.function_name); - } else { - return zend_string_copy(fn->common.function_name); - } + if ((fn->common.fn_flags & ZEND_ACC_FAKE_CLOSURE) && fn->common.scope) { + return zend_create_member_string(fn->common.scope->name, fn->common.function_name); } + return zend_string_copy(fn->common.function_name); } return zend_string_concat2( diff --git a/tests/output/ob_013.phpt b/tests/output/ob_013.phpt index b42f443ad28cb..c1f9f090b7b31 100644 --- a/tests/output/ob_013.phpt +++ b/tests/output/ob_013.phpt @@ -57,11 +57,11 @@ Array [5] => E::f [6] => E::g [7] => E::__invoke - [8] => Closure::__invoke + [8] => {closure:%s:%d} ) Array ( - [name] => Closure::__invoke + [name] => {closure:%s:%d} [type] => 1 [flags] => 20593 [level] => 8 @@ -161,7 +161,7 @@ Array [8] => Array ( - [name] => Closure::__invoke + [name] => {closure:%s:%d} [type] => 1 [flags] => 20593 [level] => 8 From c161bb0c18f6012795cc7763ac06733b29ae64b0 Mon Sep 17 00:00:00 2001 From: SakiTakamachi Date: Fri, 27 Jun 2025 20:34:09 +0900 Subject: [PATCH 231/682] Fix GH-18873 - Free column->descid appropriately (#18957) fixes #18873 closes #18957 --- NEWS | 4 ++++ ext/oci8/oci8.c | 8 ++------ ext/oci8/tests/gh18873.phpt | 38 +++++++++++++++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 6 deletions(-) create mode 100644 ext/oci8/tests/gh18873.phpt diff --git a/NEWS b/NEWS index 08be236abbdd0..814c4692f3d6a 100644 --- a/NEWS +++ b/NEWS @@ -22,6 +22,10 @@ PHP NEWS - MbString: . Fixed bug GH-18901 (integer overflow mb_split). (nielsdos) +- OCI8: + . Fixed bug GH-18873 (OCI_RETURN_LOBS flag causes oci8 to leak memory). + (Saki Takamachi) + - Opcache: . Fixed bug GH-18639 (Internal class aliases can break preloading + JIT). (nielsdos) diff --git a/ext/oci8/oci8.c b/ext/oci8/oci8.c index 01cb1c8ad9277..b13843e866665 100644 --- a/ext/oci8/oci8.c +++ b/ext/oci8/oci8.c @@ -573,12 +573,8 @@ void php_oci_column_hash_dtor(zval *data) zend_list_close(column->stmtid); } - if (column->descid) { - if (GC_REFCOUNT(column->descid) == 1) - zend_list_close(column->descid); - else { - GC_DELREF(column->descid); - } + if (column->descid && !GC_DELREF(column->descid)) { + zend_list_free(column->descid); } if (column->data) { diff --git a/ext/oci8/tests/gh18873.phpt b/ext/oci8/tests/gh18873.phpt new file mode 100644 index 0000000000000..acd88facb57c1 --- /dev/null +++ b/ext/oci8/tests/gh18873.phpt @@ -0,0 +1,38 @@ +--TEST-- +GH-18873 (OCI_RETURN_LOBS flag causes oci8 to leak memory) +--EXTENSIONS-- +oci8 +--SKIPIF-- + +--FILE-- + +--EXPECT-- +Done! From 840dc1981f90edca0bbbdace5e19c3118525e75a Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Thu, 3 Jul 2025 15:24:35 +0200 Subject: [PATCH 232/682] fix ldap.h detection without pkgconfig (#19005) --- ext/ldap/config.m4 | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/ext/ldap/config.m4 b/ext/ldap/config.m4 index 7d0229f6868c4..ae0ae7fba9598 100644 --- a/ext/ldap/config.m4 +++ b/ext/ldap/config.m4 @@ -60,15 +60,20 @@ if test "$PHP_LDAP" != "no"; then [-DZEND_ENABLE_STATIC_TSRMLS_CACHE=1]) AS_VAR_IF([PHP_LDAP], [yes], [ - PKG_CHECK_MODULES([LDAP], [lber ldap]) - PHP_LDAP_PKGCONFIG=true - ], [PHP_LDAP_CHECKS([$PHP_LDAP])]) + PKG_CHECK_MODULES([LDAP], [lber ldap], + PHP_LDAP_PKGCONFIG=true, PHP_LDAP_PKGCONFIG=false)]) AS_IF([test "$PHP_LDAP_PKGCONFIG" = true], [ PHP_EVAL_INCLINE([$LDAP_CFLAGS]) PHP_EVAL_LIBLINE([$LDAP_LIBS], [LDAP_SHARED_LIBADD]) ], [ - AS_VAR_IF([LDAP_DIR],, [AC_MSG_ERROR([Cannot find ldap.h])]) + AS_VAR_IF([PHP_LDAP], [yes], [ + for i in /usr/local /usr; do + PHP_LDAP_CHECKS([$i]) + done + ], [PHP_LDAP_CHECKS([$PHP_LDAP])]) + AC_MSG_CHECKING([for ldap.h]) + AS_VAR_IF([LDAP_DIR],, [AC_MSG_ERROR([Cannot find ldap.h])], AC_MSG_RESULT([$LDAP_DIR])) dnl -pc removal is a hack for clang MACHINE_INCLUDES=$($CC -dumpmachine | $SED 's/-pc//') From f61ae0001c75f12104b516c36619836ddf31e62b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Thu, 3 Jul 2025 16:32:10 +0200 Subject: [PATCH 233/682] Zend: `const`ify various parameters in zend_object_handlers and zend_lazy_objects (#19019) --- Zend/zend_lazy_objects.c | 16 ++++++++-------- Zend/zend_lazy_objects.h | 18 +++++++++--------- Zend/zend_object_handlers.c | 10 +++++----- Zend/zend_object_handlers.h | 6 +++--- 4 files changed, 25 insertions(+), 25 deletions(-) diff --git a/Zend/zend_lazy_objects.c b/Zend/zend_lazy_objects.c index d1b950160e1cc..cf00804eda33b 100644 --- a/Zend/zend_lazy_objects.c +++ b/Zend/zend_lazy_objects.c @@ -93,7 +93,7 @@ void zend_lazy_objects_destroy(zend_lazy_objects_store *store) zend_hash_destroy(&store->infos); } -static void zend_lazy_object_set_info(zend_object *obj, zend_lazy_object_info *info) +static void zend_lazy_object_set_info(const zend_object *obj, zend_lazy_object_info *info) { ZEND_ASSERT(zend_object_is_lazy(obj)); @@ -102,7 +102,7 @@ static void zend_lazy_object_set_info(zend_object *obj, zend_lazy_object_info *i (void)zv; } -static zend_lazy_object_info* zend_lazy_object_get_info(zend_object *obj) +static zend_lazy_object_info* zend_lazy_object_get_info(const zend_object *obj) { ZEND_ASSERT(zend_object_is_lazy(obj)); @@ -112,7 +112,7 @@ static zend_lazy_object_info* zend_lazy_object_get_info(zend_object *obj) return info; } -static bool zend_lazy_object_has_stale_info(zend_object *obj) +static bool zend_lazy_object_has_stale_info(const zend_object *obj) { return zend_hash_index_find_ptr(&EG(lazy_objects_store).infos, obj->handle); } @@ -154,18 +154,18 @@ zend_object* zend_lazy_object_get_instance(zend_object *obj) return obj; } -zend_lazy_object_flags_t zend_lazy_object_get_flags(zend_object *obj) +zend_lazy_object_flags_t zend_lazy_object_get_flags(const zend_object *obj) { return zend_lazy_object_get_info(obj)->flags; } -void zend_lazy_object_del_info(zend_object *obj) +void zend_lazy_object_del_info(const zend_object *obj) { zend_result res = zend_hash_index_del(&EG(lazy_objects_store).infos, obj->handle); ZEND_ASSERT(res == SUCCESS); } -bool zend_lazy_object_decr_lazy_props(zend_object *obj) +bool zend_lazy_object_decr_lazy_props(const zend_object *obj) { ZEND_ASSERT(zend_object_is_lazy(obj)); ZEND_ASSERT(!zend_lazy_object_initialized(obj)); @@ -183,7 +183,7 @@ bool zend_lazy_object_decr_lazy_props(zend_object *obj) * Making objects lazy */ -ZEND_API bool zend_class_can_be_lazy(zend_class_entry *ce) +ZEND_API bool zend_class_can_be_lazy(const zend_class_entry *ce) { /* Internal classes are not supported */ if (UNEXPECTED(ce->type == ZEND_INTERNAL_CLASS && ce != zend_standard_class_def)) { @@ -444,7 +444,7 @@ static void zend_lazy_object_revert_init(zend_object *obj, zval *properties_tabl OBJ_EXTRA_FLAGS(obj) |= IS_OBJ_LAZY_UNINITIALIZED; } -static bool zend_lazy_object_compatible(zend_object *real_object, zend_object *lazy_object) +static bool zend_lazy_object_compatible(const zend_object *real_object, const zend_object *lazy_object) { if (EXPECTED(real_object->ce == lazy_object->ce)) { return true; diff --git a/Zend/zend_lazy_objects.h b/Zend/zend_lazy_objects.h index 64f68d66360cd..fc0a908e7ad2f 100644 --- a/Zend/zend_lazy_objects.h +++ b/Zend/zend_lazy_objects.h @@ -57,7 +57,7 @@ typedef struct _zend_property_info zend_property_info; typedef struct _zend_fcall_info zend_fcall_info; typedef struct _zend_fcall_info_cache zend_fcall_info_cache; -ZEND_API bool zend_class_can_be_lazy(zend_class_entry *ce); +ZEND_API bool zend_class_can_be_lazy(const zend_class_entry *ce); ZEND_API zend_object *zend_object_make_lazy(zend_object *obj, zend_class_entry *class_type, zval *initializer_zv, zend_fcall_info_cache *initializer_fcc, zend_lazy_object_flags_t flags); @@ -68,39 +68,39 @@ void zend_lazy_objects_init(zend_lazy_objects_store *store); void zend_lazy_objects_destroy(zend_lazy_objects_store *store); zval* zend_lazy_object_get_initializer_zv(zend_object *obj); zend_object *zend_lazy_object_get_instance(zend_object *obj); -zend_lazy_object_flags_t zend_lazy_object_get_flags(zend_object *obj); -void zend_lazy_object_del_info(zend_object *obj); +zend_lazy_object_flags_t zend_lazy_object_get_flags(const zend_object *obj); +void zend_lazy_object_del_info(const zend_object *obj); ZEND_API HashTable *zend_lazy_object_get_properties(zend_object *object); zend_object *zend_lazy_object_clone(zend_object *old_obj); HashTable *zend_lazy_object_debug_info(zend_object *object, int *is_temp); HashTable *zend_lazy_object_get_gc(zend_object *zobj, zval **table, int *n); -bool zend_lazy_object_decr_lazy_props(zend_object *obj); +bool zend_lazy_object_decr_lazy_props(const zend_object *obj); void zend_lazy_object_realize(zend_object *obj); ZEND_API zend_property_info *zend_lazy_object_get_property_info_for_slot(zend_object *obj, zval *slot); -static zend_always_inline bool zend_object_is_lazy(zend_object *obj) +static zend_always_inline bool zend_object_is_lazy(const zend_object *obj) { return (OBJ_EXTRA_FLAGS(obj) & (IS_OBJ_LAZY_UNINITIALIZED | IS_OBJ_LAZY_PROXY)); } -static zend_always_inline bool zend_object_is_lazy_proxy(zend_object *obj) +static zend_always_inline bool zend_object_is_lazy_proxy(const zend_object *obj) { return (OBJ_EXTRA_FLAGS(obj) & IS_OBJ_LAZY_PROXY); } -static zend_always_inline bool zend_lazy_object_initialized(zend_object *obj) +static zend_always_inline bool zend_lazy_object_initialized(const zend_object *obj) { return !(OBJ_EXTRA_FLAGS(obj) & IS_OBJ_LAZY_UNINITIALIZED); } /* True if accessing a lazy prop on obj mandates a call to * zend_lazy_object_init() */ -static zend_always_inline bool zend_lazy_object_must_init(zend_object *obj) +static zend_always_inline bool zend_lazy_object_must_init(const zend_object *obj) { return zend_object_is_lazy(obj); } -static inline bool zend_lazy_object_initialize_on_serialize(zend_object *obj) +static inline bool zend_lazy_object_initialize_on_serialize(const zend_object *obj) { return !(zend_lazy_object_get_flags(obj) & ZEND_LAZY_OBJECT_SKIP_INITIALIZATION_ON_SERIALIZE); } diff --git a/Zend/zend_object_handlers.c b/Zend/zend_object_handlers.c index 3d782b03fe174..971df5a7f232a 100644 --- a/Zend/zend_object_handlers.c +++ b/Zend/zend_object_handlers.c @@ -289,7 +289,7 @@ static zend_never_inline int is_protected_compatible_scope(const zend_class_entr } /* }}} */ -static zend_never_inline zend_property_info *zend_get_parent_private_property(zend_class_entry *scope, const zend_class_entry *ce, zend_string *member) /* {{{ */ +static zend_never_inline zend_property_info *zend_get_parent_private_property(const zend_class_entry *scope, const zend_class_entry *ce, zend_string *member) /* {{{ */ { zval *zv; zend_property_info *prop_info; @@ -1823,7 +1823,7 @@ static zend_always_inline zend_function *zend_get_user_call_function(zend_class_ } /* }}} */ -ZEND_API ZEND_COLD zend_never_inline void zend_bad_method_call(zend_function *fbc, zend_string *method_name, zend_class_entry *scope) /* {{{ */ +ZEND_API ZEND_COLD zend_never_inline void zend_bad_method_call(const zend_function *fbc, const zend_string *method_name, const zend_class_entry *scope) /* {{{ */ { zend_throw_error(NULL, "Call to %s method %s::%s() from %s%s", zend_visibility_string(fbc->common.fn_flags), ZEND_FN_SCOPE_NAME(fbc), ZSTR_VAL(method_name), @@ -1833,7 +1833,7 @@ ZEND_API ZEND_COLD zend_never_inline void zend_bad_method_call(zend_function *fb } /* }}} */ -ZEND_API ZEND_COLD zend_never_inline void zend_abstract_method_call(zend_function *fbc) /* {{{ */ +ZEND_API ZEND_COLD zend_never_inline void zend_abstract_method_call(const zend_function *fbc) /* {{{ */ { zend_throw_error(NULL, "Cannot call abstract method %s::%s()", ZSTR_VAL(fbc->common.scope->name), ZSTR_VAL(fbc->common.function_name)); @@ -2090,14 +2090,14 @@ ZEND_API zval *zend_std_get_static_property(zend_class_entry *ce, zend_string *p return zend_std_get_static_property_with_info(ce, property_name, type, &prop_info); } -ZEND_API ZEND_COLD bool zend_std_unset_static_property(zend_class_entry *ce, zend_string *property_name) /* {{{ */ +ZEND_API ZEND_COLD bool zend_std_unset_static_property(const zend_class_entry *ce, const zend_string *property_name) /* {{{ */ { zend_throw_error(NULL, "Attempt to unset static property %s::$%s", ZSTR_VAL(ce->name), ZSTR_VAL(property_name)); return 0; } /* }}} */ -static ZEND_COLD zend_never_inline void zend_bad_constructor_call(zend_function *constructor, zend_class_entry *scope) /* {{{ */ +static ZEND_COLD zend_never_inline void zend_bad_constructor_call(const zend_function *constructor, const zend_class_entry *scope) /* {{{ */ { if (scope) { zend_throw_error(NULL, "Call to %s %s::%s() from scope %s", diff --git a/Zend/zend_object_handlers.h b/Zend/zend_object_handlers.h index 7e7d3df37a6ad..fb87695a2ed25 100644 --- a/Zend/zend_object_handlers.h +++ b/Zend/zend_object_handlers.h @@ -249,7 +249,7 @@ ZEND_API void zend_class_init_statics(zend_class_entry *ce); ZEND_API zend_function *zend_std_get_static_method(zend_class_entry *ce, zend_string *function_name_strval, const zval *key); ZEND_API zval *zend_std_get_static_property_with_info(zend_class_entry *ce, zend_string *property_name, int type, struct _zend_property_info **prop_info); ZEND_API zval *zend_std_get_static_property(zend_class_entry *ce, zend_string *property_name, int type); -ZEND_API ZEND_COLD bool zend_std_unset_static_property(zend_class_entry *ce, zend_string *property_name); +ZEND_API ZEND_COLD bool zend_std_unset_static_property(const zend_class_entry *ce, const zend_string *property_name); ZEND_API zend_function *zend_std_get_constructor(zend_object *object); ZEND_API struct _zend_property_info *zend_get_property_info(const zend_class_entry *ce, zend_string *member, int silent); ZEND_API HashTable *zend_std_get_properties(zend_object *object); @@ -272,8 +272,8 @@ ZEND_API int zend_std_compare_objects(zval *o1, zval *o2); ZEND_API zend_result zend_std_get_closure(zend_object *obj, zend_class_entry **ce_ptr, zend_function **fptr_ptr, zend_object **obj_ptr, bool check_only); /* Use zend_std_get_properties_ex() */ ZEND_API HashTable *rebuild_object_properties_internal(zend_object *zobj); -ZEND_API ZEND_COLD zend_never_inline void zend_bad_method_call(zend_function *fbc, zend_string *method_name, zend_class_entry *scope); -ZEND_API ZEND_COLD zend_never_inline void zend_abstract_method_call(zend_function *fbc); +ZEND_API ZEND_COLD zend_never_inline void zend_bad_method_call(const zend_function *fbc, const zend_string *method_name, const zend_class_entry *scope); +ZEND_API ZEND_COLD zend_never_inline void zend_abstract_method_call(const zend_function *fbc); static zend_always_inline HashTable *zend_std_get_properties_ex(zend_object *object) { From b6660634b4ff951d1ca98d02381853645faa57af Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Thu, 15 Feb 2024 10:24:41 +0100 Subject: [PATCH 234/682] Disable JIT on Apple Silicon + ZTS Apple Silicon has stricter rules about rwx mmap regions. They need to be created using the MAP_JIT flag. However, the MAP_JIT seems to be incompatible with MAP_SHARED. ZTS requires MAP_SHARED so that some threads may execute code from a page while another writes/appends to it. We did not find another solution, other than completely disabling JIT for Apple Silicon + ZTS. See discussion in https://github.com/php/php-src/pull/13351. Co-authored-by: Peter Kokot Fixes GH-13400 Closes GH-13396 --- ext/opcache/config.m4 | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ext/opcache/config.m4 b/ext/opcache/config.m4 index 0b923206282c4..d35efbc689ed9 100644 --- a/ext/opcache/config.m4 +++ b/ext/opcache/config.m4 @@ -36,6 +36,10 @@ if test "$PHP_OPCACHE" != "no"; then PHP_OPCACHE_JIT=no ;; esac + if test "$host_vendor" = "apple" && test "$host_cpu" = "aarch64" && test "$PHP_THREAD_SAFETY" = "yes"; then + AC_MSG_WARN([JIT not supported on Apple Silicon with ZTS]) + PHP_OPCACHE_JIT=no + fi fi if test "$PHP_OPCACHE_JIT" = "yes"; then From 32f0d24e1fbefb825b02ba304dabad5f1432be6f Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Wed, 2 Jul 2025 22:35:38 +0200 Subject: [PATCH 235/682] soap: Get decompression function directly from function table and call it The code is already looking up the entry in the function table anyway, so might as well use it directly. This simplifies the code and avoids a redundant lookup. --- ext/soap/php_http.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/ext/soap/php_http.c b/ext/soap/php_http.c index 69eca93268a84..088d6605b3cdd 100644 --- a/ext/soap/php_http.c +++ b/ext/soap/php_http.c @@ -1294,20 +1294,18 @@ int make_http_soap_request(zval *this_ptr, /* Decompress response */ content_encoding = get_http_header_value(ZSTR_VAL(http_headers), "Content-Encoding:"); if (content_encoding) { - zval func; zval retval; zval params[1]; + zend_function *decompression_fn; /* Warning: the zlib function names are chosen in an unfortunate manner. * Check zlib.c to see how a function corresponds with a particular format. */ if ((strcmp(content_encoding,"gzip") == 0 || strcmp(content_encoding,"x-gzip") == 0) && - zend_hash_str_exists(EG(function_table), "gzdecode", sizeof("gzdecode")-1)) { - ZVAL_STRING(&func, "gzdecode"); + (decompression_fn = zend_hash_str_find_ptr(EG(function_table), "gzdecode", sizeof("gzdecode")-1))) { ZVAL_STR_COPY(¶ms[0], http_body); } else if (strcmp(content_encoding,"deflate") == 0 && - zend_hash_str_exists(EG(function_table), "gzuncompress", sizeof("gzuncompress")-1)) { - ZVAL_STRING(&func, "gzuncompress"); + (decompression_fn = zend_hash_str_find_ptr(EG(function_table), "gzuncompress", sizeof("gzuncompress")-1))) { ZVAL_STR_COPY(¶ms[0], http_body); } else { efree(content_encoding); @@ -1319,15 +1317,13 @@ int make_http_soap_request(zval *this_ptr, add_soap_fault(this_ptr, "HTTP", "Unknown Content-Encoding", NULL, NULL, SOAP_GLOBAL(lang_en)); return FALSE; } - if (call_user_function(CG(function_table), (zval*)NULL, &func, &retval, 1, params) == SUCCESS && - Z_TYPE(retval) == IS_STRING) { + zend_call_known_function(decompression_fn, NULL, NULL, &retval, 1, params, NULL); + if (Z_TYPE(retval) == IS_STRING) { zval_ptr_dtor(¶ms[0]); - zval_ptr_dtor(&func); zend_string_release_ex(http_body, 0); ZVAL_COPY_VALUE(return_value, &retval); } else { zval_ptr_dtor(¶ms[0]); - zval_ptr_dtor(&func); zval_ptr_dtor(&retval); efree(content_encoding); zend_string_release_ex(http_headers, 0); From e6e088700589b64ea6537f09a7ec217cd37e7d8e Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Wed, 2 Jul 2025 22:38:08 +0200 Subject: [PATCH 236/682] soap: Avoid redundant copying of http body string --- ext/soap/php_http.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/ext/soap/php_http.c b/ext/soap/php_http.c index 088d6605b3cdd..c9a4e48435932 100644 --- a/ext/soap/php_http.c +++ b/ext/soap/php_http.c @@ -1303,10 +1303,10 @@ int make_http_soap_request(zval *this_ptr, if ((strcmp(content_encoding,"gzip") == 0 || strcmp(content_encoding,"x-gzip") == 0) && (decompression_fn = zend_hash_str_find_ptr(EG(function_table), "gzdecode", sizeof("gzdecode")-1))) { - ZVAL_STR_COPY(¶ms[0], http_body); + ZVAL_STR(¶ms[0], http_body); } else if (strcmp(content_encoding,"deflate") == 0 && (decompression_fn = zend_hash_str_find_ptr(EG(function_table), "gzuncompress", sizeof("gzuncompress")-1))) { - ZVAL_STR_COPY(¶ms[0], http_body); + ZVAL_STR(¶ms[0], http_body); } else { efree(content_encoding); zend_string_release_ex(http_headers, 0); @@ -1319,11 +1319,9 @@ int make_http_soap_request(zval *this_ptr, } zend_call_known_function(decompression_fn, NULL, NULL, &retval, 1, params, NULL); if (Z_TYPE(retval) == IS_STRING) { - zval_ptr_dtor(¶ms[0]); zend_string_release_ex(http_body, 0); ZVAL_COPY_VALUE(return_value, &retval); } else { - zval_ptr_dtor(¶ms[0]); zval_ptr_dtor(&retval); efree(content_encoding); zend_string_release_ex(http_headers, 0); From 8fdd434bb5cafff4da6b5539930b6c8d883133a2 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Wed, 2 Jul 2025 22:19:38 +0200 Subject: [PATCH 237/682] Don't deref soap private properties They are private and can't be made references. --- ext/soap/php_soap.h | 73 +++++++++++++++++++++++---------------------- 1 file changed, 37 insertions(+), 36 deletions(-) diff --git a/ext/soap/php_soap.h b/ext/soap/php_soap.h index 98e3d4af6f19d..1eea30c62e905 100644 --- a/ext/soap/php_soap.h +++ b/ext/soap/php_soap.h @@ -216,42 +216,43 @@ static zend_always_inline zval *php_soap_deref(zval *zv) { return zv; } -#define Z_CLIENT_URI_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 0)) -#define Z_CLIENT_STYLE_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 1)) -#define Z_CLIENT_USE_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 2)) -#define Z_CLIENT_LOCATION_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 3)) -#define Z_CLIENT_TRACE_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 4)) -#define Z_CLIENT_COMPRESSION_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 5)) -#define Z_CLIENT_SDL_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 6)) -#define Z_CLIENT_TYPEMAP_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 7)) -#define Z_CLIENT_HTTPSOCKET_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 8)) -#define Z_CLIENT_HTTPURL_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 9)) -#define Z_CLIENT_LOGIN_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 10)) -#define Z_CLIENT_PASSWORD_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 11)) -#define Z_CLIENT_USE_DIGEST_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 12)) -#define Z_CLIENT_DIGEST_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 13)) -#define Z_CLIENT_PROXY_HOST_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 14)) -#define Z_CLIENT_PROXY_PORT_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 15)) -#define Z_CLIENT_PROXY_LOGIN_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 16)) -#define Z_CLIENT_PROXY_PASSWORD_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 17)) -#define Z_CLIENT_EXCEPTIONS_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 18)) -#define Z_CLIENT_ENCODING_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 19)) -#define Z_CLIENT_CLASSMAP_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 20)) -#define Z_CLIENT_FEATURES_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 21)) -#define Z_CLIENT_CONNECTION_TIMEOUT_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 22)) -#define Z_CLIENT_STREAM_CONTEXT_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 23)) -#define Z_CLIENT_USER_AGENT_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 24)) -#define Z_CLIENT_KEEP_ALIVE_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 25)) -#define Z_CLIENT_SSL_METHOD_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 26)) -#define Z_CLIENT_SOAP_VERSION_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 27)) -#define Z_CLIENT_USE_PROXY_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 28)) -#define Z_CLIENT_COOKIES_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 29)) -#define Z_CLIENT_DEFAULT_HEADERS_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 30)) -#define Z_CLIENT_SOAP_FAULT_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 31)) -#define Z_CLIENT_LAST_REQUEST_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 32)) -#define Z_CLIENT_LAST_RESPONSE_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 33)) -#define Z_CLIENT_LAST_REQUEST_HEADERS_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 34)) -#define Z_CLIENT_LAST_RESPONSE_HEADERS_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 35)) +/* SoapClient's properties are all private and can't be references */ +#define Z_CLIENT_URI_P(zv) OBJ_PROP_NUM(Z_OBJ_P(zv), 0) +#define Z_CLIENT_STYLE_P(zv) OBJ_PROP_NUM(Z_OBJ_P(zv), 1) +#define Z_CLIENT_USE_P(zv) OBJ_PROP_NUM(Z_OBJ_P(zv), 2) +#define Z_CLIENT_LOCATION_P(zv) OBJ_PROP_NUM(Z_OBJ_P(zv), 3) +#define Z_CLIENT_TRACE_P(zv) OBJ_PROP_NUM(Z_OBJ_P(zv), 4) +#define Z_CLIENT_COMPRESSION_P(zv) OBJ_PROP_NUM(Z_OBJ_P(zv), 5) +#define Z_CLIENT_SDL_P(zv) OBJ_PROP_NUM(Z_OBJ_P(zv), 6) +#define Z_CLIENT_TYPEMAP_P(zv) OBJ_PROP_NUM(Z_OBJ_P(zv), 7) +#define Z_CLIENT_HTTPSOCKET_P(zv) OBJ_PROP_NUM(Z_OBJ_P(zv), 8) +#define Z_CLIENT_HTTPURL_P(zv) OBJ_PROP_NUM(Z_OBJ_P(zv), 9) +#define Z_CLIENT_LOGIN_P(zv) OBJ_PROP_NUM(Z_OBJ_P(zv), 10) +#define Z_CLIENT_PASSWORD_P(zv) OBJ_PROP_NUM(Z_OBJ_P(zv), 11) +#define Z_CLIENT_USE_DIGEST_P(zv) OBJ_PROP_NUM(Z_OBJ_P(zv), 12) +#define Z_CLIENT_DIGEST_P(zv) OBJ_PROP_NUM(Z_OBJ_P(zv), 13) +#define Z_CLIENT_PROXY_HOST_P(zv) OBJ_PROP_NUM(Z_OBJ_P(zv), 14) +#define Z_CLIENT_PROXY_PORT_P(zv) OBJ_PROP_NUM(Z_OBJ_P(zv), 15) +#define Z_CLIENT_PROXY_LOGIN_P(zv) OBJ_PROP_NUM(Z_OBJ_P(zv), 16) +#define Z_CLIENT_PROXY_PASSWORD_P(zv) OBJ_PROP_NUM(Z_OBJ_P(zv), 17) +#define Z_CLIENT_EXCEPTIONS_P(zv) OBJ_PROP_NUM(Z_OBJ_P(zv), 18) +#define Z_CLIENT_ENCODING_P(zv) OBJ_PROP_NUM(Z_OBJ_P(zv), 19) +#define Z_CLIENT_CLASSMAP_P(zv) OBJ_PROP_NUM(Z_OBJ_P(zv), 20) +#define Z_CLIENT_FEATURES_P(zv) OBJ_PROP_NUM(Z_OBJ_P(zv), 21) +#define Z_CLIENT_CONNECTION_TIMEOUT_P(zv) OBJ_PROP_NUM(Z_OBJ_P(zv), 22) +#define Z_CLIENT_STREAM_CONTEXT_P(zv) OBJ_PROP_NUM(Z_OBJ_P(zv), 23) +#define Z_CLIENT_USER_AGENT_P(zv) OBJ_PROP_NUM(Z_OBJ_P(zv), 24) +#define Z_CLIENT_KEEP_ALIVE_P(zv) OBJ_PROP_NUM(Z_OBJ_P(zv), 25) +#define Z_CLIENT_SSL_METHOD_P(zv) OBJ_PROP_NUM(Z_OBJ_P(zv), 26) +#define Z_CLIENT_SOAP_VERSION_P(zv) OBJ_PROP_NUM(Z_OBJ_P(zv), 27) +#define Z_CLIENT_USE_PROXY_P(zv) OBJ_PROP_NUM(Z_OBJ_P(zv), 28) +#define Z_CLIENT_COOKIES_P(zv) OBJ_PROP_NUM(Z_OBJ_P(zv), 29) +#define Z_CLIENT_DEFAULT_HEADERS_P(zv) OBJ_PROP_NUM(Z_OBJ_P(zv), 30) +#define Z_CLIENT_SOAP_FAULT_P(zv) OBJ_PROP_NUM(Z_OBJ_P(zv), 31) +#define Z_CLIENT_LAST_REQUEST_P(zv) OBJ_PROP_NUM(Z_OBJ_P(zv), 32) +#define Z_CLIENT_LAST_RESPONSE_P(zv) OBJ_PROP_NUM(Z_OBJ_P(zv), 33) +#define Z_CLIENT_LAST_REQUEST_HEADERS_P(zv) OBJ_PROP_NUM(Z_OBJ_P(zv), 34) +#define Z_CLIENT_LAST_RESPONSE_HEADERS_P(zv) OBJ_PROP_NUM(Z_OBJ_P(zv), 35) typedef struct soap_url_object { php_url *url; From aa0e8bf568f9ded224c83942f2a8a89c73630917 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Wed, 2 Jul 2025 22:22:01 +0200 Subject: [PATCH 238/682] Use ZVAL_NULL() directly for Z_CLIENT_USE_PROXY_P() This is just a `?int` property, no need to do anything fancy. --- ext/soap/php_http.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/ext/soap/php_http.c b/ext/soap/php_http.c index c9a4e48435932..2d6ed207efbdd 100644 --- a/ext/soap/php_http.c +++ b/ext/soap/php_http.c @@ -509,7 +509,7 @@ int make_http_soap_request(zval *this_ptr, ZVAL_NULL(Z_CLIENT_HTTPSOCKET_P(this_ptr)); php_stream_close(stream); convert_to_null(Z_CLIENT_HTTPURL_P(this_ptr)); - convert_to_null(Z_CLIENT_USE_PROXY_P(this_ptr)); + ZVAL_NULL(Z_CLIENT_USE_PROXY_P(this_ptr)); stream = NULL; use_proxy = 0; } @@ -520,7 +520,7 @@ int make_http_soap_request(zval *this_ptr, ZVAL_NULL(Z_CLIENT_HTTPSOCKET_P(this_ptr)); php_stream_close(stream); convert_to_null(Z_CLIENT_HTTPURL_P(this_ptr)); - convert_to_null(Z_CLIENT_USE_PROXY_P(this_ptr)); + ZVAL_NULL(Z_CLIENT_USE_PROXY_P(this_ptr)); stream = NULL; use_proxy = 0; } @@ -687,7 +687,7 @@ int make_http_soap_request(zval *this_ptr, ZVAL_NULL(Z_CLIENT_HTTPSOCKET_P(this_ptr)); php_stream_close(stream); convert_to_null(Z_CLIENT_HTTPURL_P(this_ptr)); - convert_to_null(Z_CLIENT_USE_PROXY_P(this_ptr)); + ZVAL_NULL(Z_CLIENT_USE_PROXY_P(this_ptr)); smart_str_free(&soap_headers_z); smart_str_free(&soap_headers); efree(http_msg); @@ -905,7 +905,7 @@ int make_http_soap_request(zval *this_ptr, ZVAL_NULL(Z_CLIENT_HTTPSOCKET_P(this_ptr)); php_stream_close(stream); convert_to_null(Z_CLIENT_HTTPURL_P(this_ptr)); - convert_to_null(Z_CLIENT_USE_PROXY_P(this_ptr)); + ZVAL_NULL(Z_CLIENT_USE_PROXY_P(this_ptr)); add_soap_fault(this_ptr, "HTTP", "Failed Sending HTTP SOAP request", NULL, NULL, SOAP_GLOBAL(lang_en)); smart_str_free(&soap_headers_z); efree(http_msg); @@ -929,7 +929,7 @@ int make_http_soap_request(zval *this_ptr, } ZVAL_NULL(Z_CLIENT_HTTPSOCKET_P(this_ptr)); php_stream_close(stream); - convert_to_null(Z_CLIENT_USE_PROXY_P(this_ptr)); + ZVAL_NULL(Z_CLIENT_USE_PROXY_P(this_ptr)); add_soap_fault(this_ptr, "HTTP", "Error Fetching http headers", NULL, NULL, SOAP_GLOBAL(lang_en)); smart_str_free(&soap_headers_z); efree(http_msg); @@ -985,7 +985,7 @@ int make_http_soap_request(zval *this_ptr, if (http_headers) { zend_string_release_ex(http_headers, 0); } - convert_to_null(Z_CLIENT_USE_PROXY_P(this_ptr)); + ZVAL_NULL(Z_CLIENT_USE_PROXY_P(this_ptr)); if (http_msg) { efree(http_msg); } @@ -1118,7 +1118,7 @@ int make_http_soap_request(zval *this_ptr, ZVAL_NULL(Z_CLIENT_HTTPSOCKET_P(this_ptr)); php_stream_close(stream); zend_string_release_ex(http_headers, 0); - convert_to_null(Z_CLIENT_USE_PROXY_P(this_ptr)); + ZVAL_NULL(Z_CLIENT_USE_PROXY_P(this_ptr)); add_soap_fault(this_ptr, "HTTP", "Error Fetching http body, No Content-Length, connection closed or chunked data", NULL, NULL, SOAP_GLOBAL(lang_en)); if (http_msg) { efree(http_msg); @@ -1134,7 +1134,7 @@ int make_http_soap_request(zval *this_ptr, if (http_close) { ZVAL_NULL(Z_CLIENT_HTTPSOCKET_P(this_ptr)); php_stream_close(stream); - convert_to_null(Z_CLIENT_USE_PROXY_P(this_ptr)); + ZVAL_NULL(Z_CLIENT_USE_PROXY_P(this_ptr)); stream = NULL; } From faef0042501897b246aac514eb8538c680a3107c Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Thu, 3 Jul 2025 15:19:42 +0100 Subject: [PATCH 239/682] ext/spl: Add tests for disabled sort functions --- ext/spl/tests/ArrayObject/asort_disabled.phpt | 18 ++++++++++++++++++ ext/spl/tests/ArrayObject/ksort_disabled.phpt | 18 ++++++++++++++++++ .../ArrayObject/natcasesort_disabled.phpt | 18 ++++++++++++++++++ .../tests/ArrayObject/natsort_disabled.phpt | 18 ++++++++++++++++++ ext/spl/tests/ArrayObject/uasort_disabled.phpt | 18 ++++++++++++++++++ ext/spl/tests/ArrayObject/uksort_disabled.phpt | 18 ++++++++++++++++++ 6 files changed, 108 insertions(+) create mode 100644 ext/spl/tests/ArrayObject/asort_disabled.phpt create mode 100644 ext/spl/tests/ArrayObject/ksort_disabled.phpt create mode 100644 ext/spl/tests/ArrayObject/natcasesort_disabled.phpt create mode 100644 ext/spl/tests/ArrayObject/natsort_disabled.phpt create mode 100644 ext/spl/tests/ArrayObject/uasort_disabled.phpt create mode 100644 ext/spl/tests/ArrayObject/uksort_disabled.phpt diff --git a/ext/spl/tests/ArrayObject/asort_disabled.phpt b/ext/spl/tests/ArrayObject/asort_disabled.phpt new file mode 100644 index 0000000000000..57fb742acbddb --- /dev/null +++ b/ext/spl/tests/ArrayObject/asort_disabled.phpt @@ -0,0 +1,18 @@ +--TEST-- +ArrayObject when function asort() is disabled +--INI-- +disable_functions=asort +--FILE-- +asort(); +var_dump($ao); + +?> +--EXPECTF-- +Fatal error: Uncaught Error: Invalid callback asort, function "asort" not found or invalid function name in %s:%d +Stack trace: +#0 %s(%d): ArrayObject->asort() +#1 {main} + thrown in %s on line %d diff --git a/ext/spl/tests/ArrayObject/ksort_disabled.phpt b/ext/spl/tests/ArrayObject/ksort_disabled.phpt new file mode 100644 index 0000000000000..4299c282189f1 --- /dev/null +++ b/ext/spl/tests/ArrayObject/ksort_disabled.phpt @@ -0,0 +1,18 @@ +--TEST-- +ArrayObject when function ksort() is disabled +--INI-- +disable_functions=ksort +--FILE-- +ksort(); +var_dump($ao); + +?> +--EXPECTF-- +Fatal error: Uncaught Error: Invalid callback ksort, function "ksort" not found or invalid function name in %s:%d +Stack trace: +#0 %s(%d): ArrayObject->ksort() +#1 {main} + thrown in %s on line %d diff --git a/ext/spl/tests/ArrayObject/natcasesort_disabled.phpt b/ext/spl/tests/ArrayObject/natcasesort_disabled.phpt new file mode 100644 index 0000000000000..def9af50e3561 --- /dev/null +++ b/ext/spl/tests/ArrayObject/natcasesort_disabled.phpt @@ -0,0 +1,18 @@ +--TEST-- +ArrayObject when function natcasesort() is disabled +--INI-- +disable_functions=natcasesort +--FILE-- +natcasesort(); +var_dump($ao); + +?> +--EXPECTF-- +Fatal error: Uncaught Error: Invalid callback natcasesort, function "natcasesort" not found or invalid function name in %s:%d +Stack trace: +#0 %s(%d): ArrayObject->natcasesort() +#1 {main} + thrown in %s on line %d diff --git a/ext/spl/tests/ArrayObject/natsort_disabled.phpt b/ext/spl/tests/ArrayObject/natsort_disabled.phpt new file mode 100644 index 0000000000000..994088b7950ec --- /dev/null +++ b/ext/spl/tests/ArrayObject/natsort_disabled.phpt @@ -0,0 +1,18 @@ +--TEST-- +ArrayObject when function natsort() is disabled +--INI-- +disable_functions=natsort +--FILE-- +natsort(); +var_dump($ao); + +?> +--EXPECTF-- +Fatal error: Uncaught Error: Invalid callback natsort, function "natsort" not found or invalid function name in %s:%d +Stack trace: +#0 %s(%d): ArrayObject->natsort() +#1 {main} + thrown in %s on line %d diff --git a/ext/spl/tests/ArrayObject/uasort_disabled.phpt b/ext/spl/tests/ArrayObject/uasort_disabled.phpt new file mode 100644 index 0000000000000..298eadc7303c6 --- /dev/null +++ b/ext/spl/tests/ArrayObject/uasort_disabled.phpt @@ -0,0 +1,18 @@ +--TEST-- +ArrayObject when function uasort() is disabled +--INI-- +disable_functions=uasort +--FILE-- +uasort(fn ($l, $r) => $l <=> $r); +var_dump($ao); + +?> +--EXPECTF-- +Fatal error: Uncaught Error: Invalid callback uasort, function "uasort" not found or invalid function name in %s:%d +Stack trace: +#0 %s(%d): ArrayObject->uasort(Object(Closure)) +#1 {main} + thrown in %s on line %d diff --git a/ext/spl/tests/ArrayObject/uksort_disabled.phpt b/ext/spl/tests/ArrayObject/uksort_disabled.phpt new file mode 100644 index 0000000000000..2abb020659c03 --- /dev/null +++ b/ext/spl/tests/ArrayObject/uksort_disabled.phpt @@ -0,0 +1,18 @@ +--TEST-- +ArrayObject when function uksort() is disabled +--INI-- +disable_functions=uksort +--FILE-- +uksort(fn ($l, $r) => $l <=> $r); +var_dump($ao); + +?> +--EXPECTF-- +Fatal error: Uncaught Error: Invalid callback uksort, function "uksort" not found or invalid function name in %s:%d +Stack trace: +#0 %s(%d): ArrayObject->uksort(Object(Closure)) +#1 {main} + thrown in %s on line %d From 50ddf6a68f78a8c084420955b04f52f4e57281f4 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Thu, 3 Jul 2025 15:20:35 +0100 Subject: [PATCH 240/682] ext/spl: Refactor ArrayObject sort methods --- ext/spl/spl_array.c | 52 +++++++++++-------- ext/spl/spl_array.h | 4 -- ext/spl/tests/ArrayObject/asort_disabled.phpt | 2 +- ext/spl/tests/ArrayObject/ksort_disabled.phpt | 2 +- .../ArrayObject/natcasesort_disabled.phpt | 2 +- .../tests/ArrayObject/natsort_disabled.phpt | 2 +- .../tests/ArrayObject/uasort_disabled.phpt | 2 +- .../tests/ArrayObject/uksort_disabled.phpt | 2 +- 8 files changed, 37 insertions(+), 31 deletions(-) diff --git a/ext/spl/spl_array.c b/ext/spl/spl_array.c index b950330061920..5cb3e27bbb861 100644 --- a/ext/spl/spl_array.c +++ b/ext/spl/spl_array.c @@ -1203,43 +1203,54 @@ PHP_METHOD(ArrayObject, count) RETURN_LONG(spl_array_object_count_elements_helper(intern)); } /* }}} */ -static void spl_array_method(INTERNAL_FUNCTION_PARAMETERS, char *fname, size_t fname_len, int use_arg) /* {{{ */ +enum spl_array_object_sort_methods { + SPL_NAT_SORT, + SPL_CALLBACK_SORT, + SPL_OPTIONAL_FLAG_SORT +}; + +static void spl_array_method(INTERNAL_FUNCTION_PARAMETERS, const char *fname, size_t fname_len, enum spl_array_object_sort_methods use_arg) /* {{{ */ { spl_array_object *intern = Z_SPLARRAY_P(ZEND_THIS); HashTable **ht_ptr = spl_array_get_hash_table_ptr(intern); HashTable *aht = *ht_ptr; - zval function_name, params[2], *arg = NULL; + zval params[2], *arg = NULL; - ZVAL_STRINGL(&function_name, fname, fname_len); + zend_function *fn = zend_hash_str_find_ptr(EG(function_table), fname, fname_len); + if (UNEXPECTED(fn == NULL)) { + zend_throw_error(NULL, "Cannot call method %s when function %s is disabled", fname, fname); + RETURN_THROWS(); + } ZVAL_NEW_EMPTY_REF(¶ms[0]); ZVAL_ARR(Z_REFVAL(params[0]), aht); GC_ADDREF(aht); - if (!use_arg) { + if (use_arg == SPL_NAT_SORT) { if (zend_parse_parameters_none() == FAILURE) { goto exit; } intern->nApplyCount++; - call_user_function(EG(function_table), NULL, &function_name, return_value, 1, params); + zend_call_known_function(fn, NULL, NULL, return_value, 1, params, NULL); intern->nApplyCount--; - } else if (use_arg == SPL_ARRAY_METHOD_SORT_FLAGS_ARG) { + } else if (use_arg == SPL_OPTIONAL_FLAG_SORT) { zend_long sort_flags = 0; if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &sort_flags) == FAILURE) { goto exit; } ZVAL_LONG(¶ms[1], sort_flags); intern->nApplyCount++; - call_user_function(EG(function_table), NULL, &function_name, return_value, 2, params); + zend_call_known_function(fn, NULL, NULL, return_value, 2, params, NULL); intern->nApplyCount--; } else { + ZEND_ASSERT(use_arg == SPL_CALLBACK_SORT); if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &arg) == FAILURE) { goto exit; } ZVAL_COPY_VALUE(¶ms[1], arg); intern->nApplyCount++; - call_user_function(EG(function_table), NULL, &function_name, return_value, 2, params); + zend_call_known_function(fn, NULL, NULL, return_value, 2, params, NULL); intern->nApplyCount--; } @@ -1251,7 +1262,6 @@ static void spl_array_method(INTERNAL_FUNCTION_PARAMETERS, char *fname, size_t f *ht_ptr = Z_ARRVAL_P(ht_zv); ZVAL_NULL(ht_zv); zval_ptr_dtor(¶ms[0]); - zend_string_free(Z_STR(function_name)); } } /* }}} */ @@ -1261,23 +1271,23 @@ PHP_METHOD(cname, fname) \ spl_array_method(INTERNAL_FUNCTION_PARAM_PASSTHRU, #fname, sizeof(#fname)-1, use_arg); \ } -/* {{{ Sort the entries by values. */ -SPL_ARRAY_METHOD(ArrayObject, asort, SPL_ARRAY_METHOD_SORT_FLAGS_ARG) /* }}} */ +/* Sort the entries by values. */ +SPL_ARRAY_METHOD(ArrayObject, asort, SPL_OPTIONAL_FLAG_SORT) -/* {{{ Sort the entries by key. */ -SPL_ARRAY_METHOD(ArrayObject, ksort, SPL_ARRAY_METHOD_SORT_FLAGS_ARG) /* }}} */ +/* Sort the entries by key. */ +SPL_ARRAY_METHOD(ArrayObject, ksort, SPL_OPTIONAL_FLAG_SORT) -/* {{{ Sort the entries by values user defined function. */ -SPL_ARRAY_METHOD(ArrayObject, uasort, SPL_ARRAY_METHOD_CALLBACK_ARG) /* }}} */ +/* Sort the entries by values user defined function. */ +SPL_ARRAY_METHOD(ArrayObject, uasort, SPL_CALLBACK_SORT) -/* {{{ Sort the entries by key using user defined function. */ -SPL_ARRAY_METHOD(ArrayObject, uksort, SPL_ARRAY_METHOD_CALLBACK_ARG) /* }}} */ +/* Sort the entries by key using user defined function. */ +SPL_ARRAY_METHOD(ArrayObject, uksort, SPL_CALLBACK_SORT) -/* {{{ Sort the entries by values using "natural order" algorithm. */ -SPL_ARRAY_METHOD(ArrayObject, natsort, SPL_ARRAY_METHOD_NO_ARG) /* }}} */ +/* Sort the entries by values using "natural order" algorithm. */ +SPL_ARRAY_METHOD(ArrayObject, natsort, SPL_NAT_SORT) -/* {{{ Sort the entries by key using case insensitive "natural order" algorithm. */ -SPL_ARRAY_METHOD(ArrayObject, natcasesort, SPL_ARRAY_METHOD_NO_ARG) /* }}} */ +/* {{{ Sort the entries by key using case-insensitive "natural order" algorithm. */ +SPL_ARRAY_METHOD(ArrayObject, natcasesort, SPL_NAT_SORT) /* {{{ Serialize the object */ PHP_METHOD(ArrayObject, serialize) diff --git a/ext/spl/spl_array.h b/ext/spl/spl_array.h index f3577f4beeaad..86de7a955c5b2 100644 --- a/ext/spl/spl_array.h +++ b/ext/spl/spl_array.h @@ -27,10 +27,6 @@ #define SPL_ARRAY_INT_MASK 0xFFFF0000 #define SPL_ARRAY_CLONE_MASK 0x0100FFFF -#define SPL_ARRAY_METHOD_NO_ARG 0 -#define SPL_ARRAY_METHOD_CALLBACK_ARG 1 -#define SPL_ARRAY_METHOD_SORT_FLAGS_ARG 2 - extern PHPAPI zend_class_entry *spl_ce_ArrayObject; extern PHPAPI zend_class_entry *spl_ce_ArrayIterator; extern PHPAPI zend_class_entry *spl_ce_RecursiveArrayIterator; diff --git a/ext/spl/tests/ArrayObject/asort_disabled.phpt b/ext/spl/tests/ArrayObject/asort_disabled.phpt index 57fb742acbddb..8f2241332f72e 100644 --- a/ext/spl/tests/ArrayObject/asort_disabled.phpt +++ b/ext/spl/tests/ArrayObject/asort_disabled.phpt @@ -11,7 +11,7 @@ var_dump($ao); ?> --EXPECTF-- -Fatal error: Uncaught Error: Invalid callback asort, function "asort" not found or invalid function name in %s:%d +Fatal error: Uncaught Error: Cannot call method asort when function asort is disabled in %s:%d Stack trace: #0 %s(%d): ArrayObject->asort() #1 {main} diff --git a/ext/spl/tests/ArrayObject/ksort_disabled.phpt b/ext/spl/tests/ArrayObject/ksort_disabled.phpt index 4299c282189f1..258057ad64d31 100644 --- a/ext/spl/tests/ArrayObject/ksort_disabled.phpt +++ b/ext/spl/tests/ArrayObject/ksort_disabled.phpt @@ -11,7 +11,7 @@ var_dump($ao); ?> --EXPECTF-- -Fatal error: Uncaught Error: Invalid callback ksort, function "ksort" not found or invalid function name in %s:%d +Fatal error: Uncaught Error: Cannot call method ksort when function ksort is disabled in %s:%d Stack trace: #0 %s(%d): ArrayObject->ksort() #1 {main} diff --git a/ext/spl/tests/ArrayObject/natcasesort_disabled.phpt b/ext/spl/tests/ArrayObject/natcasesort_disabled.phpt index def9af50e3561..336e1245dabc5 100644 --- a/ext/spl/tests/ArrayObject/natcasesort_disabled.phpt +++ b/ext/spl/tests/ArrayObject/natcasesort_disabled.phpt @@ -11,7 +11,7 @@ var_dump($ao); ?> --EXPECTF-- -Fatal error: Uncaught Error: Invalid callback natcasesort, function "natcasesort" not found or invalid function name in %s:%d +Fatal error: Uncaught Error: Cannot call method natcasesort when function natcasesort is disabled in %s:%d Stack trace: #0 %s(%d): ArrayObject->natcasesort() #1 {main} diff --git a/ext/spl/tests/ArrayObject/natsort_disabled.phpt b/ext/spl/tests/ArrayObject/natsort_disabled.phpt index 994088b7950ec..b674235627adb 100644 --- a/ext/spl/tests/ArrayObject/natsort_disabled.phpt +++ b/ext/spl/tests/ArrayObject/natsort_disabled.phpt @@ -11,7 +11,7 @@ var_dump($ao); ?> --EXPECTF-- -Fatal error: Uncaught Error: Invalid callback natsort, function "natsort" not found or invalid function name in %s:%d +Fatal error: Uncaught Error: Cannot call method natsort when function natsort is disabled in %s:%d Stack trace: #0 %s(%d): ArrayObject->natsort() #1 {main} diff --git a/ext/spl/tests/ArrayObject/uasort_disabled.phpt b/ext/spl/tests/ArrayObject/uasort_disabled.phpt index 298eadc7303c6..5a5e9aa57b21a 100644 --- a/ext/spl/tests/ArrayObject/uasort_disabled.phpt +++ b/ext/spl/tests/ArrayObject/uasort_disabled.phpt @@ -11,7 +11,7 @@ var_dump($ao); ?> --EXPECTF-- -Fatal error: Uncaught Error: Invalid callback uasort, function "uasort" not found or invalid function name in %s:%d +Fatal error: Uncaught Error: Cannot call method uasort when function uasort is disabled in %s:%d Stack trace: #0 %s(%d): ArrayObject->uasort(Object(Closure)) #1 {main} diff --git a/ext/spl/tests/ArrayObject/uksort_disabled.phpt b/ext/spl/tests/ArrayObject/uksort_disabled.phpt index 2abb020659c03..af703883a3f55 100644 --- a/ext/spl/tests/ArrayObject/uksort_disabled.phpt +++ b/ext/spl/tests/ArrayObject/uksort_disabled.phpt @@ -11,7 +11,7 @@ var_dump($ao); ?> --EXPECTF-- -Fatal error: Uncaught Error: Invalid callback uksort, function "uksort" not found or invalid function name in %s:%d +Fatal error: Uncaught Error: Cannot call method uksort when function uksort is disabled in %s:%d Stack trace: #0 %s(%d): ArrayObject->uksort(Object(Closure)) #1 {main} From 8bb6b81c60851e35383cb59b6ffa0a1fed98151a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Fri, 4 Jul 2025 08:20:27 +0200 Subject: [PATCH 241/682] Update uriparser to commit 8c06d --- ext/uri/uriparser/include/uriparser/Uri.h | 8 ++++++++ ext/uri/uriparser/src/UriCopy.c | 7 ++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/ext/uri/uriparser/include/uriparser/Uri.h b/ext/uri/uriparser/include/uriparser/Uri.h index f0f2ad9a34bd0..9f08559a1858f 100644 --- a/ext/uri/uriparser/include/uriparser/Uri.h +++ b/ext/uri/uriparser/include/uriparser/Uri.h @@ -334,6 +334,10 @@ URI_PUBLIC int URI_FUNC(ParseSingleUriExMm)(URI_TYPE(Uri) * uri, * itself is not freed, only its members. * Uses default libc-based memory manager. * + * @remarks + * Calling on an all-zeros structure (e.g. through memset or calloc) is safe.
+ * Calling on an uninitialized structure is not safe. + * * @param uri INOUT: %URI structure whose members should be freed * * @see uriFreeUriMembersMmA @@ -348,6 +352,10 @@ URI_PUBLIC void URI_FUNC(FreeUriMembers)(URI_TYPE(Uri) * uri); * of the %URI structure. Note that the structure * itself is not freed, only its members. * + * @remarks + * Calling on an all-zeros structure (e.g. through memset or calloc) is safe.
+ * Calling on an uninitialized structure is not safe. + * * @param uri INOUT: %URI structure whose members should be freed * @param memory IN: Memory manager to use, NULL for default libc * @return 0 on success, error code otherwise diff --git a/ext/uri/uriparser/src/UriCopy.c b/ext/uri/uriparser/src/UriCopy.c index 0974ec5c0406d..103e2e7796751 100644 --- a/ext/uri/uriparser/src/UriCopy.c +++ b/ext/uri/uriparser/src/UriCopy.c @@ -111,6 +111,8 @@ int URI_FUNC(CopyUriMm)(URI_TYPE(Uri) * destUri, URI_CHECK_MEMORY_MANAGER(memory); /* may return */ + URI_FUNC(ResetUri)(destUri); + if (URI_FUNC(CopyRangeAsNeeded)(&destUri->scheme, &sourceUri->scheme, URI_FALSE, memory) == URI_FALSE) { return URI_ERROR_MALLOC; } @@ -153,7 +155,10 @@ int URI_FUNC(CopyUriMm)(URI_TYPE(Uri) * destUri, *(destUri->hostData.ip6) = *(sourceUri->hostData.ip6); } - if (URI_FUNC(CopyRangeAsNeeded)(&destUri->hostData.ipFuture, &sourceUri->hostData.ipFuture, URI_FALSE, memory) == URI_FALSE) { + if (sourceUri->hostData.ipFuture.first != NULL && sourceUri->hostText.first == sourceUri->hostData.ipFuture.first) { + destUri->hostData.ipFuture.first = destUri->hostText.first; + destUri->hostData.ipFuture.afterLast = destUri->hostText.afterLast; + } else if (URI_FUNC(CopyRangeAsNeeded)(&destUri->hostData.ipFuture, &sourceUri->hostData.ipFuture, URI_FALSE, memory) == URI_FALSE) { URI_FUNC(PreventLeakageAfterCopy)(destUri, doneMask, memory); return URI_ERROR_MALLOC; } From 75006cf21dbc73bd4a566c9960bb1bda3e2060b5 Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Fri, 4 Jul 2025 08:33:07 +0200 Subject: [PATCH 242/682] avoid false failure for long path (#18992) --- sapi/fpm/tests/socket-uds-too-long-filename-start.phpt | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/sapi/fpm/tests/socket-uds-too-long-filename-start.phpt b/sapi/fpm/tests/socket-uds-too-long-filename-start.phpt index a7b43d9519b0a..1baf47a466a42 100644 --- a/sapi/fpm/tests/socket-uds-too-long-filename-start.phpt +++ b/sapi/fpm/tests/socket-uds-too-long-filename-start.phpt @@ -40,11 +40,7 @@ $tester->expectLogPattern( $files = glob($socketFilePrefix . '*'); -if ($files === []) { - echo 'Socket files were not found.' . PHP_EOL; -} - -if ($socketFile === $files[0]) { +if (isset($files[0]) && $socketFile === $files[0]) { // this means the socket file path length is not an issue (anymore). Might be not long enough echo 'Socket file is the same as configured.' . PHP_EOL; } From 0cdb5d0aa1540110a20c615fd4b0a6405cabfd0b Mon Sep 17 00:00:00 2001 From: DanielEScherzer Date: Fri, 4 Jul 2025 11:30:31 -0700 Subject: [PATCH 243/682] release-process: update based on 8.5.0alpha1 announcement (#19028) * Add reminder to replace outdated information in the announcements * Add instruction for new RMs to subscribe to the lists they need to email --- docs/release-process.md | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/docs/release-process.md b/docs/release-process.md index 299465229551e..015872096a150 100644 --- a/docs/release-process.md +++ b/docs/release-process.md @@ -474,6 +474,15 @@ slightly different steps. We'll call attention where the steps differ. Here are a few examples of non-stable release announcement emails: + > 💬 **Hint** \ + > If you are going to base your language on one of these old announcements, + > remember that + > * `qa.php.net` has been replaced with https://www.php.net/release-candidates.php + > * `bugs.php.net` has been replaced with GitHub issues, use + > `https://github.com/php/php-src/issues/new?template=bug_report.yml` + > to link directly to the form for creating a new bug report. + > * Since 8.4 there have only been 4 release candidates for PHP X.Y.0, rather than 6. + * [PHP 8.1.0alpha1 is available for testing](https://news-web.php.net/php.qa/69043) * [PHP 8.1.0beta3 available for testing](https://news-web.php.net/php.qa/69079) * [PHP 8.1.0RC6 available for testing](https://news-web.php.net/php.qa/69117) @@ -1008,8 +1017,13 @@ volunteers to begin the selection process for the next release managers. 2. Request membership to the [release managers group](https://github.com/orgs/php/teams/release-managers) on GitHub. -3. Subscribe to the php-announce@lists.php.net mailing list by emailing - php-announce+subscribe@lists.php.net +3. Make sure you are subscribed to all of the mailing lists that you will need to send + announcements to, since you cannot post to the lists otherwise: + + * internals@lists.php.net (email internals+subscribe@lists.php.net) + * php-announce@lists.php.net (email php-announce+subscribe@lists.php.net) + * php-general@lists.php.net (email php-general+subscribe@lists.php.net) + * php-qa@lists.php.net (email php-qa+subscribe@lists.php.net) 4. Email systems@php.net to get setup for access to downloads.php.net, to be added to the release-managers@php.net distribution list, and to be added to From d43fbc0c0e50b3223b6d12d820a26e0a7707fef1 Mon Sep 17 00:00:00 2001 From: DanielEScherzer Date: Fri, 4 Jul 2025 12:33:48 -0700 Subject: [PATCH 244/682] `ReflectionParameter::allowsNull()` - fix typo in description [skip ci] --- ext/reflection/php_reflection.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 8ef0269481cf7..78817152904fb 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -2844,7 +2844,7 @@ ZEND_METHOD(ReflectionParameter, isCallable) } /* }}} */ -/* {{{ Returns whether NULL is allowed as this parameters's value */ +/* {{{ Returns whether NULL is allowed as this parameter's value */ ZEND_METHOD(ReflectionParameter, allowsNull) { reflection_object *intern; From 3558293ce89deba525145458b79af7245ddfb22d Mon Sep 17 00:00:00 2001 From: DanielEScherzer Date: Fri, 4 Jul 2025 14:40:54 -0700 Subject: [PATCH 245/682] Remove broken zend_get_zendleng() declaration (#19032) Does not actually exist anywhere --- Zend/zend_compile.h | 1 - 1 file changed, 1 deletion(-) diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 62d0fbcded2ee..7ac0a2b8b2c44 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -886,7 +886,6 @@ ZEND_API zend_string *zend_get_compiled_variable_name(const zend_op_array *op_ar #ifdef ZTS const char *zend_get_zendtext(void); -int zend_get_zendleng(void); #endif typedef zend_result (ZEND_FASTCALL *unary_op_type)(zval *, zval *); From 4e42ad5bf2dbc51d18bc683db3b2168ebf8df8d1 Mon Sep 17 00:00:00 2001 From: DanielEScherzer Date: Fri, 4 Jul 2025 14:41:24 -0700 Subject: [PATCH 246/682] ext/standard/string.c: don't use `STR_EMPTY_ALLOC()` (#19033) This was the only remaining use of a compatibility alias from 10 years ago; replace with `ZSTR_EMPTY_ALLOC()`. --- ext/standard/string.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/standard/string.c b/ext/standard/string.c index 36903b3c5c7b9..75be1f1dcab1c 100644 --- a/ext/standard/string.c +++ b/ext/standard/string.c @@ -2404,7 +2404,7 @@ PHP_FUNCTION(substr_replace) if (repl_idx < repl_ht->nNumUsed) { repl_str = zval_get_tmp_string(tmp_repl, &tmp_repl_str); } else { - repl_str = STR_EMPTY_ALLOC(); + repl_str = ZSTR_EMPTY_ALLOC(); } } From 4aac98f1456069b69ffd701dadb31be014a8e90c Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Wed, 2 Jul 2025 20:29:35 +0200 Subject: [PATCH 247/682] Fix OSS-Fuzz #428983568 and #428760800 Both these issues have the same root cause, their reproducer is extremely similar so I don't duplicate the test. If the parser invokes the lexer, and the lexer fails, it could've allocated a string which must be freed when the parser backs up. The `%destructor` list is responsible for this but did not have an entry for `fallback` yet. Solve the issue by adding such an entry. Closes GH-19012. --- NEWS | 1 + Zend/tests/zend_ini/oss_fuzz_428983568.phpt | 14 ++++++++++++++ Zend/zend_ini_parser.y | 2 +- 3 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 Zend/tests/zend_ini/oss_fuzz_428983568.phpt diff --git a/NEWS b/NEWS index 814c4692f3d6a..11b5146bc33f9 100644 --- a/NEWS +++ b/NEWS @@ -9,6 +9,7 @@ PHP NEWS . Fixed bug GH-18833 (Use after free with weakmaps dependent on destruction order). (Daniil Gentili) . Fix OSS-Fuzz #427814456. (nielsdos) + . Fix OSS-Fuzz #428983568 and #428760800. (nielsdos) - Curl: . Fix memory leaks when returning refcounted value from curl callback. diff --git a/Zend/tests/zend_ini/oss_fuzz_428983568.phpt b/Zend/tests/zend_ini/oss_fuzz_428983568.phpt new file mode 100644 index 0000000000000..80310fbd9287f --- /dev/null +++ b/Zend/tests/zend_ini/oss_fuzz_428983568.phpt @@ -0,0 +1,14 @@ +--TEST-- +OSS-Fuzz #428983568 +--FILE-- + +--EXPECTF-- +Warning: syntax error, unexpected end of file, expecting '}' in Unknown on line 1 + in %s on line %d +bool(false) diff --git a/Zend/zend_ini_parser.y b/Zend/zend_ini_parser.y index 352f2eb3eec26..370493d54e1be 100644 --- a/Zend/zend_ini_parser.y +++ b/Zend/zend_ini_parser.y @@ -353,7 +353,7 @@ static void normalize_value(zval *zv) %left '|' '&' '^' %precedence '~' '!' -%destructor { zval_ini_dtor(&$$); } TC_RAW TC_CONSTANT TC_NUMBER TC_STRING TC_WHITESPACE TC_LABEL TC_OFFSET TC_VARNAME BOOL_TRUE BOOL_FALSE NULL_NULL cfg_var_ref constant_literal constant_string encapsed_list expr option_offset section_string_or_value string_or_value var_string_list var_string_list_section +%destructor { zval_ini_dtor(&$$); } TC_RAW TC_CONSTANT TC_NUMBER TC_STRING TC_WHITESPACE TC_LABEL TC_OFFSET TC_VARNAME BOOL_TRUE BOOL_FALSE NULL_NULL cfg_var_ref constant_literal constant_string encapsed_list expr fallback option_offset section_string_or_value string_or_value var_string_list var_string_list_section %% From 4560f7037da25ae0738d025f0d3c9143b15921bd Mon Sep 17 00:00:00 2001 From: DanielEScherzer Date: Fri, 4 Jul 2025 15:10:16 -0700 Subject: [PATCH 248/682] EXTENSIONS: lexbor is since 2025 [skip ci] (#19031) --- EXTENSIONS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EXTENSIONS b/EXTENSIONS index d15401f372384..01685748b5e09 100644 --- a/EXTENSIONS +++ b/EXTENSIONS @@ -196,7 +196,7 @@ MAINTENANCE: Maintained STATUS: Working ------------------------------------------------------------------------------- EXTENSION: lexbor -PRIMARY MAINTAINER: Niels Dossche (2023 - 2025) +PRIMARY MAINTAINER: Niels Dossche (2025 - 2025) Mate Kocsis (2025 - 2025) MAINTENANCE: Maintained STATUS: Working From 5a9f5a65142efb0b89be413b4bc9a604b3e6d825 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Sat, 5 Jul 2025 10:00:20 +0200 Subject: [PATCH 249/682] Add the Uri\Rfc3986\Uri class to ext/uri without wither support (#18836) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Relates to #14461 and https://wiki.php.net/rfc/url_parsing_api Co-authored-by: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Co-authored-by: Tim Düsterhus --- ext/uri/config.m4 | 3 +- ext/uri/config.w32 | 4 +- ext/uri/php_uri.c | 403 +++++++++++++++++++++++++++--------- ext/uri/php_uri.stub.php | 64 ++++++ ext/uri/php_uri_arginfo.h | 195 ++++++++++++++---- ext/uri/php_uri_common.c | 12 +- ext/uri/php_uri_common.h | 3 + ext/uri/php_uriparser.c | 415 ++++++++++++++++++++++++++++++++++++++ ext/uri/php_uriparser.h | 38 ++++ ext/uri/tests/003.phpt | 21 ++ ext/uri/tests/004.phpt | 33 ++- ext/uri/tests/005.phpt | 3 + ext/uri/tests/006.phpt | 21 ++ ext/uri/tests/007.phpt | 7 + ext/uri/tests/008.phpt | 44 ++++ ext/uri/tests/009.phpt | 19 ++ ext/uri/tests/010.phpt | 38 ++++ ext/uri/tests/011.phpt | 11 + ext/uri/tests/012.phpt | 38 ++++ ext/uri/tests/013.phpt | 44 ++++ ext/uri/tests/014.phpt | 3 + ext/uri/tests/015.phpt | 8 + ext/uri/tests/018.phpt | 45 ++++- ext/uri/tests/019.phpt | 8 + ext/uri/tests/021.phpt | 14 ++ ext/uri/tests/031.phpt | 82 ++++++++ ext/uri/tests/032.phpt | 4 + ext/uri/tests/033.phpt | 6 + ext/uri/tests/034.phpt | 5 + ext/uri/tests/035.phpt | 7 + ext/uri/tests/036.phpt | 15 ++ ext/uri/tests/038.phpt | 16 ++ ext/uri/tests/039.phpt | 44 ++++ ext/uri/tests/040.phpt | 23 +++ ext/uri/tests/041.phpt | 21 ++ ext/uri/tests/042.phpt | 21 ++ ext/uri/tests/043.phpt | 86 ++++++++ ext/uri/tests/045.phpt | 9 + ext/uri/tests/046.phpt | 59 ++++++ ext/uri/tests/047.phpt | 30 +++ ext/uri/tests/048.phpt | 17 ++ ext/uri/tests/049.phpt | 6 + ext/uri/tests/050.phpt | 7 + ext/uri/tests/051.phpt | 9 + ext/uri/tests/055.phpt | 15 ++ 45 files changed, 1831 insertions(+), 145 deletions(-) create mode 100644 ext/uri/php_uriparser.c create mode 100644 ext/uri/php_uriparser.h create mode 100644 ext/uri/tests/021.phpt create mode 100644 ext/uri/tests/048.phpt create mode 100644 ext/uri/tests/055.phpt diff --git a/ext/uri/config.m4 b/ext/uri/config.m4 index 3631ad3c5c06d..2f5a7a489b5ae 100644 --- a/ext/uri/config.m4 +++ b/ext/uri/config.m4 @@ -5,6 +5,7 @@ PHP_INSTALL_HEADERS([ext/uri], m4_normalize([ php_lexbor.h php_uri.h php_uri_common.h + php_uriparser.h ])) AC_DEFINE([URI_ENABLE_ANSI], [1], [Define to 1 for enabling ANSI support of uriparser.]) @@ -17,6 +18,6 @@ $URIPARSER_DIR/src/UriMemory.c $URIPARSER_DIR/src/UriNormalize.c $URIPARSER_DIR/ $URIPARSER_DIR/src/UriParse.c $URIPARSER_DIR/src/UriParseBase.c $URIPARSER_DIR/src/UriQuery.c \ $URIPARSER_DIR/src/UriRecompose.c $URIPARSER_DIR/src/UriResolve.c $URIPARSER_DIR/src/UriShorten.c" -PHP_NEW_EXTENSION(uri, [php_lexbor.c php_uri.c php_uri_common.c $URIPARSER_SOURCES], [no],,[-I$ext_srcdir/$URIPARSER_DIR/include -DURI_STATIC_BUILD -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1]) +PHP_NEW_EXTENSION(uri, [php_lexbor.c php_uri.c php_uri_common.c php_uriparser.c $URIPARSER_SOURCES], [no],,[-I$ext_srcdir/$URIPARSER_DIR/include -DURI_STATIC_BUILD -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1]) PHP_ADD_EXTENSION_DEP(uri, lexbor) PHP_ADD_BUILD_DIR($ext_builddir/$URIPARSER_DIR/src $ext_builddir/$URIPARSER_DIR/include) diff --git a/ext/uri/config.w32 b/ext/uri/config.w32 index 8086b4b9bfc93..6954ca06af555 100644 --- a/ext/uri/config.w32 +++ b/ext/uri/config.w32 @@ -1,4 +1,4 @@ -EXTENSION("uri", "php_lexbor.c php_uri.c php_uri_common.c", false /* never shared */, "/I ext/lexbor /I ext/uri/uriparser/include /DZEND_ENABLE_STATIC_TSRMLS_CACHE=1"); +EXTENSION("uri", "php_lexbor.c php_uri.c php_uri_common.c php_uriparser.c", false /* never shared */, "/I ext/lexbor /I ext/uri/uriparser/include /DZEND_ENABLE_STATIC_TSRMLS_CACHE=1"); AC_DEFINE("URI_ENABLE_ANSI", 1, "Define to 1 for enabling ANSI support of uriparser.") AC_DEFINE("URI_NO_UNICODE", 1, "Define to 1 for disabling unicode support of uriparser.") @@ -6,4 +6,4 @@ ADD_FLAG("CFLAGS_URI", "/D URI_STATIC_BUILD"); ADD_EXTENSION_DEP('uri', 'lexbor'); ADD_SOURCES("ext/uri/uriparser/src", "UriCommon.c UriCompare.c UriCopy.c UriEscape.c UriFile.c UriIp4.c UriIp4Base.c UriMemory.c UriNormalize.c UriNormalizeBase.c UriParse.c UriParseBase.c UriQuery.c UriRecompose.c UriResolve.c UriShorten.c", "uri"); -PHP_INSTALL_HEADERS("ext/uri", "php_lexbor.h php_uri.h php_uri_common.h uriparser/src uriparser/include"); +PHP_INSTALL_HEADERS("ext/uri", "php_lexbor.h php_uri.h php_uri_common.h php_uriparser.h uriparser/src uriparser/include"); diff --git a/ext/uri/php_uri.c b/ext/uri/php_uri.c index 5b2e21b1625a3..f5a5b160bba06 100644 --- a/ext/uri/php_uri.c +++ b/ext/uri/php_uri.c @@ -25,12 +25,14 @@ #include "Zend/zend_enum.h" #include "ext/standard/info.h" -#include "php_uri.h" #include "php_uri_common.h" #include "php_lexbor.h" +#include "php_uriparser.h" #include "php_uri_arginfo.h" #include "uriparser/src/UriConfig.h" +zend_class_entry *uri_rfc3986_uri_ce; +zend_object_handlers uri_rfc3986_uri_object_handlers; zend_class_entry *uri_whatwg_url_ce; zend_object_handlers uri_whatwg_uri_object_handlers; zend_class_entry *uri_comparison_mode_ce; @@ -104,67 +106,6 @@ static HashTable *uri_get_debug_properties(zend_object *object) return result; } -PHP_METHOD(Uri_WhatWg_InvalidUrlException, __construct) -{ - zend_string *message = NULL; - zval *errors = NULL; - zend_long code = 0; - zval *previous = NULL; - - ZEND_PARSE_PARAMETERS_START(0, 4) - Z_PARAM_OPTIONAL - Z_PARAM_STR(message) - Z_PARAM_ARRAY(errors) - Z_PARAM_LONG(code) - Z_PARAM_OBJECT_OF_CLASS_OR_NULL(previous, zend_ce_throwable) - ZEND_PARSE_PARAMETERS_END(); - - if (zend_update_exception_properties(INTERNAL_FUNCTION_PARAM_PASSTHRU, message, code, previous) == FAILURE) { - RETURN_THROWS(); - } - - if (errors == NULL) { - zval tmp; - ZVAL_EMPTY_ARRAY(&tmp); - zend_update_property(uri_whatwg_invalid_url_exception_ce, Z_OBJ_P(ZEND_THIS), ZEND_STRL("errors"), &tmp); - } else { - zend_update_property(uri_whatwg_invalid_url_exception_ce, Z_OBJ_P(ZEND_THIS), ZEND_STRL("errors"), errors); - } - if (EG(exception)) { - RETURN_THROWS(); - } -} - -PHP_METHOD(Uri_WhatWg_UrlValidationError, __construct) -{ - zend_string *context; - zval *type; - bool failure; - - ZEND_PARSE_PARAMETERS_START(3, 3) - Z_PARAM_STR(context) - Z_PARAM_OBJECT_OF_CLASS(type, uri_whatwg_url_validation_error_type_ce) - Z_PARAM_BOOL(failure) - ZEND_PARSE_PARAMETERS_END(); - - zend_update_property_str(uri_whatwg_url_validation_error_ce, Z_OBJ_P(ZEND_THIS), ZEND_STRL("context"), context); - if (EG(exception)) { - RETURN_THROWS(); - } - - zend_update_property_ex(uri_whatwg_url_validation_error_ce, Z_OBJ_P(ZEND_THIS), ZSTR_KNOWN(ZEND_STR_TYPE), type); - if (EG(exception)) { - RETURN_THROWS(); - } - - zval failure_zv; - ZVAL_BOOL(&failure_zv, failure); - zend_update_property(uri_whatwg_url_validation_error_ce, Z_OBJ_P(ZEND_THIS), ZEND_STRL("failure"), &failure_zv); - if (EG(exception)) { - RETURN_THROWS(); - } -} - /** * Pass the errors parameter by ref to errors_zv for userland, and frees it if * it is not not needed anymore. @@ -242,6 +183,91 @@ PHPAPI void php_uri_instantiate_uri( uri_object->internal.uri = uri; } +static void create_rfc3986_uri(INTERNAL_FUNCTION_PARAMETERS, bool is_constructor) +{ + zend_string *uri_str; + zend_object *base_url_object = NULL; + + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_PATH_STR(uri_str) + Z_PARAM_OPTIONAL + Z_PARAM_OBJ_OF_CLASS_OR_NULL(base_url_object, uri_rfc3986_uri_ce) + ZEND_PARSE_PARAMETERS_END(); + + php_uri_instantiate_uri(INTERNAL_FUNCTION_PARAM_PASSTHRU, &uriparser_uri_handler, uri_str, base_url_object, is_constructor, is_constructor, NULL); +} + +PHP_METHOD(Uri_Rfc3986_Uri, parse) +{ + create_rfc3986_uri(INTERNAL_FUNCTION_PARAM_PASSTHRU, false); +} + +PHP_METHOD(Uri_Rfc3986_Uri, __construct) +{ + create_rfc3986_uri(INTERNAL_FUNCTION_PARAM_PASSTHRU, true); +} + +PHP_METHOD(Uri_WhatWg_InvalidUrlException, __construct) +{ + zend_string *message = NULL; + zval *errors = NULL; + zend_long code = 0; + zval *previous = NULL; + + ZEND_PARSE_PARAMETERS_START(0, 4) + Z_PARAM_OPTIONAL + Z_PARAM_STR(message) + Z_PARAM_ARRAY(errors) + Z_PARAM_LONG(code) + Z_PARAM_OBJECT_OF_CLASS_OR_NULL(previous, zend_ce_throwable) + ZEND_PARSE_PARAMETERS_END(); + + if (zend_update_exception_properties(INTERNAL_FUNCTION_PARAM_PASSTHRU, message, code, previous) == FAILURE) { + RETURN_THROWS(); + } + + if (errors == NULL) { + zval tmp; + ZVAL_EMPTY_ARRAY(&tmp); + zend_update_property(uri_whatwg_invalid_url_exception_ce, Z_OBJ_P(ZEND_THIS), ZEND_STRL("errors"), &tmp); + } else { + zend_update_property(uri_whatwg_invalid_url_exception_ce, Z_OBJ_P(ZEND_THIS), ZEND_STRL("errors"), errors); + } + if (EG(exception)) { + RETURN_THROWS(); + } +} + +PHP_METHOD(Uri_WhatWg_UrlValidationError, __construct) +{ + zend_string *context; + zval *type; + bool failure; + + ZEND_PARSE_PARAMETERS_START(3, 3) + Z_PARAM_STR(context) + Z_PARAM_OBJECT_OF_CLASS(type, uri_whatwg_url_validation_error_type_ce) + Z_PARAM_BOOL(failure) + ZEND_PARSE_PARAMETERS_END(); + + zend_update_property_str(uri_whatwg_url_validation_error_ce, Z_OBJ_P(ZEND_THIS), ZEND_STRL("context"), context); + if (EG(exception)) { + RETURN_THROWS(); + } + + zend_update_property_ex(uri_whatwg_url_validation_error_ce, Z_OBJ_P(ZEND_THIS), ZSTR_KNOWN(ZEND_STR_TYPE), type); + if (EG(exception)) { + RETURN_THROWS(); + } + + zval failure_zv; + ZVAL_BOOL(&failure_zv, failure); + zend_update_property(uri_whatwg_url_validation_error_ce, Z_OBJ_P(ZEND_THIS), ZEND_STRL("failure"), &failure_zv); + if (EG(exception)) { + RETURN_THROWS(); + } +} + static void create_whatwg_uri(INTERNAL_FUNCTION_PARAMETERS, bool is_constructor) { zend_string *uri_str; @@ -268,6 +294,109 @@ PHP_METHOD(Uri_WhatWg_Url, __construct) create_whatwg_uri(INTERNAL_FUNCTION_PARAM_PASSTHRU, true); } +PHP_METHOD(Uri_Rfc3986_Uri, getScheme) +{ + uri_read_component(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_SCHEME, URI_COMPONENT_READ_NORMALIZED_ASCII); +} + +PHP_METHOD(Uri_Rfc3986_Uri, getRawScheme) +{ + uri_read_component(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_SCHEME, URI_COMPONENT_READ_RAW); +} + +static void read_uriparser_userinfo(INTERNAL_FUNCTION_PARAMETERS, uri_component_read_mode_t read_mode) +{ + ZEND_PARSE_PARAMETERS_NONE(); + + uri_internal_t *internal_uri = Z_URI_INTERNAL_P(ZEND_THIS); + URI_ASSERT_INITIALIZATION(internal_uri); + + if (UNEXPECTED(uriparser_read_userinfo(internal_uri, read_mode, return_value) == FAILURE)) { + zend_throw_error(NULL, "The userinfo component cannot be retrieved"); + RETURN_THROWS(); + } +} + +PHP_METHOD(Uri_Rfc3986_Uri, getUserInfo) +{ + read_uriparser_userinfo(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_COMPONENT_READ_NORMALIZED_ASCII); +} + +PHP_METHOD(Uri_Rfc3986_Uri, getRawUserInfo) +{ + read_uriparser_userinfo(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_COMPONENT_READ_RAW); +} + +PHP_METHOD(Uri_Rfc3986_Uri, getUsername) +{ + uri_read_component(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_USERNAME, URI_COMPONENT_READ_NORMALIZED_ASCII); +} + +PHP_METHOD(Uri_Rfc3986_Uri, getRawUsername) +{ + uri_read_component(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_USERNAME, URI_COMPONENT_READ_RAW); +} + +PHP_METHOD(Uri_Rfc3986_Uri, getPassword) +{ + uri_read_component(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_PASSWORD, URI_COMPONENT_READ_NORMALIZED_ASCII); +} + +PHP_METHOD(Uri_Rfc3986_Uri, getRawPassword) +{ + uri_read_component(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_PASSWORD, URI_COMPONENT_READ_RAW); +} + +PHP_METHOD(Uri_Rfc3986_Uri, getHost) +{ + uri_read_component(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_HOST, URI_COMPONENT_READ_NORMALIZED_ASCII); +} + +PHP_METHOD(Uri_Rfc3986_Uri, getRawHost) +{ + uri_read_component(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_HOST, URI_COMPONENT_READ_RAW); +} + +PHP_METHOD(Uri_Rfc3986_Uri, getPort) +{ + uri_read_component(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_PORT, URI_COMPONENT_READ_NORMALIZED_ASCII); +} + +PHP_METHOD(Uri_Rfc3986_Uri, getPath) +{ + uri_read_component(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_PATH, URI_COMPONENT_READ_NORMALIZED_ASCII); +} + +PHP_METHOD(Uri_Rfc3986_Uri, getRawPath) +{ + uri_read_component(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_PATH, URI_COMPONENT_READ_RAW); +} + +PHP_METHOD(Uri_Rfc3986_Uri, getQuery) +{ + uri_read_component(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_QUERY, URI_COMPONENT_READ_NORMALIZED_ASCII); +} + +PHP_METHOD(Uri_Rfc3986_Uri, getRawQuery) +{ + uri_read_component(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_QUERY, URI_COMPONENT_READ_RAW); +} + +PHP_METHOD(Uri_Rfc3986_Uri, getFragment) +{ + uri_read_component(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_FRAGMENT, URI_COMPONENT_READ_NORMALIZED_ASCII); +} + +PHP_METHOD(Uri_Rfc3986_Uri, getRawFragment) +{ + uri_read_component(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_FRAGMENT, URI_COMPONENT_READ_RAW); +} + +static void throw_cannot_recompose_uri_to_string(zend_object *object) +{ + zend_throw_exception_ex(NULL, 0, "Cannot recompose %s to a string", ZSTR_VAL(object->ce->name)); +} + static void uri_equals(INTERNAL_FUNCTION_PARAMETERS, zend_object *that_object, zend_object *comparison_mode) { zend_object *this_object = Z_OBJ_P(ZEND_THIS); @@ -293,7 +422,7 @@ static void uri_equals(INTERNAL_FUNCTION_PARAMETERS, zend_object *that_object, z zend_string *this_str = this_internal_uri->handler->uri_to_string( this_internal_uri->uri, URI_RECOMPOSITION_NORMALIZED_ASCII, exclude_fragment); if (this_str == NULL) { - zend_throw_exception_ex(NULL, 0, "Cannot recompose %s to string", ZSTR_VAL(this_object->ce->name)); + throw_cannot_recompose_uri_to_string(this_object); RETURN_THROWS(); } @@ -301,7 +430,7 @@ static void uri_equals(INTERNAL_FUNCTION_PARAMETERS, zend_object *that_object, z that_internal_uri->uri, URI_RECOMPOSITION_NORMALIZED_ASCII, exclude_fragment); if (that_str == NULL) { zend_string_release(this_str); - zend_throw_exception_ex(NULL, 0, "Cannot recompose %s to string", ZSTR_VAL(that_object->ce->name)); + throw_cannot_recompose_uri_to_string(that_object); RETURN_THROWS(); } @@ -311,6 +440,99 @@ static void uri_equals(INTERNAL_FUNCTION_PARAMETERS, zend_object *that_object, z zend_string_release(that_str); } +PHP_METHOD(Uri_Rfc3986_Uri, equals) +{ + zend_object *that_object; + zend_object *comparison_mode = NULL; + + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_OBJ_OF_CLASS(that_object, uri_rfc3986_uri_ce) + Z_PARAM_OPTIONAL + Z_PARAM_OBJ_OF_CLASS(comparison_mode, uri_comparison_mode_ce) + ZEND_PARSE_PARAMETERS_END(); + + uri_equals(INTERNAL_FUNCTION_PARAM_PASSTHRU, that_object, comparison_mode); +} + +PHP_METHOD(Uri_Rfc3986_Uri, toRawString) +{ + ZEND_PARSE_PARAMETERS_NONE(); + + zend_object *this_object = Z_OBJ_P(ZEND_THIS); + uri_internal_t *internal_uri = uri_internal_from_obj(this_object); + URI_ASSERT_INITIALIZATION(internal_uri); + + zend_string *uri_str = internal_uri->handler->uri_to_string(internal_uri->uri, URI_RECOMPOSITION_RAW_ASCII, false); + if (uri_str == NULL) { + throw_cannot_recompose_uri_to_string(this_object); + RETURN_THROWS(); + } + + RETURN_STR(uri_str); +} + +PHP_METHOD(Uri_Rfc3986_Uri, toString) +{ + ZEND_PARSE_PARAMETERS_NONE(); + + zend_object *this_object = Z_OBJ_P(ZEND_THIS); + uri_internal_t *internal_uri = uri_internal_from_obj(this_object); + URI_ASSERT_INITIALIZATION(internal_uri); + + zend_string *uri_str = internal_uri->handler->uri_to_string(internal_uri->uri, URI_RECOMPOSITION_NORMALIZED_ASCII, false); + if (uri_str == NULL) { + throw_cannot_recompose_uri_to_string(this_object); + RETURN_THROWS(); + } + + RETURN_STR(uri_str); +} + +PHP_METHOD(Uri_Rfc3986_Uri, resolve) +{ + zend_string *uri_str; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_PATH_STR(uri_str) + ZEND_PARSE_PARAMETERS_END(); + + zend_object *this_object = Z_OBJ_P(ZEND_THIS); + uri_internal_t *internal_uri = uri_internal_from_obj(this_object); + URI_ASSERT_INITIALIZATION(internal_uri); + + php_uri_instantiate_uri(INTERNAL_FUNCTION_PARAM_PASSTHRU, internal_uri->handler, uri_str, this_object, true, false, NULL); +} + +PHP_METHOD(Uri_Rfc3986_Uri, __serialize) +{ + ZEND_PARSE_PARAMETERS_NONE(); + + zend_object *this_object = Z_OBJ_P(ZEND_THIS); + uri_internal_t *internal_uri = uri_internal_from_obj(this_object); + URI_ASSERT_INITIALIZATION(internal_uri); + + /* Serialize state: "uri" key in the first array */ + zend_string *uri_str = internal_uri->handler->uri_to_string(internal_uri->uri, URI_RECOMPOSITION_RAW_ASCII, false); + if (uri_str == NULL) { + throw_cannot_recompose_uri_to_string(this_object); + RETURN_THROWS(); + } + zval tmp; + ZVAL_STR(&tmp, uri_str); + + array_init(return_value); + + zval arr; + array_init(&arr); + zend_hash_str_add_new(Z_ARRVAL(arr), URI_SERIALIZED_PROPERTY_NAME, sizeof(URI_SERIALIZED_PROPERTY_NAME) - 1, &tmp); + zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &arr); + + /* Serialize regular properties: second array */ + ZVAL_ARR(&arr, this_object->handlers->get_properties(this_object)); + Z_TRY_ADDREF(arr); + zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &arr); +} + static void uri_unserialize(INTERNAL_FUNCTION_PARAMETERS, const char *handler_name) { HashTable *data; @@ -371,29 +593,33 @@ static void uri_unserialize(INTERNAL_FUNCTION_PARAMETERS, const char *handler_na } } -PHP_METHOD(Uri_WhatWg_Url, getScheme) +PHP_METHOD(Uri_Rfc3986_Uri, __unserialize) { - uri_read_component(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_SCHEME, URI_COMPONENT_READ_NORMALIZED_ASCII); + uri_unserialize(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PARSER_RFC3986); } -PHP_METHOD(Uri_WhatWg_Url, withScheme) +PHP_METHOD(Uri_Rfc3986_Uri, __debugInfo) { - uri_write_component_str(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_SCHEME); + ZEND_PARSE_PARAMETERS_NONE(); + + zend_object *object = Z_OBJ_P(ZEND_THIS); + + RETURN_ARR(uri_get_debug_properties(object)); } -PHP_METHOD(Uri_WhatWg_Url, getUsername) +PHP_METHOD(Uri_WhatWg_Url, getScheme) { - uri_read_component(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_USERNAME, URI_COMPONENT_READ_NORMALIZED_ASCII); + uri_read_component(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_SCHEME, URI_COMPONENT_READ_NORMALIZED_ASCII); } -PHP_METHOD(Uri_WhatWg_Url, withUsername) +PHP_METHOD(Uri_WhatWg_Url, withScheme) { - uri_write_component_str_or_null(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_USERNAME); + uri_write_component_str(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_SCHEME); } -PHP_METHOD(Uri_WhatWg_Url, getPassword) +PHP_METHOD(Uri_WhatWg_Url, withUsername) { - uri_read_component(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_PASSWORD, URI_COMPONENT_READ_NORMALIZED_ASCII); + uri_write_component_str_or_null(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_USERNAME); } PHP_METHOD(Uri_WhatWg_Url, withPassword) @@ -416,41 +642,21 @@ PHP_METHOD(Uri_WhatWg_Url, withHost) uri_write_component_str_or_null(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_HOST); } -PHP_METHOD(Uri_WhatWg_Url, getPort) -{ - uri_read_component(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_PORT, URI_COMPONENT_READ_NORMALIZED_ASCII); -} - PHP_METHOD(Uri_WhatWg_Url, withPort) { uri_write_component_long_or_null(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_PORT); } -PHP_METHOD(Uri_WhatWg_Url, getPath) -{ - uri_read_component(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_PATH, URI_COMPONENT_READ_NORMALIZED_ASCII); -} - PHP_METHOD(Uri_WhatWg_Url, withPath) { uri_write_component_str(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_PATH); } -PHP_METHOD(Uri_WhatWg_Url, getQuery) -{ - uri_read_component(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_QUERY, URI_COMPONENT_READ_NORMALIZED_ASCII); -} - PHP_METHOD(Uri_WhatWg_Url, withQuery) { uri_write_component_str_or_null(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_QUERY); } -PHP_METHOD(Uri_WhatWg_Url, getFragment) -{ - uri_read_component(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_FRAGMENT, URI_COMPONENT_READ_NORMALIZED_ASCII); -} - PHP_METHOD(Uri_WhatWg_Url, withFragment) { uri_write_component_str_or_null(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_FRAGMENT); @@ -521,7 +727,7 @@ PHP_METHOD(Uri_WhatWg_Url, __serialize) /* Serialize state: "uri" key in the first array */ zend_string *uri_str = internal_uri->handler->uri_to_string(internal_uri->uri, URI_RECOMPOSITION_RAW_ASCII, false); if (uri_str == NULL) { - zend_throw_exception_ex(NULL, 0, "Cannot recompose %s to string", ZSTR_VAL(this_object->ce->name)); + throw_cannot_recompose_uri_to_string(this_object); RETURN_THROWS(); } zval tmp; @@ -629,6 +835,9 @@ zend_result uri_handler_register(const uri_handler_t *uri_handler) static PHP_MINIT_FUNCTION(uri) { + uri_rfc3986_uri_ce = register_class_Uri_Rfc3986_Uri(); + php_uri_implementation_set_object_handlers(uri_rfc3986_uri_ce, &uri_rfc3986_uri_object_handlers); + uri_whatwg_url_ce = register_class_Uri_WhatWg_Url(); php_uri_implementation_set_object_handlers(uri_whatwg_url_ce, &uri_whatwg_uri_object_handlers); @@ -641,6 +850,10 @@ static PHP_MINIT_FUNCTION(uri) zend_hash_init(&uri_handlers, 4, NULL, NULL, true); + if (PHP_MINIT(uri_uriparser)(INIT_FUNC_ARGS_PASSTHRU) == FAILURE) { + return FAILURE; + } + if (uri_handler_register(&lexbor_uri_handler) == FAILURE) { return FAILURE; } diff --git a/ext/uri/php_uri.stub.php b/ext/uri/php_uri.stub.php index ef49e4ba6f968..9fbd40d98e5e9 100644 --- a/ext/uri/php_uri.stub.php +++ b/ext/uri/php_uri.stub.php @@ -20,6 +20,64 @@ enum UriComparisonMode } } +namespace Uri\Rfc3986 { + /** @strict-properties */ + final readonly class Uri + { + public static function parse(string $uri, ?\Uri\Rfc3986\Uri $baseUrl = null): ?static {} + + public function __construct(string $uri, ?\Uri\Rfc3986\Uri $baseUrl = null) {} + + public function getScheme(): ?string {} + + public function getRawScheme(): ?string {} + + public function getUserInfo(): ?string {} + + public function getRawUserInfo(): ?string {} + + public function getUsername(): ?string {} + + public function getRawUsername(): ?string {} + + public function getPassword(): ?string {} + + public function getRawPassword(): ?string {} + + public function getHost(): ?string {} + + public function getRawHost(): ?string {} + + public function getPort(): ?int {} + + public function getPath(): string {} + + public function getRawPath(): string {} + + public function getQuery(): ?string {} + + public function getRawQuery(): ?string {} + + public function getFragment(): ?string {} + + public function getRawFragment(): ?string {} + + public function equals(\Uri\Rfc3986\Uri $uri, \Uri\UriComparisonMode $comparisonMode = \Uri\UriComparisonMode::ExcludeFragment): bool {} + + public function toString(): string {} + + public function toRawString(): string {} + + public function resolve(string $uri): static {} + + public function __serialize(): array {} + + public function __unserialize(array $data): void {} + + public function __debugInfo(): array {} + } +} + namespace Uri\WhatWg { /** @strict-properties */ class InvalidUrlException extends \Uri\InvalidUriException @@ -85,10 +143,12 @@ public function getScheme(): string {} public function withScheme(string $scheme): static {} + /** @implementation-alias Uri\Rfc3986\Uri::getUsername */ public function getUsername(): ?string {} public function withUsername(?string $username): static {} + /** @implementation-alias Uri\Rfc3986\Uri::getPassword */ public function getPassword(): ?string {} public function withPassword(#[\SensitiveParameter] ?string $password): static {} @@ -99,18 +159,22 @@ public function getUnicodeHost(): ?string {} public function withHost(?string $host): static {} + /** @implementation-alias Uri\Rfc3986\Uri::getPort */ public function getPort(): ?int {} public function withPort(?int $port): static {} + /** @implementation-alias Uri\Rfc3986\Uri::getPath */ public function getPath(): string {} public function withPath(string $path): static {} + /** @implementation-alias Uri\Rfc3986\Uri::getQuery */ public function getQuery(): ?string {} public function withQuery(?string $query): static {} + /** @implementation-alias Uri\Rfc3986\Uri::getFragment */ public function getFragment(): ?string {} public function withFragment(?string $fragment): static {} diff --git a/ext/uri/php_uri_arginfo.h b/ext/uri/php_uri_arginfo.h index 0ae755a9f70dc..65630f113a3d3 100644 --- a/ext/uri/php_uri_arginfo.h +++ b/ext/uri/php_uri_arginfo.h @@ -1,5 +1,74 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 1945c28deef13c2af552b18c2a5a6c7798d4aeec */ + * Stub hash: e2c448000b1e00485bc988f073ea61dfc984e953 */ + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_Rfc3986_Uri_parse, 0, 1, IS_STATIC, 1) + ZEND_ARG_TYPE_INFO(0, uri, IS_STRING, 0) + ZEND_ARG_OBJ_INFO_WITH_DEFAULT_VALUE(0, baseUrl, Uri\\Rfc3986\\\125ri, 1, "null") +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Uri_Rfc3986_Uri___construct, 0, 0, 1) + ZEND_ARG_TYPE_INFO(0, uri, IS_STRING, 0) + ZEND_ARG_OBJ_INFO_WITH_DEFAULT_VALUE(0, baseUrl, Uri\\Rfc3986\\\125ri, 1, "null") +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_Rfc3986_Uri_getScheme, 0, 0, IS_STRING, 1) +ZEND_END_ARG_INFO() + +#define arginfo_class_Uri_Rfc3986_Uri_getRawScheme arginfo_class_Uri_Rfc3986_Uri_getScheme + +#define arginfo_class_Uri_Rfc3986_Uri_getUserInfo arginfo_class_Uri_Rfc3986_Uri_getScheme + +#define arginfo_class_Uri_Rfc3986_Uri_getRawUserInfo arginfo_class_Uri_Rfc3986_Uri_getScheme + +#define arginfo_class_Uri_Rfc3986_Uri_getUsername arginfo_class_Uri_Rfc3986_Uri_getScheme + +#define arginfo_class_Uri_Rfc3986_Uri_getRawUsername arginfo_class_Uri_Rfc3986_Uri_getScheme + +#define arginfo_class_Uri_Rfc3986_Uri_getPassword arginfo_class_Uri_Rfc3986_Uri_getScheme + +#define arginfo_class_Uri_Rfc3986_Uri_getRawPassword arginfo_class_Uri_Rfc3986_Uri_getScheme + +#define arginfo_class_Uri_Rfc3986_Uri_getHost arginfo_class_Uri_Rfc3986_Uri_getScheme + +#define arginfo_class_Uri_Rfc3986_Uri_getRawHost arginfo_class_Uri_Rfc3986_Uri_getScheme + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_Rfc3986_Uri_getPort, 0, 0, IS_LONG, 1) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_Rfc3986_Uri_getPath, 0, 0, IS_STRING, 0) +ZEND_END_ARG_INFO() + +#define arginfo_class_Uri_Rfc3986_Uri_getRawPath arginfo_class_Uri_Rfc3986_Uri_getPath + +#define arginfo_class_Uri_Rfc3986_Uri_getQuery arginfo_class_Uri_Rfc3986_Uri_getScheme + +#define arginfo_class_Uri_Rfc3986_Uri_getRawQuery arginfo_class_Uri_Rfc3986_Uri_getScheme + +#define arginfo_class_Uri_Rfc3986_Uri_getFragment arginfo_class_Uri_Rfc3986_Uri_getScheme + +#define arginfo_class_Uri_Rfc3986_Uri_getRawFragment arginfo_class_Uri_Rfc3986_Uri_getScheme + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_Rfc3986_Uri_equals, 0, 1, _IS_BOOL, 0) + ZEND_ARG_OBJ_INFO(0, uri, Uri\\Rfc3986\\\125ri, 0) + ZEND_ARG_OBJ_INFO_WITH_DEFAULT_VALUE(0, comparisonMode, Uri\\\125riComparisonMode, 0, "Uri\\UriComparisonMode::ExcludeFragment") +ZEND_END_ARG_INFO() + +#define arginfo_class_Uri_Rfc3986_Uri_toString arginfo_class_Uri_Rfc3986_Uri_getPath + +#define arginfo_class_Uri_Rfc3986_Uri_toRawString arginfo_class_Uri_Rfc3986_Uri_getPath + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_Rfc3986_Uri_resolve, 0, 1, IS_STATIC, 0) + ZEND_ARG_TYPE_INFO(0, uri, IS_STRING, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_Rfc3986_Uri___serialize, 0, 0, IS_ARRAY, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_Rfc3986_Uri___unserialize, 0, 1, IS_VOID, 0) + ZEND_ARG_TYPE_INFO(0, data, IS_ARRAY, 0) +ZEND_END_ARG_INFO() + +#define arginfo_class_Uri_Rfc3986_Uri___debugInfo arginfo_class_Uri_Rfc3986_Uri___serialize ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Uri_WhatWg_InvalidUrlException___construct, 0, 0, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, message, IS_STRING, 0, "\"\"") @@ -26,54 +95,51 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Uri_WhatWg_Url___construct, 0, 0, 1) ZEND_ARG_INFO_WITH_DEFAULT_VALUE(1, softErrors, "null") ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_WhatWg_Url_getScheme, 0, 0, IS_STRING, 0) -ZEND_END_ARG_INFO() +#define arginfo_class_Uri_WhatWg_Url_getScheme arginfo_class_Uri_Rfc3986_Uri_getPath ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_WhatWg_Url_withScheme, 0, 1, IS_STATIC, 0) ZEND_ARG_TYPE_INFO(0, scheme, IS_STRING, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_WhatWg_Url_getUsername, 0, 0, IS_STRING, 1) -ZEND_END_ARG_INFO() +#define arginfo_class_Uri_WhatWg_Url_getUsername arginfo_class_Uri_Rfc3986_Uri_getScheme ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_WhatWg_Url_withUsername, 0, 1, IS_STATIC, 0) ZEND_ARG_TYPE_INFO(0, username, IS_STRING, 1) ZEND_END_ARG_INFO() -#define arginfo_class_Uri_WhatWg_Url_getPassword arginfo_class_Uri_WhatWg_Url_getUsername +#define arginfo_class_Uri_WhatWg_Url_getPassword arginfo_class_Uri_Rfc3986_Uri_getScheme ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_WhatWg_Url_withPassword, 0, 1, IS_STATIC, 0) ZEND_ARG_TYPE_INFO(0, password, IS_STRING, 1) ZEND_END_ARG_INFO() -#define arginfo_class_Uri_WhatWg_Url_getAsciiHost arginfo_class_Uri_WhatWg_Url_getUsername +#define arginfo_class_Uri_WhatWg_Url_getAsciiHost arginfo_class_Uri_Rfc3986_Uri_getScheme -#define arginfo_class_Uri_WhatWg_Url_getUnicodeHost arginfo_class_Uri_WhatWg_Url_getUsername +#define arginfo_class_Uri_WhatWg_Url_getUnicodeHost arginfo_class_Uri_Rfc3986_Uri_getScheme ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_WhatWg_Url_withHost, 0, 1, IS_STATIC, 0) ZEND_ARG_TYPE_INFO(0, host, IS_STRING, 1) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_WhatWg_Url_getPort, 0, 0, IS_LONG, 1) -ZEND_END_ARG_INFO() +#define arginfo_class_Uri_WhatWg_Url_getPort arginfo_class_Uri_Rfc3986_Uri_getPort ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_WhatWg_Url_withPort, 0, 1, IS_STATIC, 0) ZEND_ARG_TYPE_INFO(0, port, IS_LONG, 1) ZEND_END_ARG_INFO() -#define arginfo_class_Uri_WhatWg_Url_getPath arginfo_class_Uri_WhatWg_Url_getScheme +#define arginfo_class_Uri_WhatWg_Url_getPath arginfo_class_Uri_Rfc3986_Uri_getPath ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_WhatWg_Url_withPath, 0, 1, IS_STATIC, 0) ZEND_ARG_TYPE_INFO(0, path, IS_STRING, 0) ZEND_END_ARG_INFO() -#define arginfo_class_Uri_WhatWg_Url_getQuery arginfo_class_Uri_WhatWg_Url_getUsername +#define arginfo_class_Uri_WhatWg_Url_getQuery arginfo_class_Uri_Rfc3986_Uri_getScheme ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_WhatWg_Url_withQuery, 0, 1, IS_STATIC, 0) ZEND_ARG_TYPE_INFO(0, query, IS_STRING, 1) ZEND_END_ARG_INFO() -#define arginfo_class_Uri_WhatWg_Url_getFragment arginfo_class_Uri_WhatWg_Url_getUsername +#define arginfo_class_Uri_WhatWg_Url_getFragment arginfo_class_Uri_Rfc3986_Uri_getScheme ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_WhatWg_Url_withFragment, 0, 1, IS_STATIC, 0) ZEND_ARG_TYPE_INFO(0, fragment, IS_STRING, 1) @@ -84,44 +150,61 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_WhatWg_Url_equals, 0, ZEND_ARG_OBJ_INFO_WITH_DEFAULT_VALUE(0, comparisonMode, Uri\\\125riComparisonMode, 0, "Uri\\UriComparisonMode::ExcludeFragment") ZEND_END_ARG_INFO() -#define arginfo_class_Uri_WhatWg_Url_toAsciiString arginfo_class_Uri_WhatWg_Url_getScheme +#define arginfo_class_Uri_WhatWg_Url_toAsciiString arginfo_class_Uri_Rfc3986_Uri_getPath -#define arginfo_class_Uri_WhatWg_Url_toUnicodeString arginfo_class_Uri_WhatWg_Url_getScheme +#define arginfo_class_Uri_WhatWg_Url_toUnicodeString arginfo_class_Uri_Rfc3986_Uri_getPath ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_WhatWg_Url_resolve, 0, 1, IS_STATIC, 0) ZEND_ARG_TYPE_INFO(0, uri, IS_STRING, 0) ZEND_ARG_INFO_WITH_DEFAULT_VALUE(1, softErrors, "null") ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_WhatWg_Url___serialize, 0, 0, IS_ARRAY, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_WhatWg_Url___unserialize, 0, 1, IS_VOID, 0) - ZEND_ARG_TYPE_INFO(0, data, IS_ARRAY, 0) -ZEND_END_ARG_INFO() - -#define arginfo_class_Uri_WhatWg_Url___debugInfo arginfo_class_Uri_WhatWg_Url___serialize - +#define arginfo_class_Uri_WhatWg_Url___serialize arginfo_class_Uri_Rfc3986_Uri___serialize + +#define arginfo_class_Uri_WhatWg_Url___unserialize arginfo_class_Uri_Rfc3986_Uri___unserialize + +#define arginfo_class_Uri_WhatWg_Url___debugInfo arginfo_class_Uri_Rfc3986_Uri___serialize + +ZEND_METHOD(Uri_Rfc3986_Uri, parse); +ZEND_METHOD(Uri_Rfc3986_Uri, __construct); +ZEND_METHOD(Uri_Rfc3986_Uri, getScheme); +ZEND_METHOD(Uri_Rfc3986_Uri, getRawScheme); +ZEND_METHOD(Uri_Rfc3986_Uri, getUserInfo); +ZEND_METHOD(Uri_Rfc3986_Uri, getRawUserInfo); +ZEND_METHOD(Uri_Rfc3986_Uri, getUsername); +ZEND_METHOD(Uri_Rfc3986_Uri, getRawUsername); +ZEND_METHOD(Uri_Rfc3986_Uri, getPassword); +ZEND_METHOD(Uri_Rfc3986_Uri, getRawPassword); +ZEND_METHOD(Uri_Rfc3986_Uri, getHost); +ZEND_METHOD(Uri_Rfc3986_Uri, getRawHost); +ZEND_METHOD(Uri_Rfc3986_Uri, getPort); +ZEND_METHOD(Uri_Rfc3986_Uri, getPath); +ZEND_METHOD(Uri_Rfc3986_Uri, getRawPath); +ZEND_METHOD(Uri_Rfc3986_Uri, getQuery); +ZEND_METHOD(Uri_Rfc3986_Uri, getRawQuery); +ZEND_METHOD(Uri_Rfc3986_Uri, getFragment); +ZEND_METHOD(Uri_Rfc3986_Uri, getRawFragment); +ZEND_METHOD(Uri_Rfc3986_Uri, equals); +ZEND_METHOD(Uri_Rfc3986_Uri, toString); +ZEND_METHOD(Uri_Rfc3986_Uri, toRawString); +ZEND_METHOD(Uri_Rfc3986_Uri, resolve); +ZEND_METHOD(Uri_Rfc3986_Uri, __serialize); +ZEND_METHOD(Uri_Rfc3986_Uri, __unserialize); +ZEND_METHOD(Uri_Rfc3986_Uri, __debugInfo); ZEND_METHOD(Uri_WhatWg_InvalidUrlException, __construct); ZEND_METHOD(Uri_WhatWg_UrlValidationError, __construct); ZEND_METHOD(Uri_WhatWg_Url, parse); ZEND_METHOD(Uri_WhatWg_Url, __construct); ZEND_METHOD(Uri_WhatWg_Url, getScheme); ZEND_METHOD(Uri_WhatWg_Url, withScheme); -ZEND_METHOD(Uri_WhatWg_Url, getUsername); ZEND_METHOD(Uri_WhatWg_Url, withUsername); -ZEND_METHOD(Uri_WhatWg_Url, getPassword); ZEND_METHOD(Uri_WhatWg_Url, withPassword); ZEND_METHOD(Uri_WhatWg_Url, getAsciiHost); ZEND_METHOD(Uri_WhatWg_Url, getUnicodeHost); ZEND_METHOD(Uri_WhatWg_Url, withHost); -ZEND_METHOD(Uri_WhatWg_Url, getPort); ZEND_METHOD(Uri_WhatWg_Url, withPort); -ZEND_METHOD(Uri_WhatWg_Url, getPath); ZEND_METHOD(Uri_WhatWg_Url, withPath); -ZEND_METHOD(Uri_WhatWg_Url, getQuery); ZEND_METHOD(Uri_WhatWg_Url, withQuery); -ZEND_METHOD(Uri_WhatWg_Url, getFragment); ZEND_METHOD(Uri_WhatWg_Url, withFragment); ZEND_METHOD(Uri_WhatWg_Url, equals); ZEND_METHOD(Uri_WhatWg_Url, toAsciiString); @@ -131,6 +214,36 @@ ZEND_METHOD(Uri_WhatWg_Url, __serialize); ZEND_METHOD(Uri_WhatWg_Url, __unserialize); ZEND_METHOD(Uri_WhatWg_Url, __debugInfo); +static const zend_function_entry class_Uri_Rfc3986_Uri_methods[] = { + ZEND_ME(Uri_Rfc3986_Uri, parse, arginfo_class_Uri_Rfc3986_Uri_parse, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) + ZEND_ME(Uri_Rfc3986_Uri, __construct, arginfo_class_Uri_Rfc3986_Uri___construct, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_Rfc3986_Uri, getScheme, arginfo_class_Uri_Rfc3986_Uri_getScheme, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_Rfc3986_Uri, getRawScheme, arginfo_class_Uri_Rfc3986_Uri_getRawScheme, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_Rfc3986_Uri, getUserInfo, arginfo_class_Uri_Rfc3986_Uri_getUserInfo, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_Rfc3986_Uri, getRawUserInfo, arginfo_class_Uri_Rfc3986_Uri_getRawUserInfo, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_Rfc3986_Uri, getUsername, arginfo_class_Uri_Rfc3986_Uri_getUsername, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_Rfc3986_Uri, getRawUsername, arginfo_class_Uri_Rfc3986_Uri_getRawUsername, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_Rfc3986_Uri, getPassword, arginfo_class_Uri_Rfc3986_Uri_getPassword, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_Rfc3986_Uri, getRawPassword, arginfo_class_Uri_Rfc3986_Uri_getRawPassword, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_Rfc3986_Uri, getHost, arginfo_class_Uri_Rfc3986_Uri_getHost, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_Rfc3986_Uri, getRawHost, arginfo_class_Uri_Rfc3986_Uri_getRawHost, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_Rfc3986_Uri, getPort, arginfo_class_Uri_Rfc3986_Uri_getPort, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_Rfc3986_Uri, getPath, arginfo_class_Uri_Rfc3986_Uri_getPath, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_Rfc3986_Uri, getRawPath, arginfo_class_Uri_Rfc3986_Uri_getRawPath, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_Rfc3986_Uri, getQuery, arginfo_class_Uri_Rfc3986_Uri_getQuery, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_Rfc3986_Uri, getRawQuery, arginfo_class_Uri_Rfc3986_Uri_getRawQuery, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_Rfc3986_Uri, getFragment, arginfo_class_Uri_Rfc3986_Uri_getFragment, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_Rfc3986_Uri, getRawFragment, arginfo_class_Uri_Rfc3986_Uri_getRawFragment, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_Rfc3986_Uri, equals, arginfo_class_Uri_Rfc3986_Uri_equals, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_Rfc3986_Uri, toString, arginfo_class_Uri_Rfc3986_Uri_toString, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_Rfc3986_Uri, toRawString, arginfo_class_Uri_Rfc3986_Uri_toRawString, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_Rfc3986_Uri, resolve, arginfo_class_Uri_Rfc3986_Uri_resolve, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_Rfc3986_Uri, __serialize, arginfo_class_Uri_Rfc3986_Uri___serialize, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_Rfc3986_Uri, __unserialize, arginfo_class_Uri_Rfc3986_Uri___unserialize, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_Rfc3986_Uri, __debugInfo, arginfo_class_Uri_Rfc3986_Uri___debugInfo, ZEND_ACC_PUBLIC) + ZEND_FE_END +}; + static const zend_function_entry class_Uri_WhatWg_InvalidUrlException_methods[] = { ZEND_ME(Uri_WhatWg_InvalidUrlException, __construct, arginfo_class_Uri_WhatWg_InvalidUrlException___construct, ZEND_ACC_PUBLIC) ZEND_FE_END @@ -146,20 +259,20 @@ static const zend_function_entry class_Uri_WhatWg_Url_methods[] = { ZEND_ME(Uri_WhatWg_Url, __construct, arginfo_class_Uri_WhatWg_Url___construct, ZEND_ACC_PUBLIC) ZEND_ME(Uri_WhatWg_Url, getScheme, arginfo_class_Uri_WhatWg_Url_getScheme, ZEND_ACC_PUBLIC) ZEND_ME(Uri_WhatWg_Url, withScheme, arginfo_class_Uri_WhatWg_Url_withScheme, ZEND_ACC_PUBLIC) - ZEND_ME(Uri_WhatWg_Url, getUsername, arginfo_class_Uri_WhatWg_Url_getUsername, ZEND_ACC_PUBLIC) + ZEND_RAW_FENTRY("getUsername", zim_Uri_Rfc3986_Uri_getUsername, arginfo_class_Uri_WhatWg_Url_getUsername, ZEND_ACC_PUBLIC, NULL, NULL) ZEND_ME(Uri_WhatWg_Url, withUsername, arginfo_class_Uri_WhatWg_Url_withUsername, ZEND_ACC_PUBLIC) - ZEND_ME(Uri_WhatWg_Url, getPassword, arginfo_class_Uri_WhatWg_Url_getPassword, ZEND_ACC_PUBLIC) + ZEND_RAW_FENTRY("getPassword", zim_Uri_Rfc3986_Uri_getPassword, arginfo_class_Uri_WhatWg_Url_getPassword, ZEND_ACC_PUBLIC, NULL, NULL) ZEND_ME(Uri_WhatWg_Url, withPassword, arginfo_class_Uri_WhatWg_Url_withPassword, ZEND_ACC_PUBLIC) ZEND_ME(Uri_WhatWg_Url, getAsciiHost, arginfo_class_Uri_WhatWg_Url_getAsciiHost, ZEND_ACC_PUBLIC) ZEND_ME(Uri_WhatWg_Url, getUnicodeHost, arginfo_class_Uri_WhatWg_Url_getUnicodeHost, ZEND_ACC_PUBLIC) ZEND_ME(Uri_WhatWg_Url, withHost, arginfo_class_Uri_WhatWg_Url_withHost, ZEND_ACC_PUBLIC) - ZEND_ME(Uri_WhatWg_Url, getPort, arginfo_class_Uri_WhatWg_Url_getPort, ZEND_ACC_PUBLIC) + ZEND_RAW_FENTRY("getPort", zim_Uri_Rfc3986_Uri_getPort, arginfo_class_Uri_WhatWg_Url_getPort, ZEND_ACC_PUBLIC, NULL, NULL) ZEND_ME(Uri_WhatWg_Url, withPort, arginfo_class_Uri_WhatWg_Url_withPort, ZEND_ACC_PUBLIC) - ZEND_ME(Uri_WhatWg_Url, getPath, arginfo_class_Uri_WhatWg_Url_getPath, ZEND_ACC_PUBLIC) + ZEND_RAW_FENTRY("getPath", zim_Uri_Rfc3986_Uri_getPath, arginfo_class_Uri_WhatWg_Url_getPath, ZEND_ACC_PUBLIC, NULL, NULL) ZEND_ME(Uri_WhatWg_Url, withPath, arginfo_class_Uri_WhatWg_Url_withPath, ZEND_ACC_PUBLIC) - ZEND_ME(Uri_WhatWg_Url, getQuery, arginfo_class_Uri_WhatWg_Url_getQuery, ZEND_ACC_PUBLIC) + ZEND_RAW_FENTRY("getQuery", zim_Uri_Rfc3986_Uri_getQuery, arginfo_class_Uri_WhatWg_Url_getQuery, ZEND_ACC_PUBLIC, NULL, NULL) ZEND_ME(Uri_WhatWg_Url, withQuery, arginfo_class_Uri_WhatWg_Url_withQuery, ZEND_ACC_PUBLIC) - ZEND_ME(Uri_WhatWg_Url, getFragment, arginfo_class_Uri_WhatWg_Url_getFragment, ZEND_ACC_PUBLIC) + ZEND_RAW_FENTRY("getFragment", zim_Uri_Rfc3986_Uri_getFragment, arginfo_class_Uri_WhatWg_Url_getFragment, ZEND_ACC_PUBLIC, NULL, NULL) ZEND_ME(Uri_WhatWg_Url, withFragment, arginfo_class_Uri_WhatWg_Url_withFragment, ZEND_ACC_PUBLIC) ZEND_ME(Uri_WhatWg_Url, equals, arginfo_class_Uri_WhatWg_Url_equals, ZEND_ACC_PUBLIC) ZEND_ME(Uri_WhatWg_Url, toAsciiString, arginfo_class_Uri_WhatWg_Url_toAsciiString, ZEND_ACC_PUBLIC) @@ -202,6 +315,16 @@ static zend_class_entry *register_class_Uri_UriComparisonMode(void) return class_entry; } +static zend_class_entry *register_class_Uri_Rfc3986_Uri(void) +{ + zend_class_entry ce, *class_entry; + + INIT_NS_CLASS_ENTRY(ce, "Uri\\Rfc3986", "Uri", class_Uri_Rfc3986_Uri_methods); + class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_READONLY_CLASS); + + return class_entry; +} + static zend_class_entry *register_class_Uri_WhatWg_InvalidUrlException(zend_class_entry *class_entry_Uri_InvalidUriException) { zend_class_entry ce, *class_entry; diff --git a/ext/uri/php_uri_common.c b/ext/uri/php_uri_common.c index 9128b942e57b8..29de370bd56b6 100644 --- a/ext/uri/php_uri_common.c +++ b/ext/uri/php_uri_common.c @@ -76,8 +76,7 @@ void uri_read_component(INTERNAL_FUNCTION_PARAMETERS, uri_property_name_t proper ZEND_ASSERT(property_handler != NULL); if (UNEXPECTED(property_handler->read_func(internal_uri, component_read_mode, return_value) == FAILURE)) { - zend_throw_error(NULL, "%s::$%s property cannot be retrieved", ZSTR_VAL(Z_OBJ_P(ZEND_THIS)->ce->name), - ZSTR_VAL(get_known_string_by_property_name(property_name))); + zend_throw_error(NULL, "The %s component cannot be retrieved", ZSTR_VAL(get_known_string_by_property_name(property_name))); RETURN_THROWS(); } } @@ -91,14 +90,11 @@ static void uri_write_component_ex(INTERNAL_FUNCTION_PARAMETERS, uri_property_na ZEND_ASSERT(property_handler != NULL); zend_object *new_object = uri_clone_obj_handler(Z_OBJ_P(ZEND_THIS)); - if (UNEXPECTED(EG(exception) != NULL)) { - zend_object_release(new_object); - RETURN_THROWS(); - } + ZEND_ASSERT(new_object != NULL); uri_internal_t *new_internal_uri = uri_internal_from_obj(new_object); URI_ASSERT_INITIALIZATION(new_internal_uri); - if (property_handler->write_func == NULL) { + if (UNEXPECTED(property_handler->write_func == NULL)) { zend_readonly_property_modification_error_ex(ZSTR_VAL(Z_OBJ_P(ZEND_THIS)->ce->name), ZSTR_VAL(get_known_string_by_property_name(property_name))); zend_object_release(new_object); @@ -107,7 +103,7 @@ static void uri_write_component_ex(INTERNAL_FUNCTION_PARAMETERS, uri_property_na zval errors; ZVAL_UNDEF(&errors); - if (property_handler->write_func(new_internal_uri, property_zv, &errors) == FAILURE) { + if (UNEXPECTED(property_handler->write_func(new_internal_uri, property_zv, &errors) == FAILURE)) { zval_ptr_dtor(&errors); zend_object_release(new_object); RETURN_THROWS(); diff --git a/ext/uri/php_uri_common.h b/ext/uri/php_uri_common.h index 1aee1cd512472..8e5b0c2d2279f 100644 --- a/ext/uri/php_uri_common.h +++ b/ext/uri/php_uri_common.h @@ -17,6 +17,8 @@ #ifndef PHP_URI_COMMON_H #define PHP_URI_COMMON_H +extern zend_class_entry *uri_rfc3986_uri_ce; +extern zend_object_handlers uri_rfc3986_uri_object_handlers; extern zend_class_entry *uri_whatwg_url_ce; extern zend_object_handlers uri_whatwg_uri_object_handlers; extern zend_class_entry *uri_comparison_mode_ce; @@ -121,6 +123,7 @@ static inline uri_internal_t *uri_internal_from_obj(const zend_object *object) { #define Z_URI_OBJECT_P(zv) uri_object_from_obj(Z_OBJ_P((zv))) #define Z_URI_INTERNAL_P(zv) uri_internal_from_obj(Z_OBJ_P((zv))) +#define URI_PARSER_RFC3986 "Uri\\Rfc3986\\Uri" #define URI_PARSER_WHATWG "Uri\\WhatWg\\Url" #define URI_SERIALIZED_PROPERTY_NAME "uri" diff --git a/ext/uri/php_uriparser.c b/ext/uri/php_uriparser.c new file mode 100644 index 0000000000000..8c84f6f3a285a --- /dev/null +++ b/ext/uri/php_uriparser.c @@ -0,0 +1,415 @@ +/* + +----------------------------------------------------------------------+ + | Copyright (c) The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | https://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Máté Kocsis | + +----------------------------------------------------------------------+ +*/ + +#include "php.h" +#include "php_uriparser.h" +#include "php_uri_common.h" +#include "Zend/zend_smart_str.h" +#include "Zend/zend_exceptions.h" + +static void uriparser_free_uri(void *uri); +static void throw_invalid_uri_exception(void); + +static inline size_t get_text_range_length(const UriTextRangeA *range) +{ + return range->afterLast - range->first; +} + +ZEND_ATTRIBUTE_NONNULL static void uriparser_copy_uri(UriUriA *new_uriparser_uri, const UriUriA *uriparser_uri) +{ + int result = uriCopyUriA(new_uriparser_uri, uriparser_uri); + ZEND_ASSERT(result == URI_SUCCESS); +} + +ZEND_ATTRIBUTE_NONNULL static UriUriA *get_normalized_uri(uriparser_uris_t *uriparser_uris) { + if (!uriparser_uris->normalized_uri_initialized) { + uriparser_copy_uri(&uriparser_uris->normalized_uri, &uriparser_uris->uri); + int result = uriNormalizeSyntaxExA(&uriparser_uris->normalized_uri, (unsigned int)-1); + ZEND_ASSERT(result == URI_SUCCESS); + uriparser_uris->normalized_uri_initialized = true; + } + + return &uriparser_uris->normalized_uri; +} + +ZEND_ATTRIBUTE_NONNULL static UriUriA *uriparser_read_uri(uriparser_uris_t *uriparser_uris, uri_component_read_mode_t read_mode) +{ + switch (read_mode) { + case URI_COMPONENT_READ_RAW: + return &uriparser_uris->uri; + case URI_COMPONENT_READ_NORMALIZED_ASCII: + ZEND_FALLTHROUGH; + case URI_COMPONENT_READ_NORMALIZED_UNICODE: + return get_normalized_uri(uriparser_uris); + EMPTY_SWITCH_DEFAULT_CASE() + } +} + +ZEND_ATTRIBUTE_NONNULL static zend_result uriparser_read_scheme(const uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval) +{ + UriUriA *uriparser_uri = uriparser_read_uri(internal_uri->uri, read_mode); + ZEND_ASSERT(uriparser_uri != NULL); + + if (uriparser_uri->scheme.first != NULL && uriparser_uri->scheme.afterLast != NULL) { + zend_string *str = zend_string_init(uriparser_uri->scheme.first, get_text_range_length(&uriparser_uri->scheme), false); + ZVAL_NEW_STR(retval, str); + } else { + ZVAL_NULL(retval); + } + + return SUCCESS; +} + +ZEND_ATTRIBUTE_NONNULL zend_result uriparser_read_userinfo(const uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval) +{ + UriUriA *uriparser_uri = uriparser_read_uri(internal_uri->uri, read_mode); + ZEND_ASSERT(uriparser_uri != NULL); + + if (uriparser_uri->userInfo.first != NULL && uriparser_uri->userInfo.afterLast != NULL) { + ZVAL_STRINGL(retval, uriparser_uri->userInfo.first, get_text_range_length(&uriparser_uri->userInfo)); + } else { + ZVAL_NULL(retval); + } + + return SUCCESS; +} + +ZEND_ATTRIBUTE_NONNULL static zend_result uriparser_read_username(const uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval) +{ + UriUriA *uriparser_uri = uriparser_read_uri(internal_uri->uri, read_mode); + ZEND_ASSERT(uriparser_uri != NULL); + + if (uriparser_uri->userInfo.first != NULL && uriparser_uri->userInfo.afterLast != NULL) { + size_t length = get_text_range_length(&uriparser_uri->userInfo); + const char *c = memchr(uriparser_uri->userInfo.first, ':', length); + + if (c == NULL && length > 0) { + ZVAL_STRINGL(retval, uriparser_uri->userInfo.first, length); + } else if (c != NULL && c - uriparser_uri->userInfo.first > 0) { + ZVAL_STRINGL(retval, uriparser_uri->userInfo.first, c - uriparser_uri->userInfo.first); + } else { + ZVAL_NULL(retval); + } + } else { + ZVAL_NULL(retval); + } + + return SUCCESS; +} + +ZEND_ATTRIBUTE_NONNULL static zend_result uriparser_read_password(const uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval) +{ + UriUriA *uriparser_uri = uriparser_read_uri(internal_uri->uri, read_mode); + ZEND_ASSERT(uriparser_uri != NULL); + + if (uriparser_uri->userInfo.first != NULL && uriparser_uri->userInfo.afterLast != NULL) { + const char *c = memchr(uriparser_uri->userInfo.first, ':', get_text_range_length(&uriparser_uri->userInfo)); + + if (c != NULL && uriparser_uri->userInfo.afterLast - c - 1 > 0) { + ZVAL_STRINGL(retval, c + 1, uriparser_uri->userInfo.afterLast - c - 1); + } else { + ZVAL_NULL(retval); + } + } else { + ZVAL_NULL(retval); + } + + return SUCCESS; +} + +ZEND_ATTRIBUTE_NONNULL static zend_result uriparser_read_host(const uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval) +{ + UriUriA *uriparser_uri = uriparser_read_uri(internal_uri->uri, read_mode); + ZEND_ASSERT(uriparser_uri != NULL); + + if (uriparser_uri->hostText.first != NULL && uriparser_uri->hostText.afterLast != NULL && get_text_range_length(&uriparser_uri->hostText) > 0) { + if (uriparser_uri->hostData.ip6 != NULL || uriparser_uri->hostData.ipFuture.first != NULL) { + /* the textual representation of the host is always accessible in the .hostText field no matter what the host is */ + smart_str host_str = {0}; + + smart_str_appendc(&host_str, '['); + smart_str_appendl(&host_str, uriparser_uri->hostText.first, get_text_range_length(&uriparser_uri->hostText)); + smart_str_appendc(&host_str, ']'); + + ZVAL_NEW_STR(retval, smart_str_extract(&host_str)); + } else { + ZVAL_STRINGL(retval, uriparser_uri->hostText.first, get_text_range_length(&uriparser_uri->hostText)); + } + } else { + ZVAL_NULL(retval); + } + + return SUCCESS; +} + +ZEND_ATTRIBUTE_NONNULL static size_t str_to_int(const char *str, size_t len) +{ + size_t result = 0; + + for (size_t i = 0; i < len; ++i) { + result = result * 10 + (str[i] - '0'); + } + + return result; +} + +ZEND_ATTRIBUTE_NONNULL static zend_result uriparser_read_port(const uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval) +{ + UriUriA *uriparser_uri = uriparser_read_uri(internal_uri->uri, read_mode); + ZEND_ASSERT(uriparser_uri != NULL); + + if (uriparser_uri->portText.first != NULL && uriparser_uri->portText.afterLast != NULL) { + ZVAL_LONG(retval, str_to_int(uriparser_uri->portText.first, get_text_range_length(&uriparser_uri->portText))); + } else { + ZVAL_NULL(retval); + } + + return SUCCESS; +} + +ZEND_ATTRIBUTE_NONNULL static zend_result uriparser_read_path(const uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval) +{ + UriUriA *uriparser_uri = uriparser_read_uri(internal_uri->uri, read_mode); + ZEND_ASSERT(uriparser_uri != NULL); + + if (uriparser_uri->pathHead != NULL) { + smart_str str = {0}; + + if (uriparser_uri->absolutePath || uriHasHostA(uriparser_uri)) { + smart_str_appendc(&str, '/'); + } + + for (const UriPathSegmentA *p = uriparser_uri->pathHead; p; p = p->next) { + smart_str_appendl(&str, p->text.first, get_text_range_length(&p->text)); + if (p->next) { + smart_str_appendc(&str, '/'); + } + } + + ZVAL_NEW_STR(retval, smart_str_extract(&str)); + } else if (uriparser_uri->absolutePath) { + ZVAL_CHAR(retval, '/'); + } else { + ZVAL_EMPTY_STRING(retval); + } + + return SUCCESS; +} + +ZEND_ATTRIBUTE_NONNULL static zend_result uriparser_read_query(const uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval) +{ + UriUriA *uriparser_uri = uriparser_read_uri(internal_uri->uri, read_mode); + ZEND_ASSERT(uriparser_uri != NULL); + + if (uriparser_uri->query.first != NULL && uriparser_uri->query.afterLast != NULL) { + ZVAL_STRINGL(retval, uriparser_uri->query.first, get_text_range_length(&uriparser_uri->query)); + } else { + ZVAL_NULL(retval); + } + + return SUCCESS; +} + +ZEND_ATTRIBUTE_NONNULL static zend_result uriparser_read_fragment(const uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval) +{ + UriUriA *uriparser_uri = uriparser_read_uri(internal_uri->uri, read_mode); + ZEND_ASSERT(uriparser_uri != NULL); + + if (uriparser_uri->fragment.first != NULL && uriparser_uri->fragment.afterLast != NULL) { + ZVAL_STRINGL(retval, uriparser_uri->fragment.first, get_text_range_length(&uriparser_uri->fragment)); + } else { + ZVAL_NULL(retval); + } + + return SUCCESS; +} + +static void *uriparser_malloc(UriMemoryManager *memory_manager, size_t size) +{ + return emalloc(size); +} + +static void *uriparser_calloc(UriMemoryManager *memory_manager, size_t nmemb, size_t size) +{ + return ecalloc(nmemb, size); +} + +static void *uriparser_realloc(UriMemoryManager *memory_manager, void *ptr, size_t size) +{ + return erealloc(ptr, size); +} + +static void *uriparser_reallocarray(UriMemoryManager *memory_manager, void *ptr, size_t nmemb, size_t size) +{ + return safe_erealloc(ptr, nmemb, size, 0); +} + +static void uriparser_free(UriMemoryManager *memory_manager, void *ptr) +{ + efree(ptr); +} + +PHP_MINIT_FUNCTION(uri_uriparser) +{ + if (uri_handler_register(&uriparser_uri_handler) == FAILURE) { + return FAILURE; + } + + defaultMemoryManager.malloc = uriparser_malloc; + defaultMemoryManager.calloc = uriparser_calloc; + defaultMemoryManager.realloc = uriparser_realloc; + defaultMemoryManager.reallocarray = uriparser_reallocarray; + defaultMemoryManager.free = uriparser_free; + + return SUCCESS; +} + +static uriparser_uris_t *uriparser_create_uris(void) +{ + uriparser_uris_t *uriparser_uris = ecalloc(1, sizeof(*uriparser_uris)); + uriparser_uris->normalized_uri_initialized = false; + + return uriparser_uris; +} + +static void throw_invalid_uri_exception(void) +{ + zend_throw_exception(uri_invalid_uri_exception_ce, "The specified URI is malformed", 0); +} + +#define PARSE_URI(dest_uri, uri_str, uriparser_uris, silent) \ + do { \ + if (ZSTR_LEN(uri_str) == 0 || \ + uriParseSingleUriExA(dest_uri, ZSTR_VAL(uri_str), ZSTR_VAL(uri_str) + ZSTR_LEN(uri_str), NULL) != URI_SUCCESS \ + ) { \ + efree(uriparser_uris); \ + if (!silent) { \ + throw_invalid_uri_exception(); \ + } \ + return NULL; \ + } \ + } while (0) + +void *uriparser_parse_uri_ex(const zend_string *uri_str, const uriparser_uris_t *uriparser_base_urls, bool silent) +{ + uriparser_uris_t *uriparser_uris = uriparser_create_uris(); + + if (uriparser_base_urls == NULL) { + PARSE_URI(&uriparser_uris->uri, uri_str, uriparser_uris, silent); + uriMakeOwnerA(&uriparser_uris->uri); + } else { + UriUriA uri; + + PARSE_URI(&uri, uri_str, uriparser_uris, silent); + + if (uriAddBaseUriA(&uriparser_uris->uri, &uri, &uriparser_base_urls->uri) != URI_SUCCESS) { + efree(uriparser_uris); + uriFreeUriMembersA(&uri); + if (!silent) { + throw_invalid_uri_exception(); + } + + return NULL; + } + + uriMakeOwnerA(&uriparser_uris->uri); + uriFreeUriMembersA(&uri); + } + + return uriparser_uris; +} + +void *uriparser_parse_uri(const zend_string *uri_str, const void *base_url, zval *errors, bool silent) +{ + return uriparser_parse_uri_ex(uri_str, base_url, silent); +} + +/* TODO make the clone handler accept a flag to distinguish between clone() calls and withers. + * When calling a wither successfully, the normalized URI is surely invalidated, therefore + * it doesn't make sense to copy it. In case of failure, an exception is thrown, and the URI object + * is discarded altogether. */ +ZEND_ATTRIBUTE_NONNULL static void *uriparser_clone_uri(void *uri) +{ + uriparser_uris_t *uriparser_uris = uri; + + uriparser_uris_t *new_uriparser_uris = uriparser_create_uris(); + uriparser_copy_uri(&new_uriparser_uris->uri, &uriparser_uris->uri); + if (uriparser_uris->normalized_uri_initialized) { + uriparser_copy_uri(&new_uriparser_uris->normalized_uri, &uriparser_uris->normalized_uri); + new_uriparser_uris->normalized_uri_initialized = true; + } + + return new_uriparser_uris; +} + +ZEND_ATTRIBUTE_NONNULL static zend_string *uriparser_uri_to_string(void *uri, uri_recomposition_mode_t recomposition_mode, bool exclude_fragment) +{ + uriparser_uris_t *uriparser_uris = uri; + UriUriA *uriparser_uri; + + if (recomposition_mode == URI_RECOMPOSITION_RAW_ASCII || recomposition_mode == URI_RECOMPOSITION_RAW_UNICODE) { + uriparser_uri = &uriparser_uris->uri; + } else { + uriparser_uri = get_normalized_uri(uriparser_uris); + } + + int charsRequired = 0; + int result = uriToStringCharsRequiredA(uriparser_uri, &charsRequired); + ZEND_ASSERT(result == URI_SUCCESS); + + charsRequired++; + + zend_string *uri_string = zend_string_alloc(charsRequired - 1, false); + result = uriToStringA(ZSTR_VAL(uri_string), uriparser_uri, charsRequired, NULL); + ZEND_ASSERT(result == URI_SUCCESS); + + if (exclude_fragment) { + const char *pos = zend_memrchr(ZSTR_VAL(uri_string), '#', ZSTR_LEN(uri_string)); + if (pos != NULL) { + uri_string = zend_string_truncate(uri_string, (pos - ZSTR_VAL(uri_string)), false); + } + } + + return uri_string; +} + +ZEND_ATTRIBUTE_NONNULL static void uriparser_free_uri(void *uri) +{ + uriparser_uris_t *uriparser_uris = uri; + + uriFreeUriMembersA(&uriparser_uris->uri); + uriFreeUriMembersA(&uriparser_uris->normalized_uri); + + efree(uriparser_uris); +} + +const uri_handler_t uriparser_uri_handler = { + .name = URI_PARSER_RFC3986, + .parse_uri = uriparser_parse_uri, + .clone_uri = uriparser_clone_uri, + .uri_to_string = uriparser_uri_to_string, + .free_uri = uriparser_free_uri, + { + .scheme = {.read_func = uriparser_read_scheme, .write_func = NULL}, + .username = {.read_func = uriparser_read_username, .write_func = NULL}, + .password = {.read_func = uriparser_read_password, .write_func = NULL}, + .host = {.read_func = uriparser_read_host, .write_func = NULL}, + .port = {.read_func = uriparser_read_port, .write_func = NULL}, + .path = {.read_func = uriparser_read_path, .write_func = NULL}, + .query = {.read_func = uriparser_read_query, .write_func = NULL}, + .fragment = {.read_func = uriparser_read_fragment, .write_func = NULL}, + } +}; diff --git a/ext/uri/php_uriparser.h b/ext/uri/php_uriparser.h new file mode 100644 index 0000000000000..5f1cc04570697 --- /dev/null +++ b/ext/uri/php_uriparser.h @@ -0,0 +1,38 @@ +/* + +----------------------------------------------------------------------+ + | Copyright (c) The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | https://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Máté Kocsis | + +----------------------------------------------------------------------+ +*/ + +#ifndef PHP_URIPARSER_H +#define PHP_URIPARSER_H + +#include +#include "uriparser/src/UriMemory.h" +#include "php_uri_common.h" + +extern const uri_handler_t uriparser_uri_handler; + +typedef struct uriparser_uris_t { + UriUriA uri; + UriUriA normalized_uri; + bool normalized_uri_initialized; +} uriparser_uris_t; + +PHP_MINIT_FUNCTION(uri_uriparser); + +zend_result uriparser_read_userinfo(const uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval); + +void *uriparser_parse_uri_ex(const zend_string *uri_str, const uriparser_uris_t *uriparser_base_url, bool silent); + +#endif diff --git a/ext/uri/tests/003.phpt b/ext/uri/tests/003.phpt index bcd6e417441c2..be607fd6cacef 100644 --- a/ext/uri/tests/003.phpt +++ b/ext/uri/tests/003.phpt @@ -5,12 +5,15 @@ uri --FILE-- --EXPECTF-- +NULL object(Uri\WhatWg\Url)#%d (%d) { ["scheme"]=> string(4) "http" @@ -29,4 +32,22 @@ object(Uri\WhatWg\Url)#%d (%d) { ["fragment"]=> string(6) "anchor" } +object(Uri\Rfc3986\Uri)#%d (%d) { + ["scheme"]=> + NULL + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + NULL + ["port"]=> + NULL + ["path"]=> + string(7) "/page:1" + ["query"]=> + NULL + ["fragment"]=> + NULL +} NULL diff --git a/ext/uri/tests/004.phpt b/ext/uri/tests/004.phpt index abbad59fee2e8..10e90dc584a8d 100644 --- a/ext/uri/tests/004.phpt +++ b/ext/uri/tests/004.phpt @@ -5,6 +5,14 @@ uri --FILE-- getMessage() . "\n"; +} + +var_dump(Uri\Rfc3986\Uri::parse("")); + try { new Uri\WhatWg\Url(""); } catch (Uri\WhatWg\InvalidUrlException $e) { @@ -13,13 +21,36 @@ try { var_dump(Uri\WhatWg\Url::parse("")); +var_dump(Uri\Rfc3986\Uri::parse("192.168/contact.html")); var_dump(Uri\WhatWg\Url::parse("192.168/contact.html", null)); +var_dump(Uri\Rfc3986\Uri::parse("http://RuPaul's Drag Race All Stars 7 Winners Cast on This Season's")); var_dump(Uri\WhatWg\Url::parse("http://RuPaul's Drag Race All Stars 7 Winners Cast on This Season's", null)); ?> ---EXPECT-- +--EXPECTF-- +The specified URI is malformed +NULL The specified URI is malformed (MissingSchemeNonRelativeUrl) NULL +object(Uri\Rfc3986\Uri)#%d (%d) { + ["scheme"]=> + NULL + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + NULL + ["port"]=> + NULL + ["path"]=> + string(20) "192.168/contact.html" + ["query"]=> + NULL + ["fragment"]=> + NULL +} +NULL NULL NULL diff --git a/ext/uri/tests/005.phpt b/ext/uri/tests/005.phpt index 262d43a75406b..6db6c4a56ee5a 100644 --- a/ext/uri/tests/005.phpt +++ b/ext/uri/tests/005.phpt @@ -5,6 +5,8 @@ uri --FILE-- getAsciiHost()); @@ -14,6 +16,7 @@ var_dump($url->toUnicodeString()); ?> --EXPECTF-- +NULL object(Uri\WhatWg\Url)#%d (%d) { ["scheme"]=> string(4) "http" diff --git a/ext/uri/tests/006.phpt b/ext/uri/tests/006.phpt index 0aba3e9e46b5e..c4da9db905c82 100644 --- a/ext/uri/tests/006.phpt +++ b/ext/uri/tests/006.phpt @@ -5,11 +5,32 @@ uri --FILE-- --EXPECTF-- +object(Uri\Rfc3986\Uri)#%d (%d) { + ["scheme"]=> + string(5) "https" + ["username"]=> + string(8) "username" + ["password"]=> + string(8) "password" + ["host"]=> + string(11) "example.com" + ["port"]=> + int(8080) + ["path"]=> + string(5) "/path" + ["query"]=> + string(3) "q=r" + ["fragment"]=> + string(8) "fragment" +} object(Uri\WhatWg\Url)#%d (%d) { ["scheme"]=> string(5) "https" diff --git a/ext/uri/tests/007.phpt b/ext/uri/tests/007.phpt index cb445fcf71a43..54151f2dfcb1c 100644 --- a/ext/uri/tests/007.phpt +++ b/ext/uri/tests/007.phpt @@ -5,6 +5,12 @@ uri --FILE-- getMessage() . "\n"; +} + try { new Uri\WhatWg\Url("https://example.com:8080@username:password/path?q=r#fragment"); } catch (Uri\WhatWg\InvalidUrlException $e) { @@ -19,6 +25,7 @@ var_dump($failures); ?> --EXPECTF-- +The specified URI is malformed The specified URI is malformed (PortInvalid) array(%d) { [0]=> diff --git a/ext/uri/tests/008.phpt b/ext/uri/tests/008.phpt index f4fddcd8eb777..e13130bb4c466 100644 --- a/ext/uri/tests/008.phpt +++ b/ext/uri/tests/008.phpt @@ -5,6 +5,27 @@ uri --FILE-- getScheme()); + var_dump($uri->getRawScheme()); + var_dump($uri->getUsername()); + var_dump($uri->getRawUsername()); + var_dump($uri->getPassword()); + var_dump($uri->getRawPassword()); + var_dump($uri->getUserInfo()); + var_dump($uri->getRawUserInfo()); + var_dump($uri->getHost()); + var_dump($uri->getRawHost()); + var_dump($uri->getPort()); + var_dump($uri->getPath()); + var_dump($uri->getRawPath()); + var_dump($uri->getQuery()); + var_dump($uri->getRawQuery()); + var_dump($uri->getFragment()); + var_dump($uri->getRawFragment()); +} + function callWhatWgGetters($url) { var_dump($url->getScheme()); @@ -18,11 +39,34 @@ function callWhatWgGetters($url) var_dump($url->getFragment()); } +$uri = Uri\Rfc3986\Uri::parse("https://username:password@www.google.com:8080/pathname1/pathname2/pathname3?query=true#hash-exists"); +callRfc3986Getters($uri); + +echo "\n"; + $url = Uri\WhatWg\Url::parse("https://username:password@www.google.com:8080/pathname1/pathname2/pathname3?query=true#hash-exists"); callWhatWgGetters($url); ?> --EXPECT-- +string(5) "https" +string(5) "https" +string(8) "username" +string(8) "username" +string(8) "password" +string(8) "password" +string(17) "username:password" +string(17) "username:password" +string(14) "www.google.com" +string(14) "www.google.com" +int(8080) +string(30) "/pathname1/pathname2/pathname3" +string(30) "/pathname1/pathname2/pathname3" +string(10) "query=true" +string(10) "query=true" +string(11) "hash-exists" +string(11) "hash-exists" + string(5) "https" string(8) "username" string(8) "password" diff --git a/ext/uri/tests/009.phpt b/ext/uri/tests/009.phpt index 1b279588c0167..05f2820bb3fcf 100644 --- a/ext/uri/tests/009.phpt +++ b/ext/uri/tests/009.phpt @@ -5,10 +5,29 @@ uri --FILE-- --EXPECTF-- +object(Uri\Rfc3986\Uri)#%d (%d) { + ["scheme"]=> + string(16) "chrome-extension" + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + string(11) "example.com" + ["port"]=> + NULL + ["path"]=> + string(0) "" + ["query"]=> + NULL + ["fragment"]=> + NULL +} object(Uri\WhatWg\Url)#%d (%d) { ["scheme"]=> string(16) "chrome-extension" diff --git a/ext/uri/tests/010.phpt b/ext/uri/tests/010.phpt index 4ec13f652f60c..2ca071eb2ca33 100644 --- a/ext/uri/tests/010.phpt +++ b/ext/uri/tests/010.phpt @@ -5,11 +5,49 @@ uri --FILE-- --EXPECTF-- +object(Uri\Rfc3986\Uri)#%d (%d) { + ["scheme"]=> + string(4) "http" + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + string(11) "example.com" + ["port"]=> + NULL + ["path"]=> + string(14) "/path/to/file2" + ["query"]=> + NULL + ["fragment"]=> + NULL +} +object(Uri\Rfc3986\Uri)#%d (%d) { + ["scheme"]=> + string(5) "https" + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + string(8) "test.com" + ["port"]=> + NULL + ["path"]=> + string(14) "/path/to/file2" + ["query"]=> + NULL + ["fragment"]=> + NULL +} object(Uri\WhatWg\Url)#%d (%d) { ["scheme"]=> string(4) "http" diff --git a/ext/uri/tests/011.phpt b/ext/uri/tests/011.phpt index 283886fb34fbb..49b915fa22a31 100644 --- a/ext/uri/tests/011.phpt +++ b/ext/uri/tests/011.phpt @@ -5,6 +5,12 @@ uri --FILE-- toRawString()); +var_dump(Uri\Rfc3986\Uri::parse("https://www.example.com/dir1/../dir2")->toRawString()); +var_dump(Uri\Rfc3986\Uri::parse("https://你好你好")); +var_dump(Uri\Rfc3986\Uri::parse("https://ï¼ï¼¸ï½ƒï¼ï¼Žï¼ï¼’5ï¼ï¼Žï¼ï¼‘")); +var_dump(Uri\Rfc3986\Uri::parse("HttPs://0300.0250.0000.0001/path?query=foo%20bar")->toRawString()); + var_dump(Uri\WhatWg\Url::parse("http://////www.EXAMPLE.com:80")->toAsciiString()); var_dump(Uri\WhatWg\Url::parse("https://www.example.com:443/dir1/../dir2")->toAsciiString()); var_dump(Uri\WhatWg\Url::parse("https://你好你好")->toAsciiString()); @@ -14,6 +20,11 @@ var_dump(Uri\WhatWg\Url::parse("HttPs://0300.0250.0000.0001/path?query=foo%20bar ?> --EXPECT-- +string(29) "http://////www.EXAMPLE.com:80" +string(36) "https://www.example.com/dir1/../dir2" +NULL +NULL +string(48) "HttPs://0300.0250.0000.0001/path?query=foo%20bar" string(23) "http://www.example.com/" string(28) "https://www.example.com/dir2" string(23) "https://xn--6qqa088eba/" diff --git a/ext/uri/tests/012.phpt b/ext/uri/tests/012.phpt index 0784a74e625f0..7c14014fb3519 100644 --- a/ext/uri/tests/012.phpt +++ b/ext/uri/tests/012.phpt @@ -5,12 +5,32 @@ uri --FILE-- --EXPECTF-- +object(Uri\Rfc3986\Uri)#%d (%d) { + ["scheme"]=> + string(6) "mailto" + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + NULL + ["port"]=> + NULL + ["path"]=> + string(15) "Joe@Example.COM" + ["query"]=> + NULL + ["fragment"]=> + NULL +} object(Uri\WhatWg\Url)#%d (%d) { ["scheme"]=> string(6) "mailto" @@ -29,6 +49,24 @@ object(Uri\WhatWg\Url)#%d (%d) { ["fragment"]=> NULL } +object(Uri\Rfc3986\Uri)#%d (%d) { + ["scheme"]=> + string(4) "file" + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + NULL + ["port"]=> + NULL + ["path"]=> + string(30) "/E:/Documents%20and%20Settings" + ["query"]=> + NULL + ["fragment"]=> + NULL +} object(Uri\WhatWg\Url)#%d (%d) { ["scheme"]=> string(4) "file" diff --git a/ext/uri/tests/013.phpt b/ext/uri/tests/013.phpt index 016fe6632782c..6a5bb31870fb1 100644 --- a/ext/uri/tests/013.phpt +++ b/ext/uri/tests/013.phpt @@ -5,14 +5,39 @@ uri --FILE-- + @")); + var_dump(Uri\WhatWg\Url::parse("http://example.com?foobar=%27%3Cscript%3E+%2B+%40")); var_dump(Uri\WhatWg\Url::parse("http://example.com?foobar='